From 61cdb9a57d3b3d1ea0ddcaad87422905f1a2db7f Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Sun, 30 Jun 2024 19:21:36 +0800 Subject: [PATCH 01/48] init --- arthas-grpc-server/pom.xml | 84 +++++++++++++++++++ .../src/main/java/com/taobao/arthas/Main.java | 27 ++++++ .../arthas/service/impl/HelloServiceImpl.java | 23 +++++ arthas-grpc-server/src/main/proto/Test.proto | 15 ++++ pom.xml | 1 + 5 files changed, 150 insertions(+) create mode 100644 arthas-grpc-server/pom.xml create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/service/impl/HelloServiceImpl.java create mode 100644 arthas-grpc-server/src/main/proto/Test.proto diff --git a/arthas-grpc-server/pom.xml b/arthas-grpc-server/pom.xml new file mode 100644 index 0000000000..84f7039153 --- /dev/null +++ b/arthas-grpc-server/pom.xml @@ -0,0 +1,84 @@ + + + + + arthas-all + com.taobao.arthas + ${revision} + ../pom.xml + + 4.0.0 + arthas-grpc-server + arthas-grpc-server + https://github.com/alibaba/arthas + + + 8 + 8 + UTF-8 + 1.46.0 + + + + + + io.grpc + grpc-bom + ${grpc.version} + pom + import + + + + + + + io.netty + netty-codec-http + + + io.grpc + grpc-netty + + + io.grpc + grpc-services + + + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + + ${basedir}/src/main/proto + com.google.protobuf:protoc:3.11.0:exe:${os.detected.classifier} + grpc-java + io.grpc:protoc-gen-grpc-java:1.28.0:exe:${os.detected.classifier} + + + + + compile + compile-custom + + + + + + + + kr.motd.maven + os-maven-plugin + 1.4.1.Final + + + + + + \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java new file mode 100644 index 0000000000..c13a983689 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java @@ -0,0 +1,27 @@ +package com.taobao.arthas; + +import com.taobao.arthas.service.impl.HelloServiceImpl; +import io.grpc.Server; +import io.grpc.ServerBuilder; + +import java.io.IOException; + +/** + * @author: 風楪 + * @date: 2024/6/30 上午1:22 + */ +public class Main { + public static void main(String[] args) { + int port = 9090; + Server server = ServerBuilder.forPort(port) + .addService(new HelloServiceImpl()) + .build(); + try { + server.start(); + System.out.println("Server started, listening on " + port); + server.awaitTermination(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/impl/HelloServiceImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/impl/HelloServiceImpl.java new file mode 100644 index 0000000000..3581bae3ae --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/impl/HelloServiceImpl.java @@ -0,0 +1,23 @@ +package com.taobao.arthas.service.impl;/** + * @author: 風楪 + * @date: 2024/6/30 下午6:42 + */ + +import helloworld.HelloServiceGrpc; +import helloworld.Test; +import io.grpc.stub.StreamObserver; + +/** + * @author: FengYe + * @date: 2024/6/30 下午6:42 + * @description: HelloServiceImpl + */ +public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase { + @Override + public void sayHello(Test.HelloRequest request, StreamObserver responseObserver) { + String name = request.getName(); + System.out.println(name); + responseObserver.onNext(Test.HelloReply.newBuilder().setMessage(name).build()); + responseObserver.onCompleted(); + } +} diff --git a/arthas-grpc-server/src/main/proto/Test.proto b/arthas-grpc-server/src/main/proto/Test.proto new file mode 100644 index 0000000000..8c24d6207b --- /dev/null +++ b/arthas-grpc-server/src/main/proto/Test.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package helloworld; + +service HelloService { + rpc SayHello(HelloRequest) returns (HelloReply); +} + +message HelloRequest { + string name = 1; +} + +message HelloReply { + string message = 1; +} diff --git a/pom.xml b/pom.xml index 1aeb8b4c2c..0f882f67d2 100644 --- a/pom.xml +++ b/pom.xml @@ -76,6 +76,7 @@ site packaging arthas-grpc-web-proxy + arthas-grpc-server From c1a9ea87b2e44e35c177b0c960be82262aff4f0c Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Mon, 1 Jul 2024 00:39:36 +0800 Subject: [PATCH 02/48] init --- .../taobao/arthas/service/ArthasSampleService.java | 13 +++++++++++++ .../arthas/service/ArthasSampleServiceImpl.java | 12 ++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/service/ArthasSampleService.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/service/ArthasSampleServiceImpl.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/ArthasSampleService.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/ArthasSampleService.java new file mode 100644 index 0000000000..744258fc17 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/ArthasSampleService.java @@ -0,0 +1,13 @@ +package com.taobao.arthas.service;/** + * @author: 風楪 + * @date: 2024/6/30 下午11:42 + */ + +/** + * @author: FengYe + * @date: 2024/6/30 下午11:42 + * @description: ArthasSampleService + */ +public interface ArthasSampleService { + String trace(String command); +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/ArthasSampleServiceImpl.java new file mode 100644 index 0000000000..da2f5c3737 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/ArthasSampleServiceImpl.java @@ -0,0 +1,12 @@ +package com.taobao.arthas.service;/** + * @author: 風楪 + * @date: 2024/6/30 下午11:43 + */ + +/** + * @author: FengYe + * @date: 2024/6/30 下午11:43 + * @description: ArthasSampleServiceImpl + */ +public class ArthasSampleServiceImpl { +} From 610d12128991d2cf10f3dcb84a166b3e309d9b51 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Thu, 4 Jul 2024 02:16:18 +0800 Subject: [PATCH 03/48] init --- arthas-grpc-server/pom.xml | 23 +++++++++++++++++++ .../com/taobao/arthas/ArthasGrpcServer.java | 15 ++++++++++++ .../service/ArthasSampleServiceImpl.java | 12 ---------- .../service/impl/ArthasSampleServiceImpl.java | 21 +++++++++++++++++ 4 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/service/ArthasSampleServiceImpl.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/service/impl/ArthasSampleServiceImpl.java diff --git a/arthas-grpc-server/pom.xml b/arthas-grpc-server/pom.xml index 84f7039153..a77107969b 100644 --- a/arthas-grpc-server/pom.xml +++ b/arthas-grpc-server/pom.xml @@ -34,6 +34,7 @@ + io.netty netty-codec-http @@ -46,6 +47,28 @@ io.grpc grpc-services + + + + + org.slf4j + slf4j-api + 2.0.12 + + + + + ch.qos.logback + logback-classic + 1.5.0 + + + + org.projectlombok + lombok + 1.18.30 + provided + diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java new file mode 100644 index 0000000000..4353c9d763 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java @@ -0,0 +1,15 @@ +package com.taobao.arthas;/** + * @author: 風楪 + * @date: 2024/7/3 上午12:30 + */ + +/** + * @author: FengYe + * @date: 2024/7/3 上午12:30 + * @description: ArthasGrpcServer + */ +public class ArthasGrpcServer { + public static void main(String[] args) { + + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/ArthasSampleServiceImpl.java deleted file mode 100644 index da2f5c3737..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/ArthasSampleServiceImpl.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.taobao.arthas.service;/** - * @author: 風楪 - * @date: 2024/6/30 下午11:43 - */ - -/** - * @author: FengYe - * @date: 2024/6/30 下午11:43 - * @description: ArthasSampleServiceImpl - */ -public class ArthasSampleServiceImpl { -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/impl/ArthasSampleServiceImpl.java new file mode 100644 index 0000000000..194d8364fe --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/impl/ArthasSampleServiceImpl.java @@ -0,0 +1,21 @@ +package com.taobao.arthas.service.impl;/** + * @author: 風楪 + * @date: 2024/6/30 下午11:43 + */ + +import com.taobao.arthas.service.ArthasSampleService; +import lombok.extern.slf4j.Slf4j; + +/** + * @author: FengYe + * @date: 2024/6/30 下午11:43 + * @description: ArthasSampleServiceImpl + */ +@Slf4j +public class ArthasSampleServiceImpl implements ArthasSampleService { + @Override + public String trace(String command) { + log.info("receive command: {}", command); + return "receive command: " + command; + } +} \ No newline at end of file From 871b0d7a6c76c47838c063cc009bf3d58d958a59 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Mon, 8 Jul 2024 01:59:44 +0800 Subject: [PATCH 04/48] init --- .../com/taobao/arthas/ArthasGrpcServer.java | 42 ++++++++++++++++- .../src/main/java/com/taobao/arthas/Main.java | 3 ++ .../com/taobao/arthas/h2/Http2Handler.java | 45 +++++++++++++++++++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java index 4353c9d763..3a6c007892 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java @@ -3,13 +3,53 @@ * @date: 2024/7/3 上午12:30 */ +import com.taobao.arthas.h2.Http2Handler; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http2.Http2FrameCodecBuilder; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.util.SelfSignedCertificate; + /** * @author: FengYe * @date: 2024/7/3 上午12:30 * @description: ArthasGrpcServer */ public class ArthasGrpcServer { - public static void main(String[] args) { + public static void main(String[] args) throws Exception { + //自签名生成密钥 + SelfSignedCertificate ssc = new SelfSignedCertificate(); + SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) + .build(); + + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + + try { + ServerBootstrap b = new ServerBootstrap(); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .option(ChannelOption.SO_BACKLOG, 1024) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) { + ch.pipeline().addLast(sslCtx.newHandler(ch.alloc())); + ch.pipeline().addLast(Http2FrameCodecBuilder.forServer().build()); + ch.pipeline().addLast(new Http2Handler()); + } + }); + // Bind and start to accept incoming connections. + b.bind(8443).sync().channel().closeFuture().sync(); + } finally { + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java index c13a983689..59bd332c5c 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java @@ -3,6 +3,8 @@ import com.taobao.arthas.service.impl.HelloServiceImpl; import io.grpc.Server; import io.grpc.ServerBuilder; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import java.io.IOException; @@ -16,6 +18,7 @@ public static void main(String[] args) { Server server = ServerBuilder.forPort(port) .addService(new HelloServiceImpl()) .build(); + ByteBuf buffer = Unpooled.buffer(); try { server.start(); System.out.println("Server started, listening on " + port); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java new file mode 100644 index 0000000000..84d8cba3a1 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java @@ -0,0 +1,45 @@ +package com.taobao.arthas.h2;/** + * @author: 風楪 + * @date: 2024/7/7 下午9:58 + */ + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http2.*; +import io.netty.util.CharsetUtil; + +/** + * @author: FengYe + * @date: 2024/7/7 下午9:58 + * @description: Http2Handler + */ +public class Http2Handler extends SimpleChannelInboundHandler { + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Http2Frame frame) { + if (frame instanceof Http2HeadersFrame) { + Http2HeadersFrame headersFrame = (Http2HeadersFrame) frame; + System.out.println("Received headers: " + headersFrame.headers()); + + // Respond to the client with headers + Http2Headers headers = new DefaultHttp2Headers().status("200"); + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(headers).stream(((Http2HeadersFrame) frame).stream())); + + } else if (frame instanceof Http2DataFrame) { + Http2DataFrame dataFrame = (Http2DataFrame) frame; + System.out.println("Received data: " + dataFrame.content().toString(CharsetUtil.UTF_8)); + dataFrame.release(); + + // Respond to the client with data + Http2DataFrame responseData = new DefaultHttp2DataFrame(ctx.alloc().buffer().writeBytes("Hello, HTTP/2".getBytes(CharsetUtil.UTF_8)), true) + .stream(((Http2HeadersFrame) frame).stream()); + ctx.writeAndFlush(responseData); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.printStackTrace(); + ctx.close(); + } +} \ No newline at end of file From f5732a64ddf1e8a0ddfa7c596ec8b54879a509be Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Wed, 10 Jul 2024 01:36:17 +0800 Subject: [PATCH 05/48] init --- .../com/taobao/arthas/ArthasGrpcServer.java | 27 +++++++++ .../com/taobao/arthas/h2/Http2Handler.java | 55 ++++++++++++++++++- 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java index 3a6c007892..45da938aa1 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java @@ -16,6 +16,11 @@ import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.SelfSignedCertificate; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; + /** * @author: FengYe * @date: 2024/7/3 上午12:30 @@ -28,6 +33,21 @@ public static void main(String[] args) throws Exception { SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .build(); + + // 指定生成自签名证书和密钥的位置 + File certFile = new File(System.getProperty("user.dir"),"certificate.crt"); + File keyFile = new File(System.getProperty("user.dir"),"privateKey.key"); + + // 将生成的证书和私钥移动到指定位置 + moveFile(ssc.certificate(), certFile); + moveFile(ssc.privateKey(), keyFile); + + System.out.println(certFile.getAbsolutePath()); + System.out.println(keyFile.getAbsolutePath()); + + System.out.println("Certificate: " + ssc.certificate()); + System.out.println("Private Key: " + ssc.privateKey()); + EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); @@ -52,4 +72,11 @@ public void initChannel(SocketChannel ch) { workerGroup.shutdownGracefully(); } } + + private static void moveFile(File source, File target) throws IOException { + if (!target.getParentFile().exists()) { + target.getParentFile().mkdirs(); + } + Files.move(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING); + } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java index 84d8cba3a1..bc64691b9b 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java @@ -3,11 +3,17 @@ * @date: 2024/7/7 下午9:58 */ +import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http2.*; import io.netty.util.CharsetUtil; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.zip.GZIPInputStream; + /** * @author: FengYe * @date: 2024/7/7 下午9:58 @@ -20,14 +26,40 @@ protected void channelRead0(ChannelHandlerContext ctx, Http2Frame frame) { if (frame instanceof Http2HeadersFrame) { Http2HeadersFrame headersFrame = (Http2HeadersFrame) frame; System.out.println("Received headers: " + headersFrame.headers()); + System.out.println(headersFrame.headers().path().toString()); - // Respond to the client with headers - Http2Headers headers = new DefaultHttp2Headers().status("200"); - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(headers).stream(((Http2HeadersFrame) frame).stream())); +// // Respond to the client with headers +// Http2Headers responseHeaders = new DefaultHttp2Headers() +// .status("200") +// .set("content-type", "text/plain; charset=UTF-8"); +// +// // 创建响应数据 +// byte[] content = "Hello, HTTP/2 World!".getBytes(); +// Http2DataFrame dataFrame = new DefaultHttp2DataFrame(ctx.alloc().buffer().writeBytes(content), true); +// +// // 发送响应头 +// ctx.write(new DefaultHttp2HeadersFrame(responseHeaders).stream(headersFrame.stream())); +// +// // 发送响应数据 +// ctx.writeAndFlush(dataFrame.stream(headersFrame.stream())); } else if (frame instanceof Http2DataFrame) { Http2DataFrame dataFrame = (Http2DataFrame) frame; System.out.println("Received data: " + dataFrame.content().toString(CharsetUtil.UTF_8)); + ByteBuf content = dataFrame.content(); + byte[] byteArray = new byte[content.readableBytes()]; + content.readBytes(byteArray); + + + try { + Object o = decompressAndDeserialize(byteArray); + System.out.println(o); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + dataFrame.release(); // Respond to the client with data @@ -42,4 +74,21 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } + + public static Object decompressAndDeserialize(byte[] compressedData) throws IOException, ClassNotFoundException { + // 解压缩数据 + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedData); + GZIPInputStream gzipInputStream = new GZIPInputStream(byteArrayInputStream); + ObjectInputStream objectInputStream = new ObjectInputStream(gzipInputStream); + + // 反序列化对象 + Object deserializedObject = objectInputStream.readObject(); + + // 关闭流 + objectInputStream.close(); + gzipInputStream.close(); + byteArrayInputStream.close(); + + return deserializedObject; + } } \ No newline at end of file From 96dcd06c480164c9ffe79a8eb0bfbe869b263970 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Mon, 15 Jul 2024 02:18:05 +0800 Subject: [PATCH 06/48] update: add arthasSample --- .../com/taobao/arthas/ArthasGrpcServer.java | 4 +- .../com/taobao/arthas/h2/Http2Handler.java | 129 +++++++++++------- .../service/req/ArthasSampleRequest.java | 64 +++++++++ arthas-grpc-server/src/main/proto/Test.proto | 3 + 4 files changed, 148 insertions(+), 52 deletions(-) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java index 45da938aa1..abc7d267b6 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java @@ -59,14 +59,14 @@ public static void main(String[] args) throws Exception { .childHandler(new ChannelInitializer() { @Override public void initChannel(SocketChannel ch) { - ch.pipeline().addLast(sslCtx.newHandler(ch.alloc())); +// ch.pipeline().addLast(sslCtx.newHandler(ch.alloc())); ch.pipeline().addLast(Http2FrameCodecBuilder.forServer().build()); ch.pipeline().addLast(new Http2Handler()); } }); // Bind and start to accept incoming connections. - b.bind(8443).sync().channel().closeFuture().sync(); + b.bind(9090).sync().channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java index bc64691b9b..05487a0770 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java @@ -3,15 +3,21 @@ * @date: 2024/7/7 下午9:58 */ +import com.google.protobuf.CodedInputStream; +import com.taobao.arthas.service.ArthasSampleService; +import com.taobao.arthas.service.req.ArthasSampleRequest; +import helloworld.Test; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http2.*; import io.netty.util.CharsetUtil; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.ObjectInputStream; +import java.io.*; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.zip.GZIPInputStream; /** @@ -21,51 +27,69 @@ */ public class Http2Handler extends SimpleChannelInboundHandler { + /** + * 暂存收到的所有请求的数据 + */ + private ConcurrentHashMap dataBuffer = new ConcurrentHashMap<>(); + @Override - protected void channelRead0(ChannelHandlerContext ctx, Http2Frame frame) { + protected void channelRead0(ChannelHandlerContext ctx, Http2Frame frame) throws IOException { if (frame instanceof Http2HeadersFrame) { + System.out.println("header"); Http2HeadersFrame headersFrame = (Http2HeadersFrame) frame; + int id = headersFrame.stream().id(); + dataBuffer.put(id, ctx.alloc().buffer()); + System.out.println("Received headers: " + headersFrame.headers()); System.out.println(headersFrame.headers().path().toString()); -// // Respond to the client with headers -// Http2Headers responseHeaders = new DefaultHttp2Headers() -// .status("200") -// .set("content-type", "text/plain; charset=UTF-8"); -// -// // 创建响应数据 -// byte[] content = "Hello, HTTP/2 World!".getBytes(); -// Http2DataFrame dataFrame = new DefaultHttp2DataFrame(ctx.alloc().buffer().writeBytes(content), true); -// -// // 发送响应头 -// ctx.write(new DefaultHttp2HeadersFrame(responseHeaders).stream(headersFrame.stream())); -// -// // 发送响应数据 -// ctx.writeAndFlush(dataFrame.stream(headersFrame.stream())); + // Respond to the client with headers + Http2Headers responseHeaders = new DefaultHttp2Headers() + .status("200") + .set("content-type", "text/plain; charset=UTF-8"); + + // 创建响应数据 + byte[] content = "Hello, HTTP/2 World!".getBytes(); + Http2DataFrame dataFrame = new DefaultHttp2DataFrame(ctx.alloc().buffer().writeBytes(content), true); + + // 发送响应头 + ctx.write(new DefaultHttp2HeadersFrame(responseHeaders).stream(headersFrame.stream())); + + // 发送响应数据 + ctx.writeAndFlush(dataFrame.stream(headersFrame.stream())); } else if (frame instanceof Http2DataFrame) { Http2DataFrame dataFrame = (Http2DataFrame) frame; - System.out.println("Received data: " + dataFrame.content().toString(CharsetUtil.UTF_8)); - ByteBuf content = dataFrame.content(); - byte[] byteArray = new byte[content.readableBytes()]; - content.readBytes(byteArray); - + byte[] data = new byte[dataFrame.content().readableBytes()]; + System.out.println(dataFrame.content().readableBytes()); + System.out.println(dataFrame.isEndStream()); + dataFrame.content().readBytes(data); - try { - Object o = decompressAndDeserialize(byteArray); - System.out.println(o); - } catch (IOException e) { - throw new RuntimeException(e); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); +// Decompress if needed + byte[] decompressedData = decompressGzip(data); + ByteBuf byteBuf = dataBuffer.get(dataFrame.stream().id()); + byteBuf.writeBytes(decompressedData); + if (dataFrame.isEndStream()) { + byteBuf.readBytes(5); + ArthasSampleRequest arthasSampleRequest = new ArthasSampleRequest(byteBuf.nioBuffer()); + Test.HelloRequest helloRequest = Test.HelloRequest.parseFrom(CodedInputStream.newInstance(byteBuf.nioBuffer())); + System.out.println(helloRequest.getName()); } - dataFrame.release(); - // Respond to the client with data - Http2DataFrame responseData = new DefaultHttp2DataFrame(ctx.alloc().buffer().writeBytes("Hello, HTTP/2".getBytes(CharsetUtil.UTF_8)), true) - .stream(((Http2HeadersFrame) frame).stream()); - ctx.writeAndFlush(responseData); +// +// byte[] responseData = "hello".getBytes(); +// +// // Send response +// Http2Headers responseHeaders = new DefaultHttp2Headers() +// .status("200") +// .set("content-type", "application/grpc"); +// ctx.write(new DefaultHttp2HeadersFrame(responseHeaders)); +// ctx.writeAndFlush(new DefaultHttp2DataFrame(ctx.alloc().buffer().writeBytes(responseData)).stream(dataFrame.stream())); +// System.out.println("finish"); +// if (((Http2DataFrame) frame).isEndStream()) { +// dataFrame.release(); +// } } } @@ -75,20 +99,25 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { ctx.close(); } - public static Object decompressAndDeserialize(byte[] compressedData) throws IOException, ClassNotFoundException { - // 解压缩数据 - ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedData); - GZIPInputStream gzipInputStream = new GZIPInputStream(byteArrayInputStream); - ObjectInputStream objectInputStream = new ObjectInputStream(gzipInputStream); - - // 反序列化对象 - Object deserializedObject = objectInputStream.readObject(); - - // 关闭流 - objectInputStream.close(); - gzipInputStream.close(); - byteArrayInputStream.close(); - - return deserializedObject; + private static byte[] decompressGzip(byte[] compressedData) throws IOException { + boolean isGzip = (compressedData.length > 2 && (compressedData[0] & 0xff) == 0x1f && (compressedData[1] & 0xff) == 0x8b); + if (isGzip) { + try { + InputStream byteStream = new ByteArrayInputStream(compressedData); + GZIPInputStream gzipStream = new GZIPInputStream(byteStream); + byte[] buffer = new byte[1024]; + int len; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + while ((len = gzipStream.read(buffer)) != -1) { + out.write(buffer, 0, len); + } + return out.toByteArray(); + } catch (IOException e) { + System.err.println("Failed to decompress GZIP data: " + e.getMessage()); + } + return null; + } else { + return compressedData; + } } } \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java new file mode 100644 index 0000000000..de7e35ea7e --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java @@ -0,0 +1,64 @@ +package com.taobao.arthas.service.req;/** + * @author: 風楪 + * @date: 2024/7/14 上午4:28 + */ + +import com.google.protobuf.*; +import com.taobao.arthas.service.ArthasSampleService; +import helloworld.Test; +import io.netty.buffer.ByteBuf; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.Map; + +/** + * @author: FengYe + * @date: 2024/7/14 上午4:28 + * @description: ArthasSampleRequest + */ +public class ArthasSampleRequest{ + + private String name; + + public ArthasSampleRequest(ByteBuffer byteBuffer){ + CodedInputStream codedInputStream = CodedInputStream.newInstance(byteBuffer); + try { + // 读取标签 + int tag; + while ((tag = codedInputStream.readTag()) != 0) { + int fieldNumber = WireFormat.getTagFieldNumber(tag); + int wireType = WireFormat.getTagWireType(tag); + + System.out.println("Field Number: " + fieldNumber); + System.out.println("Wire Type: " + wireType); + + // 根据字段编号和类型读取对应的数据 + switch (wireType) { + case WireFormat.WIRETYPE_VARINT: + long varintValue = codedInputStream.readInt64(); + System.out.println("Varint Value: " + varintValue); + break; + case WireFormat.WIRETYPE_FIXED32: + int fixed32Value = codedInputStream.readFixed32(); + System.out.println("Fixed32 Value: " + fixed32Value); + break; + case WireFormat.WIRETYPE_FIXED64: + long fixed64Value = codedInputStream.readFixed64(); + System.out.println("Fixed64 Value: " + fixed64Value); + break; + case WireFormat.WIRETYPE_LENGTH_DELIMITED: + int length = codedInputStream.readRawVarint32(); + byte[] bytes = codedInputStream.readRawBytes(length); + System.out.println("Length-delimited Value: " + new String(bytes)); + break; + default: + throw new IOException("Unsupported wire type: " + wireType); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/arthas-grpc-server/src/main/proto/Test.proto b/arthas-grpc-server/src/main/proto/Test.proto index 8c24d6207b..ec613de843 100644 --- a/arthas-grpc-server/src/main/proto/Test.proto +++ b/arthas-grpc-server/src/main/proto/Test.proto @@ -8,6 +8,9 @@ service HelloService { message HelloRequest { string name = 1; + double age = 2; + int64 price = 3; + float man = 4; } message HelloReply { From e3ec9e236c46c1f13578f7a8bc72410ff9b6ae2a Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Mon, 22 Jul 2024 02:46:35 +0800 Subject: [PATCH 07/48] update: add MiniTemplator --- arthas-grpc-server/pom.xml | 52 +- .../src/main/java/com/taobao/arthas/Main.java | 32 +- .../com/taobao/arthas/h2/Http2Handler.java | 13 +- .../taobao/arthas/protobuf/ProtobufClass.java | 12 + .../taobao/arthas/protobuf/ProtobufCodec.java | 15 + .../arthas/protobuf/ProtobufFieldType.java | 179 +++ .../arthas/protobuf/ProtobufProxyFactory.java | 40 + .../arthas/protobuf/utils/MiniTemplator.java | 1305 +++++++++++++++++ .../arthas/service/impl/HelloServiceImpl.java | 23 - .../service/req/ArthasSampleRequest.java | 126 +- .../src/main/resources/class_template.tpl | 102 ++ 11 files changed, 1799 insertions(+), 100 deletions(-) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufClass.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufCodec.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufFieldType.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxyFactory.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/MiniTemplator.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/service/impl/HelloServiceImpl.java create mode 100644 arthas-grpc-server/src/main/resources/class_template.tpl diff --git a/arthas-grpc-server/pom.xml b/arthas-grpc-server/pom.xml index a77107969b..1f98315dbc 100644 --- a/arthas-grpc-server/pom.xml +++ b/arthas-grpc-server/pom.xml @@ -34,20 +34,36 @@ - + + io.netty - netty-codec-http - - - io.grpc - grpc-netty + netty-codec-http2 + 4.1.111.Final + - io.grpc - grpc-services + com.google.protobuf + protobuf-java + 4.27.2 + + + + + + + + + + + + + + + + @@ -69,6 +85,19 @@ 1.18.30 provided + + + com.squareup.wire + wire-runtime + 4.2.0 + + + + com.baidu + jprotobuf + 2.4.20 + + @@ -81,14 +110,13 @@ ${basedir}/src/main/proto com.google.protobuf:protoc:3.11.0:exe:${os.detected.classifier} - grpc-java - io.grpc:protoc-gen-grpc-java:1.28.0:exe:${os.detected.classifier} compile - compile-custom + + @@ -102,6 +130,4 @@ - - \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java index 59bd332c5c..c00d2d95e8 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java @@ -1,30 +1,26 @@ package com.taobao.arthas; -import com.taobao.arthas.service.impl.HelloServiceImpl; -import io.grpc.Server; -import io.grpc.ServerBuilder; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; +import com.taobao.arthas.protobuf.utils.MiniTemplator; import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; /** * @author: 風楪 * @date: 2024/6/30 上午1:22 */ public class Main { - public static void main(String[] args) { - int port = 9090; - Server server = ServerBuilder.forPort(port) - .addService(new HelloServiceImpl()) - .build(); - ByteBuf buffer = Unpooled.buffer(); - try { - server.start(); - System.out.println("Server started, listening on " + port); - server.awaitTermination(); - } catch (Exception e) { - throw new RuntimeException(e); - } + + private static final String TEMPLATE_FILE = "/class_template.tpl"; + + public static void main(String[] args) throws IOException { + String path = Objects.requireNonNull(Main.class.getResource(TEMPLATE_FILE)).getPath(); + + MiniTemplator miniTemplator = new MiniTemplator(path); + + miniTemplator.setVariable("importPackage","test"); + miniTemplator.addBlock("imports"); + System.out.println(miniTemplator.generateOutput()); } } \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java index 05487a0770..74f579e80e 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java @@ -3,10 +3,11 @@ * @date: 2024/7/7 下午9:58 */ +import com.baidu.bjf.remoting.protobuf.Codec; +import com.baidu.bjf.remoting.protobuf.ProtobufProxy; import com.google.protobuf.CodedInputStream; import com.taobao.arthas.service.ArthasSampleService; import com.taobao.arthas.service.req.ArthasSampleRequest; -import helloworld.Test; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; import io.netty.channel.ChannelHandlerContext; @@ -71,9 +72,13 @@ protected void channelRead0(ChannelHandlerContext ctx, Http2Frame frame) throws byteBuf.writeBytes(decompressedData); if (dataFrame.isEndStream()) { byteBuf.readBytes(5); - ArthasSampleRequest arthasSampleRequest = new ArthasSampleRequest(byteBuf.nioBuffer()); - Test.HelloRequest helloRequest = Test.HelloRequest.parseFrom(CodedInputStream.newInstance(byteBuf.nioBuffer())); - System.out.println(helloRequest.getName()); + + byte[] byteArray = new byte[byteBuf.readableBytes()]; + byteBuf.readBytes(byteArray); + Codec codec = ProtobufProxy.create(ArthasSampleRequest.class); + ArthasSampleRequest decode = codec.decode(byteArray); + System.out.println(decode); + } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufClass.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufClass.java new file mode 100644 index 0000000000..1388162f71 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufClass.java @@ -0,0 +1,12 @@ +package com.taobao.arthas.protobuf;/** + * @author: 風楪 + * @date: 2024/7/17 下午10:00 + */ + +/** + * @author: FengYe + * @date: 2024/7/17 下午10:00 + * @description: ProtoBufClass + */ +public @interface ProtobufClass { +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufCodec.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufCodec.java new file mode 100644 index 0000000000..3f4ce336a2 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufCodec.java @@ -0,0 +1,15 @@ +package com.taobao.arthas.protobuf;/** + * @author: 風楪 + * @date: 2024/7/17 下午9:44 + */ + +/** + * @author: FengYe + * @date: 2024/7/17 下午9:44 + * @description: Codec + */ +public interface ProtobufCodec { + byte[] encode(T t); + + T decode(byte[] bytes); +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufFieldType.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufFieldType.java new file mode 100644 index 0000000000..a59ad2b814 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufFieldType.java @@ -0,0 +1,179 @@ +package com.taobao.arthas.protobuf;/** + * @author: 風楪 + * @date: 2024/7/17 下午10:02 + */ + +import com.google.protobuf.WireFormat; + +/** + * @author: FengYe protobuf字段类型映射 + * @date: 2024/7/17 下午10:02 + * @description: ProtoBufFieldType + */ +public enum ProtobufFieldType { + /** + * types defined in .proto file. + */ + DOUBLE ("Double", "double" , "WIRETYPE_FIXED64", ".doubleValue()", WireFormat.FieldType.DOUBLE ,"0d"), + FLOAT ("Float", "float", "WIRETYPE_FIXED32" , ".floatValue()", WireFormat.FieldType.FLOAT ,"0f"), + INT64 ("Long", "int64" , "WIRETYPE_VARINT" , ".longValue()", WireFormat.FieldType.INT64 ,"0L" ), + UINT64 ("Long", "uInt64" , "WIRETYPE_VARINT" , ".longValue()", WireFormat.FieldType.UINT64 ,"0L" ), + INT32 ("Integer", "int32" , "WIRETYPE_VARINT" , ".intValue()" , WireFormat.FieldType.INT32 ,"0" ), + FIXED64 ("Long", "fixed64" , "WIRETYPE_FIXED64" , ".longValue()" , WireFormat.FieldType.FIXED64 ,"0L" ), + FIXED32 ("Integer", "fixed32" , "WIRETYPE_FIXED32" , ".intValue()", WireFormat.FieldType.FIXED32 ,"0" ), + BOOL ("Boolean", "bool" , "WIRETYPE_VARINT" , ".booleanValue()", WireFormat.FieldType.BOOL ,"false" ), + STRING ("String", "string" , "WIRETYPE_LENGTH_DELIMITED", "", WireFormat.FieldType.STRING ,"\"\""), + BYTES ("byte[]", "bytes", "WIRETYPE_LENGTH_DELIMITED", "", WireFormat.FieldType.BYTES, "new byte[0]"), + UINT32 ("Integer", "uInt32" , "WIRETYPE_VARINT" , ".intValue()" , WireFormat.FieldType.UINT32 ,"0" ), + SFIXED32("Integer", "sFixed32" , "WIRETYPE_FIXED32" , ".intValue()" , WireFormat.FieldType.SFIXED32 ,"0" ), + SFIXED64("Long", "sFixed64" , "WIRETYPE_FIXED64" , ".longValue()" , WireFormat.FieldType.SFIXED64 ,"0L" ), + SINT32 ("Integer", "sInt32" , "WIRETYPE_VARINT" , ".intValue()" , WireFormat.FieldType.SINT32 ,"0" ), + SINT64 ("Long", "sInt64" , "WIRETYPE_VARINT" , ".longValue()" , WireFormat.FieldType.SINT64 ,"0L" ), + OBJECT ("Object", "object" , "WIRETYPE_LENGTH_DELIMITED" , "" , WireFormat.FieldType.MESSAGE ,null ), + ENUM ("Enum", "enum" , "WIRETYPE_VARINT" , ".ordinal()" , WireFormat.FieldType.ENUM , null ), + MAP ("Map", "map" , "WIRETYPE_VARINT" , "" , WireFormat.FieldType.MESSAGE , null ), + DATE ("Date", "int64" , "WIRETYPE_VARINT" , ".getTime()" , WireFormat.FieldType.INT64 , null ), + BIGDECIMAL("java.math.BigDecimal", "string", "WIRETYPE_LENGTH_DELIMITED", ".toString()", + WireFormat.FieldType.STRING, null), + BIGINTEGER("java.math.BigInteger", "string", "WIRETYPE_LENGTH_DELIMITED", ".toString()", + WireFormat.FieldType.STRING, null), + DEFAULT("", "" , "" , "" , WireFormat.FieldType.MESSAGE , null ); + + /** + * java original type + */ + private final String javaType; + /** + * protobuf type + */ + private final String type; + /** + * protobuf wire format type + */ + private final String wireFormat; + + /** + * to primitive type + */ + private final String toPrimitiveType; + + /** + * internal field type + */ + private WireFormat.FieldType internalFieldType; + + /** + * default value + */ + private String defaultValue; + + /** + * get the defaultValue + * @return the defaultValue + */ + public String getDefaultValue() { + return defaultValue; + } + + /** + * set defaultValue value to defaultValue + * @param defaultValue the defaultValue to set + */ + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + /** + * get the internalFieldType + * @return the internalFieldType + */ + public WireFormat.FieldType getInternalFieldType() { + return internalFieldType; + } + + /** + * set internalFieldType value to internalFieldType + * @param internalFieldType the internalFieldType to set + */ + public void setInternalFieldType(WireFormat.FieldType internalFieldType) { + this.internalFieldType = internalFieldType; + } + + /** + * get primitive type in string + * @return primitive type in string + */ + public String getToPrimitiveType() { + return toPrimitiveType; + } + + /** + * get protobuf wire format type + * @return protobuf wire format type + */ + public String getWireFormat() { + return wireFormat; + } + + /** + * get protobuf type + * @return protobuf type + */ + public String getType() { + return type; + } + + /** + * get java original type + * @return java original type + */ + public String getJavaType() { + if (this == ProtobufFieldType.ENUM) { + return Enum.class.getName(); + } + return javaType; + } + + /** + * Constructor method + * + * @param javaType java original type + * @param type protobuf type + * @param wireFormat protobuf wire format type + */ + ProtobufFieldType(String javaType, String type, String wireFormat, + String toPrimitiveType, WireFormat.FieldType internalFieldType, String defaultValue) { + this.javaType = javaType; + this.type = type; + this.wireFormat = wireFormat; + this.toPrimitiveType = toPrimitiveType; + this.internalFieldType = internalFieldType; + this.defaultValue = defaultValue; + } + + /** + * Checks if is primitive. + * + * @return true, if is primitive + */ + public boolean isPrimitive() { + if (this == INT32 || this == DOUBLE || this == FIXED32 + || this == FIXED64 || this == FLOAT + || this == INT64 || this == SFIXED32 + || this == SFIXED64 || this == SINT32 + || this == SINT64 || this == BOOL || this == ProtobufFieldType.UINT32 || this == ProtobufFieldType.UINT64) { + return true; + } + + return false; + } + + /** + * Checks if is enum. + * + * @return true, if is enum + */ + public boolean isEnum() { + return this == ENUM; + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxyFactory.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxyFactory.java new file mode 100644 index 0000000000..93f874e745 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxyFactory.java @@ -0,0 +1,40 @@ +package com.taobao.arthas.protobuf;/** + * @author: 風楪 + * @date: 2024/7/17 下午9:57 + */ + + +import com.taobao.arthas.Main; +import com.taobao.arthas.protobuf.utils.MiniTemplator; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author: FengYe + * @date: 2024/7/17 下午9:57 + * @description: ProtoBufProxy + */ +public class ProtobufProxyFactory { + private static final String TEMPLATE_FILE = "/class_template.tpl"; + + private static final Map codecCache = new ConcurrentHashMap(); + + public static ProtobufCodec getCodec(Class clazz) throws IOException { + ProtobufCodec codec = codecCache.get(clazz.getName()); + if (codec != null) { + return codec; + } + return create(clazz); + } + + public static ProtobufCodec create(Class clazz) throws IOException { + String path = Objects.requireNonNull(clazz.getResource(TEMPLATE_FILE)).getPath(); + MiniTemplator miniTemplator = new MiniTemplator(path); + + return null; + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/MiniTemplator.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/MiniTemplator.java new file mode 100644 index 0000000000..1701d1034d --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/MiniTemplator.java @@ -0,0 +1,1305 @@ +// Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland +// www.source-code.biz, www.inventec.ch/chdh +// +// This module is multi-licensed and may be used under the terms +// of any of the following licenses: +// +// EPL, Eclipse Public License, http://www.eclipse.org/legal +// LGPL, GNU Lesser General Public License, http://www.gnu.org/licenses/lgpl.html +// +// Please contact the author if you need another license. +// This module is provided "as is", without warranties of any kind. + +package com.taobao.arthas.protobuf.utils;; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * A compact template engine for HTML files. + * + *

+ * Template syntax:
+ *

+ *    Variables:
+ *       ${VariableName}
+ *
+ *    Blocks:
+ *       <!-- $beginBlock blockName -->
+ *         ... block contents ...
+ *       <!-- $endBlock blockName -->
+ *
+ *    Conditional blocks:
+ *       <!-- $if flag1 flag2 -->
+ *         ... included if flag1 or flag2 is set ...
+ *       <!-- $elseIf !flag3 flag4 -->
+ *         ... included if flag3 is not set or flag4 is set ...
+ *       <!-- $else -->
+ *         ... included if none of the above conditions is met ...
+ *       <!-- $endIf -->
+ *
+ *    Short form of conditional blocks:
+ *    (only recognized if {@link TemplateSpecification#shortFormEnabled TemplateSpecification.shortFormEnabled} is true)
+ *       <$? flag1 flag2 >
+ *         ... included if flag1 or flag2 is set ...
+ *       <$: !flag3 flag4 >
+ *         ... included if flag3 is not set or flag4 is set ...
+ *       <$:>
+ *         ... included if none of the above conditions is met ...
+ *       <$/?>
+ *    Example:
+ *       <$?de> Hallo Welt!
+ *       <$:fr> Bonjour tout le monde!
+ *       <$:  > Hello world!
+ *       <$/?>
+ *
+ *    Include a subtemplate:
+ *       <!-- $include relativeFileName -->
+ * + *

+ * General remarks:

+ *
    + *
  • Variable names, block names, condition flags and commands (e.g. "$beginBlock") are case-insensitive.
  • + *
  • The same variable may be used multiple times within a template.
  • + *
  • Multiple blocks with the same name may occur within a template.
  • + *
  • Blocks can be nested.
  • + *
  • Conditional blocks ($if) and includes ($include) are resolved when the template is parsed. + * Parsing is done within the MiniTemplator constructor. + * Condition flags can be passed to the constructor using {@link TemplateSpecification}. + *
  • Normal blocks ($beginBlock) must be added (and can be repeated) by the application program using addBlock(). + *
  • The {@link MiniTemplatorCache} class may be used to cache MiniTemplator objects with parsed templates.
  • + *
+ * + *

+ * Project home page: www.source-code.biz/MiniTemplator
+ * Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland + */ +public class MiniTemplator { + +//--- exceptions ----------------------------------------------------- + + /** + * Thrown when a syntax error is encountered within the template. + */ + public static class TemplateSyntaxException extends RuntimeException { + private static final long serialVersionUID = 1; + public TemplateSyntaxException (String msg) { + super("Syntax error in template: " + msg); }} + + /** + * Thrown when {@link MiniTemplator#setVariable(String, String, boolean) Minitemplator.setVariable} + * is called with a variableName that is not defined + * within the template and the isOptional parameter is false. + */ + public static class VariableNotDefinedException extends RuntimeException { + private static final long serialVersionUID = 1; + public VariableNotDefinedException (String variableName) { + super("Variable \"" + variableName + "\" not defined in template."); }} + + /** + * Thrown when {@link MiniTemplator#addBlock Minitemplator.addBlock} + * is called with a blockName that is not defined + * within the template. + */ + public static class BlockNotDefinedException extends RuntimeException { + private static final long serialVersionUID = 1; + public BlockNotDefinedException (String blockName) { + super("Block \"" + blockName + "\" not defined in template."); }} + +//--- public nested classes ------------------------------------------ + + /** + * Specifies the parameters for constructing a {@link MiniTemplator} object. + */ + public static class TemplateSpecification { // template specification + + /** + * The file name of the template file. + */ + public String templateFileName; + + /** + * The path of the base directory for reading subtemplate files. + * This path is used to convert the relative paths of subtemplate files (specified with the $include commands) + * into absolute paths. + * If this field is null, the parent directory of the main template file (specified by templateFileName) is used. + */ + public String subtemplateBasePath; + + /** + * The character set to be used for reading and writing files. + * This charset is used for reading the template and subtemplate files and for + * writing output with {@link #generateOutput(String outputFileName)}. + * If this field is null, the default charset of the Java VM is used. + */ + public Charset charset; + + /** + * The contents of the template file. + * This field may be used instead of templateFileName to pass the template text in memory. + * If this field is not null, templateFileName will be ignored. + */ + public String templateText; + + /** + * Flags for the conditional commands ($if, $elseIf). + * A set of flag names, that can be used with the $if and $elseIf commands. + * The flag names are case-insensitive. + */ + public Set conditionFlags; + + /** + * Enables the short form syntax for conditional blocks. + */ + public boolean shortFormEnabled; } + +//--- private nested classes ----------------------------------------- + + private static class BlockDynTabRec { // block dynamic data table record structure + int instances; // number of instances of this block + int firstBlockInstNo; // block instance no of first instance of this block or -1 + int lastBlockInstNo; // block instance no of last instance of this block or -1 + int currBlockInstNo; } // current block instance no, used during generation of output file + private static class BlockInstTabRec { // block instance table record structure + int blockNo; // block number + int instanceLevel; // instance level of this block + // InstanceLevel is an instance counter per block. + // (In contrast to blockInstNo, which is an instance counter over the instances of all blocks) + int parentInstLevel; // instance level of parent block + int nextBlockInstNo; // pointer to next instance of this block or -1 + // Forward chain for instances of same block. + String[] blockVarTab; } // block instance variables + +//--- private variables ---------------------------------------------- + + private MiniTemplatorParser mtp; // contains the parsed template + private Charset charset; // charset used for reading and writing files + private String subtemplateBasePath; // base path for relative file names of subtemplates, may be null + + private String[] varValuesTab; // variable values table, entries may be null + + private BlockDynTabRec[] blockDynTab; // dynamic block-specific values + private BlockInstTabRec[] blockInstTab; // block instances table + // This table contains an entry for each block instance that has been added. + // Indexed by BlockInstNo. + private int blockInstTabCnt; // no of entries used in BlockInstTab + +//--- constructors --------------------------------------------------- + + /** + * Constructs a MiniTemplator object. + *

During construction, the template and subtemplate files are read and parsed. + *

Note: The {@link MiniTemplatorCache} class may be used to cache MiniTemplator objects. + * @param templateSpec the template specification. + * @throws TemplateSyntaxException when a syntax error is detected within the template. + * @throws IOException when an i/o error occurs while reading the template. + */ + public MiniTemplator (TemplateSpecification templateSpec) + throws IOException, TemplateSyntaxException { + init(templateSpec); } + + /** + * Constructs a MiniTemplator object by specifying only the file name. + *

This is a convenience constructor that may be used when only the file name has to be specified. + * @param templateFileName the file name of the template file. + * @throws TemplateSyntaxException when a syntax error is detected within the template. + * @throws IOException when an i/o error occurs while reading the template. + * @see #MiniTemplator(TemplateSpecification) + */ + public MiniTemplator (String templateFileName) + throws IOException, TemplateSyntaxException { + TemplateSpecification templateSpec = new TemplateSpecification(); + templateSpec.templateFileName = templateFileName; + init(templateSpec); } + + private void init (TemplateSpecification templateSpec) + throws IOException, TemplateSyntaxException { + charset = templateSpec.charset; + if (charset == null) { + charset = Charset.defaultCharset(); } + subtemplateBasePath = templateSpec.subtemplateBasePath; + if (subtemplateBasePath == null && templateSpec.templateFileName != null) { + subtemplateBasePath = new File(templateSpec.templateFileName).getParent(); } + String templateText = templateSpec.templateText; + if (templateText == null && templateSpec.templateFileName != null) { + templateText = readFileIntoString(templateSpec.templateFileName); } + if (templateText == null) { + throw new IllegalArgumentException("No templateFileName or templateText specified."); } + mtp = new MiniTemplatorParser(templateText, templateSpec.conditionFlags, templateSpec.shortFormEnabled, this); + reset(); } + + /** + * Dummy constructor, used internally in newInstance(). + */ + protected MiniTemplator() {} + + /** + * Allocates a new uninitialized MiniTemplator object. + * This method is intended to be overridden in a derived class. + * It is called from cloneReset() to create a new MiniTemplator object. + */ + protected MiniTemplator newInstance() { + return new MiniTemplator(); } + +//--- loadSubtemplate ------------------------------------------------ + + /** + * Loads the template string of a subtemplate (used for the $Include command). + * This method can be overridden in a subclass, to load subtemplates from + * somewhere else, e.g. from a database. + *

This implementation of the method interprets subtemplateName + * as a relative file path name and reads the template string from that file. + * {@link MiniTemplator.TemplateSpecification#subtemplateBasePath} is used to convert + * the relative path of the subtemplate into an absolute path. + * @param subtemplateName the name of the subtemplate. + * Normally a relative file path. + * This is the argument string that was specified with the "$Include" command. + * If the string has quotes, the quotes are removed before this method is called. + * @return the template text string of the subtemplate. + */ + protected String loadSubtemplate (String subtemplateName) throws IOException { + String fileName = new File(subtemplateBasePath, subtemplateName).getPath(); + return readFileIntoString(fileName); } + +//--- build up (template variables and blocks) ------------------------ + + /** + * Resets the MiniTemplator object to the initial state. + * All variable values are cleared and all added block instances are deleted. + * This method can be used to produce another HTML page with the same + * template. It is faster than creating another MiniTemplator object, + * because the template does not have to be read and parsed again. + */ + public void reset() { + if (varValuesTab == null) { + varValuesTab = new String[mtp.varTabCnt]; } + else { + for (int varNo=0; varNoThis method is used by the {@link MiniTemplatorCache} class to + * clone the cached MiniTemplator objects. + */ + public MiniTemplator cloneReset() { + MiniTemplator m = newInstance(); + m.mtp = mtp; // the MiniTemplatorParser object is shared among the clones + m.charset = charset; + // (subtemplateBasePath does not have to be copied, because the subtemplates have already been read) + m.reset(); + return m; } + + /** + * Sets a template variable. + *

For variables that are used in blocks, the variable value + * must be set before addBlock() is called. + * @param variableName the name of the variable to be set. Case-insensitive. + * @param variableValue the new value of the variable. May be null. + * @param isOptional specifies whether an exception should be thrown when the + * variable does not exist in the template. If isOptional is + * false and the variable does not exist, an exception is thrown. + * @throws VariableNotDefinedException when no variable with the + * specified name exists in the template and isOptional is false. + */ + public void setVariable (String variableName, String variableValue, boolean isOptional) + throws VariableNotDefinedException { + int varNo = mtp.lookupVariableName(variableName); + if (varNo == -1) { + if (isOptional) { + return; } + throw new VariableNotDefinedException(variableName); } + varValuesTab[varNo] = variableValue; } + + /** + * Sets a template variable. + *

Convenience method for: setVariable (variableName, variableValue, false) + * @param variableName the name of the variable to be set. Case-insensitive. + * @param variableValue the new value of the variable. May be null. + * @throws VariableNotDefinedException when no variable with the + * specified name exists in the template. + * @see #setVariable(String, String, boolean) + */ + public void setVariable (String variableName, String variableValue) + throws VariableNotDefinedException { + setVariable(variableName, variableValue, false); } + + /** + * Sets a template variable to an integer value. + *

Convenience method for: setVariable (variableName, Integer.toString(variableValue)) + * @param variableName the name of the variable to be set. Case-insensitive. + * @param variableValue the new value of the variable. + * @throws VariableNotDefinedException when no variable with the + * specified name exists in the template. + */ + public void setVariable (String variableName, int variableValue) + throws VariableNotDefinedException { + setVariable(variableName, Integer.toString(variableValue)); } + + /** + * Sets an optional template variable. + *

Convenience method for: setVariable (variableName, variableValue, true) + * @param variableName the name of the variable to be set. Case-insensitive. + * @param variableValue the new value of the variable. May be null. + * @see #setVariable(String, String, boolean) + */ + public void setVariableOpt (String variableName, String variableValue) { + setVariable(variableName, variableValue, true); } + + /** + * Sets an optional template variable to an integer value. + *

Convenience method for: setVariableOpt (variableName, Integer.toString(variableValue)) + * @param variableName the name of the variable to be set. Case-insensitive. + * @param variableValue the new value of the variable. + */ + public void setVariableOpt (String variableName, int variableValue) { + // We want to avoid the integer to string conversion if the template variable does not exist. + int varNo = mtp.lookupVariableName(variableName); + if (varNo == -1) { + return; } + varValuesTab[varNo] = Integer.toString(variableValue); } + + /** + * Sets a template variable to an escaped value. + *

Convenience method for: setVariable (variableName, MiniTemplator.escapeHtml(variableValue), isOptional) + * @param variableName the name of the variable to be set. + * @param variableValue the new value of the variable. May be null. + * Special HTML/XML characters are escaped. + * @param isOptional specifies whether an exception should be thrown when the + * variable does not exist in the template. If isOptional is + * false and the variable does not exist, an exception is thrown. + * @throws VariableNotDefinedException when no variable with the + * specified name exists in the template and isOptional is false. + * @see #setVariable(String, String, boolean) + * @see #escapeHtml(String) + */ + public void setVariableEsc (String variableName, String variableValue, boolean isOptional) + throws VariableNotDefinedException { + setVariable(variableName, escapeHtml(variableValue), isOptional); } + + /** + * Sets a template variable to an escaped value. + *

Convenience method for: setVariable (variableName, MiniTemplator.escapeHtml(variableValue), false) + * @param variableName the name of the variable to be set. Case-insensitive. + * @param variableValue the new value of the variable. May be null. + * Special HTML/XML characters are escaped. + * @throws VariableNotDefinedException when no variable with the + * specified name exists in the template. + * @see #setVariable(String, String, boolean) + * @see #escapeHtml(String) + */ + public void setVariableEsc (String variableName, String variableValue) + throws VariableNotDefinedException { + setVariable(variableName, escapeHtml(variableValue), false); } + + /** + * Sets an optional template variable to an escaped value. + *

Convenience method for: setVariable (variableName, MiniTemplator.escapeHtml(variableValue), true) + * @param variableName the name of the variable to be set. Case-insensitive. + * @param variableValue the new value of the variable. May be null. + * Special HTML/XML characters are escaped. + * @see #setVariable(String, String, boolean) + * @see #escapeHtml(String) + */ + public void setVariableOptEsc (String variableName, String variableValue) { + setVariable(variableName, escapeHtml(variableValue), true); } + + /** + * Checks whether a variable with the specified name exists within the template. + * @param variableName the name of the variable. Case-insensitive. + * @return true if the variable exists.
+ * false if no variable with the specified name exists in the template. + */ + public boolean variableExists (String variableName) { + return mtp.lookupVariableName(variableName) != -1; } + + /** + * Returns a map with the names and current values of the template variables. + */ + public Map getVariables() { + HashMap map = new HashMap(mtp.varTabCnt); + for (int varNo = 0; varNo < mtp.varTabCnt; varNo++) + map.put(mtp.varTab[varNo], varValuesTab[varNo]); + return map; } + + /** + * Adds an instance of a template block. + *

If the block contains variables, these variables must be set + * before the block is added. + * If the block contains subblocks (nested blocks), the subblocks + * must be added before this block is added. + * If multiple blocks exist with the specified name, an instance + * is added for each block occurrence. + * @param blockName the name of the block to be added. Case-insensitive. + * @param isOptional specifies whether an exception should be thrown when the + * block does not exist in the template. If isOptional is + * false and the block does not exist, an exception is thrown. + * @throws BlockNotDefinedException when no block with the specified name + * exists in the template and isOptional is false. + */ + public void addBlock (String blockName, boolean isOptional) + throws BlockNotDefinedException { + int blockNo = mtp.lookupBlockName(blockName); + if(blockNo == -1) { + if (isOptional) { + return; } + throw new BlockNotDefinedException(blockName); } + while (blockNo != -1) { + addBlockByNo(blockNo); + blockNo = mtp.blockTab[blockNo].nextWithSameName; }} + + /** + * Adds an instance of a template block. + *

Convenience method for: addBlock (blockName, false) + * @param blockName the name of the block to be added. Case-insensitive. + * @throws BlockNotDefinedException when no block with the specified name + * exists in the template. + * @see #addBlock(String, boolean) + */ + public void addBlock (String blockName) + throws BlockNotDefinedException { + addBlock(blockName, false); } + + /** + * Adds an instance of an optional template block. + *

Convenience method for: addBlock (blockName, true) + * @param blockName the name of the block to be added. Case-insensitive. + * @see #addBlock(String, boolean) + */ + public void addBlockOpt (String blockName) { + addBlock(blockName, true); } + + private void addBlockByNo (int blockNo) { + MiniTemplatorParser.BlockTabRec btr = mtp.blockTab[blockNo]; + BlockDynTabRec bdtr = blockDynTab[blockNo]; + int blockInstNo = registerBlockInstance(); + BlockInstTabRec bitr = blockInstTab[blockInstNo]; + if (bdtr.firstBlockInstNo == -1) { + bdtr.firstBlockInstNo = blockInstNo; } + if (bdtr.lastBlockInstNo != -1) { + blockInstTab[bdtr.lastBlockInstNo].nextBlockInstNo = blockInstNo; } // set forward pointer of chain + bdtr.lastBlockInstNo = blockInstNo; + bitr.blockNo = blockNo; + bitr.instanceLevel = bdtr.instances++; + if (btr.parentBlockNo == -1) { + bitr.parentInstLevel = -1; } + else { + bitr.parentInstLevel = blockDynTab[btr.parentBlockNo].instances; } + bitr.nextBlockInstNo = -1; + if (btr.blockVarCnt > 0) { + bitr.blockVarTab = new String[btr.blockVarCnt]; } + for (int blockVarNo=0; blockVarNo blockInstTab.length) { + blockInstTab = (BlockInstTabRec[])MiniTemplatorParser.resizeArray(blockInstTab, 2*blockInstTabCnt); } + blockInstTab[blockInstNo] = new BlockInstTabRec(); + return blockInstNo; } + + /** + * Checks whether a block with the specified name exists within the template. + * @param blockName the name of the block. + * @return true if the block exists.
+ * false if no block with the specified name exists in the template. + */ + public boolean blockExists (String blockName) { + return mtp.lookupBlockName(blockName) != -1; } + +//--- output generation ---------------------------------------------- + + /** + * Generates the HTML page and writes it into a file. + * @param outputFileName name of the file to which the generated HTML page will be written. + * @throws IOException when an i/o error occurs while writing to the file. + */ + public void generateOutput (String outputFileName) + throws IOException { + FileOutputStream stream = null; + OutputStreamWriter writer = null; + try { + stream = new FileOutputStream(outputFileName); + writer = new OutputStreamWriter(stream, charset); + generateOutput(writer); } + finally { + if (writer != null) { + writer.close(); } + if (stream != null) { + stream.close(); }}} + + /** + * Generates the HTML page and writes it to a character stream. + * @param outputWriter a character stream (writer) to which + * the HTML page will be written. + * @throws IOException when an i/o error occurs while writing to the stream. + */ + public void generateOutput (Writer outputWriter) + throws IOException { + String s = generateOutput(); + outputWriter.write(s); } + + /** + * Generates the HTML page and returns it as a string. + * @return A string that contains the generated HTML page. + */ + public String generateOutput() { + if (blockDynTab[0].instances == 0) { + addBlockByNo(0); } // add main block + for (int blockNo=0; blockNo parentInstLevel) { + break; } + writeBlockInstance(out, blockInstNo); + bdtr.currBlockInstNo = bitr.nextBlockInstNo; }} + + private void writeBlockInstance (StringBuilder out, int blockInstNo) { + BlockInstTabRec bitr = blockInstTab[blockInstNo]; + int blockNo = bitr.blockNo; + MiniTemplatorParser.BlockTabRec btr = mtp.blockTab[blockNo]; + int tPos = btr.tPosContentsBegin; + int subBlockNo = blockNo + 1; + int varRefNo = btr.firstVarRefNo; + while (true) { + int tPos2 = btr.tPosContentsEnd; + int kind = 0; // assume end-of-block + if (varRefNo != -1 && varRefNo < mtp.varRefTabCnt) { // check for variable reference + MiniTemplatorParser.VarRefTabRec vrtr = mtp.varRefTab[varRefNo]; + if (vrtr.tPosBegin < tPos) { + varRefNo++; + continue; } + if (vrtr.tPosBegin < tPos2) { + tPos2 = vrtr.tPosBegin; + kind = 1; }} + if (subBlockNo < mtp.blockTabCnt) { // check for subblock + MiniTemplatorParser.BlockTabRec subBtr = mtp.blockTab[subBlockNo]; + if (subBtr.tPosBegin < tPos) { + subBlockNo++; + continue; } + if (subBtr.tPosBegin < tPos2) { + tPos2 = subBtr.tPosBegin; + kind = 2; }} + if (tPos2 > tPos) { + out.append(mtp.templateText.substring(tPos, tPos2)); } + switch (kind) { + case 0: // end of block + return; + case 1: { // variable + MiniTemplatorParser.VarRefTabRec vrtr = mtp.varRefTab[varRefNo]; + if (vrtr.blockNo != blockNo) { + throw new AssertionError(); } + String variableValue = bitr.blockVarTab[vrtr.blockVarNo]; + if (variableValue != null) { + out.append(variableValue); } + tPos = vrtr.tPosEnd; + varRefNo++; + break; } + case 2: { // sub block + MiniTemplatorParser.BlockTabRec subBtr = mtp.blockTab[subBlockNo]; + if (subBtr.parentBlockNo != blockNo) { + throw new AssertionError(); } + writeBlockInstances(out, subBlockNo, bitr.instanceLevel); // recursive call + tPos = subBtr.tPosEnd; + subBlockNo++; + break; }}}} + +//--- general utility routines --------------------------------------- + + // Reads the contents of a file into a string variable. + private String readFileIntoString (String fileName) + throws IOException { + FileInputStream stream = null; + InputStreamReader reader = null; + try { + stream = new FileInputStream(fileName); + reader = new InputStreamReader(stream, charset); + return readStreamIntoString(reader); } + finally { + if (reader != null) { + reader.close(); } + if (stream != null) { + stream.close(); }}} + + // Reads the contents of a stream into a string variable. + private static String readStreamIntoString (Reader reader) + throws IOException { + StringBuilder s = new StringBuilder(); + char a[] = new char[0x10000]; + while (true) { + int l = reader.read(a); + if (l == -1) { + break; } + if (l <= 0) { + throw new IOException(); } + s.append(a, 0, l); } + return s.toString(); } + + /** + * Escapes special HTML characters. + * Replaces the characters <, >, &, ' and " by their corresponding + * HTML/XML character entity codes. + * @param s the input string. + * @return the escaped output string. + */ + public static String escapeHtml (String s) { + // (The code of this method is a bit redundant in order to optimize speed) + if (s == null) { + return null; } + int sLength = s.length(); + boolean found = false; + int p; + loop1: + for (p=0; p': case '&': case '\'': case '"': found = true; break loop1; }} + if (!found) { + return s; } + StringBuilder sb = new StringBuilder(sLength+16); + sb.append(s.substring(0, p)); + for (; p': sb.append (">"); break; + case '&': sb.append ("&"); break; + case '\'': sb.append ("'"); break; + case '"': sb.append ("""); break; + default: sb.append (c); }} + return sb.toString(); } + +} // End class MiniTemplator + +//==================================================================================================================== + +// MiniTemplatorParser is an immutable object that contains the parsed template text. +class MiniTemplatorParser { + +//--- constants ------------------------------------------------------ + + private static final int maxNestingLevel = 20; // maximum number of block nestings + private static final int maxCondLevels = 20; // maximum number of nested conditional commands ($if) + private static final int maxInclTemplateSize = 1000000; // maximum length of template string when including subtemplates + private static final String cmdStartStr = ""; // command end string + private static final String cmdStartStrShort = "<$"; // short form command start string + private static final String cmdEndStrShort = ">"; // short form command end string + +//--- nested classes ------------------------------------------------- + + public static class VarRefTabRec { // variable reference table record structure + int varNo; // variable no + int tPosBegin; // template position of begin of variable reference + int tPosEnd; // template position of end of variable reference + int blockNo; // block no of the (innermost) block that contains this variable reference + int blockVarNo; } // block variable no. Index into BlockInstTab.BlockVarTab + public static class BlockTabRec { // block table record structure + String blockName; // block name + int nextWithSameName; // block no of next block with same name or -1 (blocks are backward linked related to their position within the template) + int tPosBegin; // template position of begin of block + int tPosContentsBegin; // template pos of begin of block contents + int tPosContentsEnd; // template pos of end of block contents + int tPosEnd; // template position of end of block + int nestingLevel; // block nesting level + int parentBlockNo; // block no of parent block + boolean definitionIsOpen; // true while $BeginBlock processed but no $EndBlock + int blockVarCnt; // number of variables in block + int[] blockVarNoToVarNoMap; // maps block variable numbers to variable numbers + int firstVarRefNo; // variable reference no of first variable of this block or -1 + boolean dummy; } // true if this is a dummy block that will never be included in the output + +//--- variables ------------------------------------------------------ + + public String templateText; // contents of the template file + private HashSet conditionFlags; // set of the condition flags, converted to uppercase + private boolean shortFormEnabled; // true to enable the short form of commands ("<$...>") + + public String[] varTab; // variables table, contains variable names, array index is variable no + public int varTabCnt; // no of entries used in VarTab + private HashMap varNameToNoMap; // maps variable names to variable numbers + public VarRefTabRec[] varRefTab; // variable references table + // Contains an entry for each variable reference in the template. Ordered by templatePos. + public int varRefTabCnt; // no of entries used in VarRefTab + + public BlockTabRec[] blockTab; // Blocks table, array index is block no + // Contains an entry for each block in the template. Ordered by tPosBegin. + public int blockTabCnt; // no of entries used in BlockTab + private HashMap blockNameToNoMap; // maps block names to block numbers + + // The following variables are only used temporarilly during parsing of the template. + private int currentNestingLevel; // current block nesting level during parsing + private int[] openBlocksTab; // indexed by the block nesting level + // During parsing, this table contains the block numbers of the open parent blocks (nested outer blocks). + private int condLevel; // current nesting level of conditional commands ($if), -1 = main level + private boolean[] condEnabled; // enabled/disables state for the conditions of each level + private boolean[] condPassed; // true if an enabled condition clause has already been processed (separate for each level) + private MiniTemplator miniTemplator; // the MiniTemplator who created this parser object + // The reference to the MiniTemplator object is only used to call MiniTemplator.loadSubtemplate(). + private boolean resumeCmdParsingFromStart; // true = resume command parsing from the start position of the last command + +//--- constructor ---------------------------------------------------- + + // (The MiniTemplator object is only passed to the parser, because the +// parser needs to call MiniTemplator.loadSubtemplate() to load subtemplates.) + public MiniTemplatorParser (String templateText, Set conditionFlags, boolean shortFormEnabled, MiniTemplator miniTemplator) + throws MiniTemplator.TemplateSyntaxException { + this.templateText = templateText; + this.conditionFlags = createConditionFlagsSet(conditionFlags); + this.shortFormEnabled = shortFormEnabled; + this.miniTemplator = miniTemplator; + parseTemplate(); + this.miniTemplator = null; } + + private HashSet createConditionFlagsSet (Set flags) { + if (flags == null || flags.isEmpty()) { + return null; } + HashSet flags2 = new HashSet(flags.size()); + for (String flag : flags) { + flags2.add (flag.toUpperCase()); } + return flags2; } + +//--- template parsing ----------------------------------------------- + + private void parseTemplate() + throws MiniTemplator.TemplateSyntaxException { + initParsing(); + beginMainBlock(); + parseTemplateCommands(); + endMainBlock(); + checkBlockDefinitionsComplete(); + if (condLevel != -1) { + throw new MiniTemplator.TemplateSyntaxException ("$if without matching $endIf."); } + parseTemplateVariables(); + associateVariablesWithBlocks(); + terminateParsing(); } + + private void initParsing() { + varTab = new String[64]; + varTabCnt = 0; + varNameToNoMap = new HashMap(); + varRefTab = new VarRefTabRec[64]; + varRefTabCnt = 0; + blockTab = new BlockTabRec[16]; + blockTabCnt = 0; + currentNestingLevel = 0; + blockNameToNoMap = new HashMap(); + openBlocksTab = new int[maxNestingLevel+1]; + condLevel = -1; + condEnabled = new boolean[maxCondLevels]; + condPassed = new boolean[maxCondLevels]; } + + private void terminateParsing() { + openBlocksTab = null; } + + // Registers the main block. +// The main block is an implicitly defined block that covers the whole template. + private void beginMainBlock() { + int blockNo = registerBlock(null); // =0 + BlockTabRec btr = blockTab[blockNo]; + btr.tPosBegin = 0; + btr.tPosContentsBegin = 0; + openBlocksTab[currentNestingLevel] = blockNo; + currentNestingLevel++; } + + // Completes the main block registration. + private void endMainBlock() { + BlockTabRec btr = blockTab[0]; + btr.tPosContentsEnd = templateText.length(); + btr.tPosEnd = templateText.length(); + btr.definitionIsOpen = false; + currentNestingLevel--; } + +//--- Template commands -------------------------------------------------------- + + // Parses commands within the template in the format "". +// If shortFormEnabled is true, the short form commands in the format "<$...>" are also recognized. + private void parseTemplateCommands() + throws MiniTemplator.TemplateSyntaxException { + int p = 0; // p is the current position within templateText + while (true) { + int p0 = templateText.indexOf(cmdStartStr, p); // p0 is the start of the current command + boolean shortForm = false; + if (shortFormEnabled && p0 != p) { + if (p0 == -1) { + p0 = templateText.indexOf(cmdStartStrShort, p); + shortForm = true; } + else { + int p2 = templateText.substring(p, p0).indexOf(cmdStartStrShort); + if (p2 != -1) { + p0 = p + p2; + shortForm = true; }}} + if (p0 == -1) { // no more commands + break; } + conditionalExclude(p, p0); // process text up to the start of the current command + if (shortForm) { // short form command + p = templateText.indexOf(cmdEndStrShort, p0 + cmdStartStrShort.length()); + if (p == -1) { // if no terminating ">" is found, we process it as normal text + p = p0 + cmdStartStrShort.length(); + conditionalExclude(p0, p); + continue; } + p += cmdEndStrShort.length(); + String cmdLine = templateText.substring(p0 + cmdStartStrShort.length(), p - cmdEndStrShort.length()); + if (!processShortFormTemplateCommand(cmdLine, p0, p)) { + // If a short form command is not recognized, we process the whole command structure are normal text. + conditionalExclude(p0, p); }} + else { // normal (long) form command + p = templateText.indexOf(cmdEndStr, p0 + cmdStartStr.length()); + if (p == -1) { + throw new MiniTemplator.TemplateSyntaxException("Invalid HTML comment in template at offset " + p0 + "."); } + p += cmdEndStr.length(); + String cmdLine = templateText.substring(p0 + cmdStartStr.length(), p - cmdEndStr.length()); + resumeCmdParsingFromStart = false; + if (!processTemplateCommand(cmdLine, p0, p)) { + conditionalExclude(p0, p); } // process as normal temlate text + if (resumeCmdParsingFromStart) { // (if a subtemplate has been included) + p = p0; }}}} + + // Returns false if the command should be treatet as normal template text. + private boolean processTemplateCommand (String cmdLine, int cmdTPosBegin, int cmdTPosEnd) + throws MiniTemplator.TemplateSyntaxException { + int p0 = skipBlanks(cmdLine, 0); + if (p0 >= cmdLine.length()) { + return false; } + int p = skipNonBlanks(cmdLine, p0); + String cmd = cmdLine.substring(p0, p); + String parms = cmdLine.substring(p); + /* select */ + if (cmd.equalsIgnoreCase("$beginBlock")) { + processBeginBlockCmd(parms, cmdTPosBegin, cmdTPosEnd); } + else if (cmd.equalsIgnoreCase("$endBlock")) { + processEndBlockCmd(parms, cmdTPosBegin, cmdTPosEnd); } + else if (cmd.equalsIgnoreCase("$include")) { + processIncludeCmd(parms, cmdTPosBegin, cmdTPosEnd); } + else if (cmd.equalsIgnoreCase("$if")) { + processIfCmd(parms, cmdTPosBegin, cmdTPosEnd); } + else if (cmd.equalsIgnoreCase("$elseIf")) { + processElseIfCmd(parms, cmdTPosBegin, cmdTPosEnd); } + else if (cmd.equalsIgnoreCase("$else")) { + processElseCmd(parms, cmdTPosBegin, cmdTPosEnd); } + else if (cmd.equalsIgnoreCase("$endIf")) { + processEndIfCmd(parms, cmdTPosBegin, cmdTPosEnd); } + else { + if (cmd.startsWith("$") && !cmd.startsWith("${")) { + throw new MiniTemplator.TemplateSyntaxException("Unknown command \"" + cmd + "\" in template at offset " + cmdTPosBegin + "."); } + else { + return false; }} + return true; } + + // Returns false if the command is not recognized and should be treatet as normal temlate text. + private boolean processShortFormTemplateCommand (String cmdLine, int cmdTPosBegin, int cmdTPosEnd) + throws MiniTemplator.TemplateSyntaxException { + int p0 = skipBlanks(cmdLine, 0); + if (p0 >= cmdLine.length()) { + return false; } + int p = p0; + char cmd1 = cmdLine.charAt(p++); + if (cmd1 == '/' && p < cmdLine.length() && !Character.isWhitespace(cmdLine.charAt(p))) { + p++; } + String cmd = cmdLine.substring(p0, p); + String parms = cmdLine.substring(p).trim(); + /* select */ + if (cmd.equals("?")) { + processIfCmd(parms, cmdTPosBegin, cmdTPosEnd); } + else if (cmd.equals(":")) { + if (parms.length() > 0) { + processElseIfCmd(parms, cmdTPosBegin, cmdTPosEnd); } + else { + processElseCmd(parms, cmdTPosBegin, cmdTPosEnd); }} + else if (cmd.equals("/?")) { + processEndIfCmd(parms, cmdTPosBegin, cmdTPosEnd); } + else { + return false; } + return true; } + + // Processes the $beginBlock command. + private void processBeginBlockCmd (String parms, int cmdTPosBegin, int cmdTPosEnd) + throws MiniTemplator.TemplateSyntaxException { + if (conditionalExclude(cmdTPosBegin, cmdTPosEnd)) { + return; } + int p0 = skipBlanks(parms, 0); + if (p0 >= parms.length()) { + throw new MiniTemplator.TemplateSyntaxException("Missing block name in $BeginBlock command in template at offset " + cmdTPosBegin + "."); } + int p = skipNonBlanks(parms, p0); + String blockName = parms.substring(p0, p); + if (!isRestOfStringBlank(parms, p)) { + throw new MiniTemplator.TemplateSyntaxException("Extra parameter in $BeginBlock command in template at offset " + cmdTPosBegin + "."); } + int blockNo = registerBlock(blockName); + BlockTabRec btr = blockTab[blockNo]; + btr.tPosBegin = cmdTPosBegin; + btr.tPosContentsBegin = cmdTPosEnd; + openBlocksTab[currentNestingLevel] = blockNo; + currentNestingLevel++; + if (currentNestingLevel > maxNestingLevel) { + throw new MiniTemplator.TemplateSyntaxException("Block nesting overflow for block \"" + blockName + "\" in template at offset " + cmdTPosBegin + "."); }} + + // Processes the $endBlock command. + private void processEndBlockCmd (String parms, int cmdTPosBegin, int cmdTPosEnd) + throws MiniTemplator.TemplateSyntaxException { + if (conditionalExclude(cmdTPosBegin, cmdTPosEnd)) { + return; } + int p0 = skipBlanks(parms, 0); + if (p0 >= parms.length()) { + throw new MiniTemplator.TemplateSyntaxException("Missing block name in $EndBlock command in template at offset " + cmdTPosBegin + "."); } + int p = skipNonBlanks(parms, p0); + String blockName = parms.substring(p0, p); + if (!isRestOfStringBlank(parms, p)) { + throw new MiniTemplator.TemplateSyntaxException("Extra parameter in $EndBlock command in template at offset " + cmdTPosBegin + "."); } + int blockNo = lookupBlockName(blockName); + if (blockNo == -1) { + throw new MiniTemplator.TemplateSyntaxException("Undefined block name \"" + blockName + "\" in $EndBlock command in template at offset " + cmdTPosBegin + "."); } + currentNestingLevel--; + BlockTabRec btr = blockTab[blockNo]; + if (!btr.definitionIsOpen) { + throw new MiniTemplator.TemplateSyntaxException("Multiple $EndBlock command for block \"" + blockName + "\" in template at offset " + cmdTPosBegin + "."); } + if (btr.nestingLevel != currentNestingLevel) { + throw new MiniTemplator.TemplateSyntaxException("Block nesting level mismatch at $EndBlock command for block \"" + blockName + "\" in template at offset " + cmdTPosBegin + "."); } + btr.tPosContentsEnd = cmdTPosBegin; + btr.tPosEnd = cmdTPosEnd; + btr.definitionIsOpen = false; } + + // Returns the block number of the newly registered block. + private int registerBlock (String blockName) { + int blockNo = blockTabCnt++; + if (blockTabCnt > blockTab.length) { + blockTab = (BlockTabRec[])resizeArray(blockTab, 2*blockTabCnt); } + BlockTabRec btr = new BlockTabRec(); + blockTab[blockNo] = btr; + btr.blockName = blockName; + if (blockName != null) { + btr.nextWithSameName = lookupBlockName(blockName); } + else { + btr.nextWithSameName = -1; } + btr.nestingLevel = currentNestingLevel; + if (currentNestingLevel > 0) { + btr.parentBlockNo = openBlocksTab[currentNestingLevel-1]; } + else { + btr.parentBlockNo = -1; } + btr.definitionIsOpen = true; + btr.blockVarCnt = 0; + btr.firstVarRefNo = -1; + btr.blockVarNoToVarNoMap = new int[32]; + btr.dummy = false; + if (blockName != null) { + blockNameToNoMap.put(blockName.toUpperCase(), new Integer(blockNo)); } + return blockNo; } + + // Registers a dummy block to exclude a range within the template text. + private void excludeTemplateRange (int tPosBegin, int tPosEnd) { + if (blockTabCnt > 0) { + // Check whether we can extend the previous block. + BlockTabRec btr = blockTab[blockTabCnt-1]; + if (btr.dummy && btr.tPosEnd == tPosBegin) { + btr.tPosContentsEnd = tPosEnd; + btr.tPosEnd = tPosEnd; + return; }} + int blockNo = registerBlock(null); + BlockTabRec btr = blockTab[blockNo]; + btr.tPosBegin = tPosBegin; + btr.tPosContentsBegin = tPosBegin; + btr.tPosContentsEnd = tPosEnd; + btr.tPosEnd = tPosEnd; + btr.definitionIsOpen = false; + btr.dummy = true; } + + // Checks that all block definitions are closed. + private void checkBlockDefinitionsComplete() + throws MiniTemplator.TemplateSyntaxException { + for (int blockNo=0; blockNo= parms.length()) { + throw new MiniTemplator.TemplateSyntaxException("Missing subtemplate name in $Include command in template at offset " + cmdTPosBegin + "."); } + int p; + if (parms.charAt(p0) == '"') { // subtemplate name is quoted + p0++; + p = parms.indexOf('"', p0); + if (p == -1) { + throw new MiniTemplator.TemplateSyntaxException("Missing closing quote for subtemplate name in $Include command in template at offset " + cmdTPosBegin + "."); }} + else { + p = skipNonBlanks(parms, p0); } + String subtemplateName = parms.substring(p0, p); + p++; + if (!isRestOfStringBlank(parms, p)) { + throw new MiniTemplator.TemplateSyntaxException("Extra parameter in $Include command in template at offset " + cmdTPosBegin + "."); } + insertSubtemplate(subtemplateName, cmdTPosBegin, cmdTPosEnd); } + + private void insertSubtemplate (String subtemplateName, int tPos1, int tPos2) { + if (templateText.length() > maxInclTemplateSize) { + throw new RuntimeException("Subtemplate include aborted because the internal template string is longer than "+maxInclTemplateSize+" characters."); } + String subtemplate; + try { + subtemplate = miniTemplator.loadSubtemplate(subtemplateName); } + catch (IOException e) { + throw new RuntimeException("Error while loading subtemplate \""+subtemplateName+"\"", e); } + // (Copying the template to insert a subtemplate is a bit slow. In a future implementation of MiniTemplator, + // a table could be used that contains references to the string fragments.) + StringBuilder s = new StringBuilder(templateText.length()+subtemplate.length()); + s.append(templateText, 0, tPos1); + s.append(subtemplate); + s.append(templateText, tPos2, templateText.length()); + templateText = s.toString(); + resumeCmdParsingFromStart = true; } + +//--- Conditional commands ----------------------------------------------------- + + // Returns the enabled/disabled state of the condition at level condLevel2. + private boolean isCondEnabled (int condLevel2) { + if (condLevel2 < 0) { + return true; } + return condEnabled[condLevel2]; } + + // If the current condition is disabled, the text from tPosBegin to tPosEnd +// is excluded and true is returned. +// Otherwise nothing is done and false is returned. + private boolean conditionalExclude (int tPosBegin, int tPosEnd) { + if (isCondEnabled(condLevel)) { + return false; } + excludeTemplateRange(tPosBegin, tPosEnd); + return true; } + + // Evaluates a condition expression of a conditional command, by comparing the +// flags in the expression with the flags in TemplateSpecification.conditionFlags. +// Returns true the condition is met. + private boolean evaluateConditionFlags (String flags) { + int p = 0; + while (true) { + p = skipBlanks(flags, p); + if (p >= flags.length()) { + break; } + boolean complement = false; + if (flags.charAt(p) == '!') { + complement = true; p++; } + p = skipBlanks(flags, p); + if (p >= flags.length()) { + break; } + int p0 = p; + p = skipNonBlanks(flags, p0+1); + String flag = flags.substring(p0, p).toUpperCase(); + if ((conditionFlags != null && conditionFlags.contains(flag)) ^ complement) { + return true; }} + return false; } + + // Processes the $if command. + private void processIfCmd (String parms, int cmdTPosBegin, int cmdTPosEnd) + throws MiniTemplator.TemplateSyntaxException { + excludeTemplateRange(cmdTPosBegin, cmdTPosEnd); + if (condLevel >= maxCondLevels-1) { + throw new MiniTemplator.TemplateSyntaxException ("Too many nested $if commands."); } + condLevel++; + boolean enabled = isCondEnabled(condLevel-1) && evaluateConditionFlags(parms); + condEnabled[condLevel] = enabled; + condPassed[condLevel] = enabled; } + + // Processes the $elseIf command. + private void processElseIfCmd (String parms, int cmdTPosBegin, int cmdTPosEnd) + throws MiniTemplator.TemplateSyntaxException { + excludeTemplateRange(cmdTPosBegin, cmdTPosEnd); + if (condLevel < 0) { + throw new MiniTemplator.TemplateSyntaxException ("$elseIf without matching $if."); } + boolean enabled = isCondEnabled(condLevel-1) && !condPassed[condLevel] && evaluateConditionFlags(parms); + condEnabled[condLevel] = enabled; + if (enabled) { + condPassed[condLevel] = true; }} + + // Processes the $else command. + private void processElseCmd (String parms, int cmdTPosBegin, int cmdTPosEnd) + throws MiniTemplator.TemplateSyntaxException { + excludeTemplateRange(cmdTPosBegin, cmdTPosEnd); + if (parms.trim().length() != 0) { + throw new MiniTemplator.TemplateSyntaxException ("Invalid parameters for $else command."); } + if (condLevel < 0) { + throw new MiniTemplator.TemplateSyntaxException ("$else without matching $if."); } + boolean enabled = isCondEnabled(condLevel-1) && !condPassed[condLevel]; + condEnabled[condLevel] = enabled; + if (enabled) { + condPassed[condLevel] = true; }} + + // Processes the $endIf command. + private void processEndIfCmd (String parms, int cmdTPosBegin, int cmdTPosEnd) + throws MiniTemplator.TemplateSyntaxException { + excludeTemplateRange(cmdTPosBegin, cmdTPosEnd); + if (parms.trim().length() != 0) { + throw new MiniTemplator.TemplateSyntaxException ("Invalid parameters for $endIf command."); } + if (condLevel < 0) { + throw new MiniTemplator.TemplateSyntaxException ("$endif without matching $if."); } + condLevel--; } + +//------------------------------------------------------------------------------ + + // Associates variable references with blocks. + private void associateVariablesWithBlocks() { + int varRefNo = 0; + int activeBlockNo = 0; + int nextBlockNo = 1; + while (varRefNo < varRefTabCnt) { + VarRefTabRec vrtr = varRefTab[varRefNo]; + int varRefTPos = vrtr.tPosBegin; + int varNo = vrtr.varNo; + if (varRefTPos >= blockTab[activeBlockNo].tPosEnd) { + activeBlockNo = blockTab[activeBlockNo].parentBlockNo; + continue; } + if (nextBlockNo < blockTabCnt && varRefTPos >= blockTab[nextBlockNo].tPosBegin) { + activeBlockNo = nextBlockNo; + nextBlockNo++; + continue; } + BlockTabRec btr = blockTab[activeBlockNo]; + if (varRefTPos < btr.tPosBegin) { + throw new AssertionError(); } + int blockVarNo = btr.blockVarCnt++; + if (btr.blockVarCnt > btr.blockVarNoToVarNoMap.length) { + btr.blockVarNoToVarNoMap = (int[])resizeArray(btr.blockVarNoToVarNoMap, 2*btr.blockVarCnt); } + btr.blockVarNoToVarNoMap[blockVarNo] = varNo; + if (btr.firstVarRefNo == -1) { + btr.firstVarRefNo = varRefNo; } + vrtr.blockNo = activeBlockNo; + vrtr.blockVarNo = blockVarNo; + varRefNo++; }} + + // Parses variable references within the template in the format "${VarName}" . + private void parseTemplateVariables() + throws MiniTemplator.TemplateSyntaxException { + int p = 0; + while (true) { + p = templateText.indexOf("${", p); + if (p == -1) { + break; } + int p0 = p; + p = templateText.indexOf("}", p); + if (p == -1) { + throw new MiniTemplator.TemplateSyntaxException("Invalid variable reference in template at offset " + p0 + "."); } + p++; + String varName = templateText.substring(p0+2, p-1).trim(); + if (varName.length() == 0) { + throw new MiniTemplator.TemplateSyntaxException("Empty variable name in template at offset " + p0 + "."); } + registerVariableReference(varName, p0, p); }} + + private void registerVariableReference (String varName, int tPosBegin, int tPosEnd) { + int varNo; + varNo = lookupVariableName(varName); + if (varNo == -1) { + varNo = registerVariable(varName); } + int varRefNo = varRefTabCnt++; + if (varRefTabCnt > varRefTab.length) { + varRefTab = (VarRefTabRec[])resizeArray(varRefTab, 2*varRefTabCnt); } + VarRefTabRec vrtr = new VarRefTabRec(); + varRefTab[varRefNo] = vrtr; + vrtr.tPosBegin = tPosBegin; + vrtr.tPosEnd = tPosEnd; + vrtr.varNo = varNo; } + + // Returns the variable number of the newly registered variable. + private int registerVariable (String varName) { + int varNo = varTabCnt++; + if (varTabCnt > varTab.length) { + varTab = (String[])resizeArray(varTab, 2*varTabCnt); } + varTab[varNo] = varName; + varNameToNoMap.put(varName.toUpperCase(), new Integer(varNo)); + return varNo; } + +//--- name lookup routines ------------------------------------------- + + // Maps variable name to variable number. +// Returns -1 if the variable name is not found. + public int lookupVariableName (String varName) { + Integer varNoWrapper = varNameToNoMap.get(varName.toUpperCase()); + if (varNoWrapper == null) { + return -1; } + int varNo = varNoWrapper.intValue(); + return varNo; } + + // Maps block name to block number. +// If there are multiple blocks with the same name, the block number of the last +// registered block with that name is returned. +// Returns -1 if the block name is not found. + public int lookupBlockName (String blockName) { + Integer blockNoWrapper = blockNameToNoMap.get(blockName.toUpperCase()); + if (blockNoWrapper == null) { + return -1; } + int blockNo = blockNoWrapper.intValue(); + return blockNo; } + +//--- general utility routines --------------------------------------- + + // Reallocates an array with a new size and copies the contents +// of the old array to the new array. + public static Object resizeArray (Object oldArray, int newSize) { + int oldSize = java.lang.reflect.Array.getLength(oldArray); + Class elementType = oldArray.getClass().getComponentType(); + Object newArray = java.lang.reflect.Array.newInstance( + elementType, newSize); + int preserveLength = Math.min(oldSize, newSize); + if (preserveLength > 0) { + System.arraycopy(oldArray, 0, newArray, 0, preserveLength); } + return newArray; } + + // Skips blanks (white space) in string s starting at position p. + private static int skipBlanks (String s, int p) { + while (p < s.length() && Character.isWhitespace(s.charAt(p))) p++; + return p; } + + // Skips non-blanks (no-white space) in string s starting at position p. + private static int skipNonBlanks (String s, int p) { + while (p < s.length() && !Character.isWhitespace(s.charAt(p))) p++; + return p; } + + // Returns true if string s is blank (white space) from position p to the end. + public static boolean isRestOfStringBlank (String s, int p) { + return skipBlanks(s, p) >= s.length(); } + +} // End class MiniTemplatorParser \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/impl/HelloServiceImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/impl/HelloServiceImpl.java deleted file mode 100644 index 3581bae3ae..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/impl/HelloServiceImpl.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.taobao.arthas.service.impl;/** - * @author: 風楪 - * @date: 2024/6/30 下午6:42 - */ - -import helloworld.HelloServiceGrpc; -import helloworld.Test; -import io.grpc.stub.StreamObserver; - -/** - * @author: FengYe - * @date: 2024/6/30 下午6:42 - * @description: HelloServiceImpl - */ -public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase { - @Override - public void sayHello(Test.HelloRequest request, StreamObserver responseObserver) { - String name = request.getName(); - System.out.println(name); - responseObserver.onNext(Test.HelloReply.newBuilder().setMessage(name).build()); - responseObserver.onCompleted(); - } -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java index de7e35ea7e..5f2eb61b51 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java @@ -3,62 +3,104 @@ * @date: 2024/7/14 上午4:28 */ +import com.baidu.bjf.remoting.protobuf.annotation.ProtobufClass; import com.google.protobuf.*; -import com.taobao.arthas.service.ArthasSampleService; -import helloworld.Test; -import io.netty.buffer.ByteBuf; import java.io.IOException; import java.nio.ByteBuffer; -import java.util.Collections; -import java.util.Map; /** * @author: FengYe * @date: 2024/7/14 上午4:28 * @description: ArthasSampleRequest */ +@ProtobufClass public class ArthasSampleRequest{ + @Override + public String toString() { + return "ArthasSampleRequest{" + +// "name='" + name + '\'' + + ", age=" + age + + ", price=" + price + + ", man=" + man + + '}'; + } +// +// public String getName() { +// return name; +// } +// +// public void setName(String name) { +// this.name = name; +// } + + public Double getAge() { + return age; + } + + public void setAge(Double age) { + this.age = age; + } - private String name; + public Long getPrice() { + return price; + } - public ArthasSampleRequest(ByteBuffer byteBuffer){ - CodedInputStream codedInputStream = CodedInputStream.newInstance(byteBuffer); - try { - // 读取标签 - int tag; - while ((tag = codedInputStream.readTag()) != 0) { - int fieldNumber = WireFormat.getTagFieldNumber(tag); - int wireType = WireFormat.getTagWireType(tag); + public void setPrice(Long price) { + this.price = price; + } - System.out.println("Field Number: " + fieldNumber); - System.out.println("Wire Type: " + wireType); + public Float getMan() { + return man; + } - // 根据字段编号和类型读取对应的数据 - switch (wireType) { - case WireFormat.WIRETYPE_VARINT: - long varintValue = codedInputStream.readInt64(); - System.out.println("Varint Value: " + varintValue); - break; - case WireFormat.WIRETYPE_FIXED32: - int fixed32Value = codedInputStream.readFixed32(); - System.out.println("Fixed32 Value: " + fixed32Value); - break; - case WireFormat.WIRETYPE_FIXED64: - long fixed64Value = codedInputStream.readFixed64(); - System.out.println("Fixed64 Value: " + fixed64Value); - break; - case WireFormat.WIRETYPE_LENGTH_DELIMITED: - int length = codedInputStream.readRawVarint32(); - byte[] bytes = codedInputStream.readRawBytes(length); - System.out.println("Length-delimited Value: " + new String(bytes)); - break; - default: - throw new IOException("Unsupported wire type: " + wireType); - } - } - } catch (IOException e) { - e.printStackTrace(); - } + public void setMan(Float man) { + this.man = man; } + +// private String name; + private Double age; + private Long price; + private Float man; + +// public ArthasSampleRequest(ByteBuffer byteBuffer){ +// CodedInputStream codedInputStream = CodedInputStream.newInstance(byteBuffer); +// try { +// // 读取标签 +// int tag; +// while ((tag = codedInputStream.readTag()) != 0) { +// int fieldNumber = WireFormat.getTagFieldNumber(tag); +// int wireType = WireFormat.getTagWireType(tag); +// +// System.out.println("Field Number: " + fieldNumber); +// System.out.println("Wire Type: " + wireType); +// +// +// // 根据字段编号和类型读取对应的数据 +// switch (wireType) { +// case WireFormat.WIRETYPE_VARINT: +// long varintValue = codedInputStream.readInt64(); +// System.out.println("Varint Value: " + varintValue); +// break; +// case WireFormat.WIRETYPE_FIXED32: +// int fixed32Value = codedInputStream.readFixed32(); +// System.out.println("Fixed32 Value: " + fixed32Value); +// break; +// case WireFormat.WIRETYPE_FIXED64: +// long fixed64Value = codedInputStream.readFixed64(); +// System.out.println("Fixed64 Value: " + fixed64Value); +// break; +// case WireFormat.WIRETYPE_LENGTH_DELIMITED: +// int length = codedInputStream.readRawVarint32(); +// byte[] bytes = codedInputStream.readRawBytes(length); +// System.out.println("Length-delimited Value: " + new String(bytes)); +// break; +// default: +// throw new IOException("Unsupported wire type: " + wireType); +// } +// } +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } } diff --git a/arthas-grpc-server/src/main/resources/class_template.tpl b/arthas-grpc-server/src/main/resources/class_template.tpl new file mode 100644 index 0000000000..f7031aa529 --- /dev/null +++ b/arthas-grpc-server/src/main/resources/class_template.tpl @@ -0,0 +1,102 @@ +${package} + +import java.io.Serializable; + +import ${importPackage}; + + +public class ${className} implements ${codecClassName}<${targetProxyClassName}>, Serializable { + public static final long serialVersionUID = 1L; + private ${descriptorClsName} descriptor; + + public byte[] encode(${targetProxyClassName} t) throws IOException { + CodecOutputByteArray output = CodecOutputByteArray.get(); + doWriteTo(t, output.getCodedOutputStream()); + return output.getData(); + } + + public ${targetProxyClassName} decode(byte[] bb) throws IOException { + CodedInputStream input = CodedInputStream.newInstance(bb, 0, bb.length); + return readFrom(input); + } + + public int size(${targetProxyClassName} t) throws IOException { + int size = 0; + + ${encodeFieldType} ${encodeFieldName} = null; + if (!CodedConstant.isNull(${encodeFieldGetter})) { + ${encodeFieldName} = ${writeValueToField}; + size += ${calcSize} + } + ${checkNull} + + return size; + } + + public void doWriteTo(${targetProxyClassName} t, CodedOutputStream output) + throws IOException { + + ${encodeFieldType} ${encodeFieldName} = null; + if (!CodedConstant.isNull(${encodeFieldGetter})) { + ${encodeFieldName} = ${writeValueToField}; + ${encodeWriteFieldValue} + } + + } + + public void writeTo(${targetProxyClassName} t, CodedOutputStream output) + throws IOException { + doWriteTo(t, output); + } + + public ${targetProxyClassName} readFrom(CodedInputStream input) throws IOException { + ${targetProxyClassName} ret = new ${targetProxyClassName}(); + + ${initListMapFields} + + + ${enumInitialize}; + + try { + boolean done = false; + Codec codec = null; + while (!done) { + int tag = input.readTag(); + if (tag == 0) { + break; + } + + if (tag == ${decodeOrder}) { + ${objectDecodeExpress} + ${decodeFieldSetValue} + ${objectDecodeExpressSuffix} + ${deocdeCheckNull} + continue; + } + ${objectPackedDecodeExpress} + + + input.skipField(tag); + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e; + } catch (java.io.IOException e) { + throw e; + } + + return ret; + + } + + + public com.google.protobuf.Descriptors.Descriptor getDescriptor() throws IOException { + if (this.descriptor != null) { + return this.descriptor; + } + com.google.protobuf.Descriptors.Descriptor descriptor = + CodedConstant.getDescriptor(${targetProxyClassName}.class); + return (this.descriptor = descriptor); + } +} + + \ No newline at end of file From 8761959911d9fffab678769783999f64e16dca3b Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Wed, 24 Jul 2024 02:15:53 +0800 Subject: [PATCH 08/48] init --- .../taobao/arthas/protobuf/ProtobufProxy.java | 84 +++++++++++++++++++ .../arthas/protobuf/ProtobufProxyFactory.java | 40 --------- .../src/main/resources/class_template.tpl | 2 +- 3 files changed, 85 insertions(+), 41 deletions(-) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxyFactory.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java new file mode 100644 index 0000000000..e343d3a42b --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java @@ -0,0 +1,84 @@ +package com.taobao.arthas.protobuf;/** + * @author: 風楪 + * @date: 2024/7/17 下午9:57 + */ + + +import com.baidu.bjf.remoting.protobuf.Codec; +import com.taobao.arthas.protobuf.utils.MiniTemplator; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author: FengYe + * @date: 2024/7/17 下午9:57 + * @description: ProtoBufProxy + */ +public class ProtobufProxy { + private static final String TEMPLATE_FILE = "/class_template.tpl"; + + private static final Map codecCache = new ConcurrentHashMap(); + + private Class clazz; + + private MiniTemplator miniTemplator; + + public ProtobufProxy(Class clazz) { + this.clazz = clazz; + } + + public ProtobufCodec getCodecCacheSide(Class clazz) { + ProtobufCodec codec = codecCache.get(clazz.getName()); + if (codec != null) { + return codec; + } + try { + codec = create(clazz); + codecCache.put(clazz.getName(), codec); + return codec; + } catch (Exception exception) { + exception.printStackTrace(); + return null; + } + } + + public ProtobufCodec create(Class clazz) throws Exception { + String path = Objects.requireNonNull(clazz.getResource(TEMPLATE_FILE)).getPath(); + miniTemplator = new MiniTemplator(path); + + miniTemplator.setVariable("package", "package " + clazz.getPackage().getName() + ";"); + + processImportBlock(); + + miniTemplator.setVariable("className", clazz.getName() + "$$ProxyClass"); + miniTemplator.setVariable("codecClassName", ProtobufCodec.class.getName()); + miniTemplator.setVariable("targetProxyClassName", clazz.getName()); + + + return null; + } + + private void processImportBlock(){ + Set imports = new HashSet<>(); + imports.add("java.util.*"); + imports.add("java.io.IOException"); + imports.add("java.lang.reflect.*"); +// imports.add("com.baidu.bjf.remoting.protobuf.FieldType"); // fix the class ambiguous of FieldType +// imports.add("com.baidu.bjf.remoting.protobuf.code.*"); +// imports.add("com.baidu.bjf.remoting.protobuf.utils.*"); +// imports.add("com.baidu.bjf.remoting.protobuf.*"); + imports.add("com.google.protobuf.*"); + imports.add(clazz.getName()); + for (String pkg : imports) { + miniTemplator.setVariable("importBlock", pkg); + miniTemplator.addBlock("imports"); + } + } + + +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxyFactory.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxyFactory.java deleted file mode 100644 index 93f874e745..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxyFactory.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.taobao.arthas.protobuf;/** - * @author: 風楪 - * @date: 2024/7/17 下午9:57 - */ - - -import com.taobao.arthas.Main; -import com.taobao.arthas.protobuf.utils.MiniTemplator; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author: FengYe - * @date: 2024/7/17 下午9:57 - * @description: ProtoBufProxy - */ -public class ProtobufProxyFactory { - private static final String TEMPLATE_FILE = "/class_template.tpl"; - - private static final Map codecCache = new ConcurrentHashMap(); - - public static ProtobufCodec getCodec(Class clazz) throws IOException { - ProtobufCodec codec = codecCache.get(clazz.getName()); - if (codec != null) { - return codec; - } - return create(clazz); - } - - public static ProtobufCodec create(Class clazz) throws IOException { - String path = Objects.requireNonNull(clazz.getResource(TEMPLATE_FILE)).getPath(); - MiniTemplator miniTemplator = new MiniTemplator(path); - - return null; - } -} diff --git a/arthas-grpc-server/src/main/resources/class_template.tpl b/arthas-grpc-server/src/main/resources/class_template.tpl index f7031aa529..76ea118627 100644 --- a/arthas-grpc-server/src/main/resources/class_template.tpl +++ b/arthas-grpc-server/src/main/resources/class_template.tpl @@ -2,7 +2,7 @@ ${package} import java.io.Serializable; -import ${importPackage}; +import ${importBlock}; public class ${className} implements ${codecClassName}<${targetProxyClassName}>, Serializable { From 0116fcdf978e93870390fd3860035396dcac5167 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Thu, 25 Jul 2024 02:12:14 +0800 Subject: [PATCH 09/48] update: add protobuf codec --- .../taobao/arthas/protobuf/ProtobufClass.java | 12 ---- .../taobao/arthas/protobuf/ProtobufField.java | 22 +++++++ .../taobao/arthas/protobuf/ProtobufProxy.java | 27 ++++++-- .../protobuf/annotation/ProtobufClass.java | 19 ++++++ .../annotation/ProtobufCustomedField.java | 23 +++++++ .../protobuf/annotation/ProtobufIgnore.java | 19 ++++++ .../arthas/protobuf/utils/FieldUtil.java | 61 +++++++++++++++++++ 7 files changed, 166 insertions(+), 17 deletions(-) delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufClass.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufClass.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufCustomedField.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufIgnore.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufClass.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufClass.java deleted file mode 100644 index 1388162f71..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufClass.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.taobao.arthas.protobuf;/** - * @author: 風楪 - * @date: 2024/7/17 下午10:00 - */ - -/** - * @author: FengYe - * @date: 2024/7/17 下午10:00 - * @description: ProtoBufClass - */ -public @interface ProtobufClass { -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java new file mode 100644 index 0000000000..700daf9eaa --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java @@ -0,0 +1,22 @@ +package com.taobao.arthas.protobuf;/** + * @author: 風楪 + * @date: 2024/7/25 上午12:14 + */ + +import com.google.protobuf.WireFormat; + +import java.lang.reflect.Field; + +/** + * @author: FengYe + * @date: 2024/7/25 上午12:14 + * @description: ProtobufField + */ +public class ProtobufField { + + private int order; + + private Field javaField; + + private ProtobufFieldType protobufFieldType; +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java index e343d3a42b..5015128756 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java @@ -5,13 +5,12 @@ import com.baidu.bjf.remoting.protobuf.Codec; +import com.taobao.arthas.protobuf.annotation.ProtobufClass; import com.taobao.arthas.protobuf.utils.MiniTemplator; import java.io.IOException; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.lang.reflect.Field; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** @@ -28,8 +27,12 @@ public class ProtobufProxy { private MiniTemplator miniTemplator; + private List protobufFields; + public ProtobufProxy(Class clazz) { + Objects.requireNonNull(clazz); this.clazz = clazz; + loadProtobufField(); } public ProtobufCodec getCodecCacheSide(Class clazz) { @@ -63,7 +66,7 @@ public ProtobufCodec create(Class clazz) throws Exception { return null; } - private void processImportBlock(){ + private void processImportBlock() { Set imports = new HashSet<>(); imports.add("java.util.*"); imports.add("java.io.IOException"); @@ -80,5 +83,19 @@ private void processImportBlock(){ } } + public void processEncodeBlock() { + + } + + + private void loadProtobufField() { + List fields = new ArrayList<>(); + ProtobufClass annotation = clazz.getAnnotation(ProtobufClass.class); + if (annotation != null) { + + } else { + //todo 添加指定字段映射关系 + } + } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufClass.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufClass.java new file mode 100644 index 0000000000..20e50adc99 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufClass.java @@ -0,0 +1,19 @@ +package com.taobao.arthas.protobuf.annotation;/** + * @author: 風楪 + * @date: 2024/7/25 上午12:19 + */ + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author: FengYe + * @date: 2024/7/25 上午12:19 + * @description: ProtobufClass 用于标识 protobuf class + */ +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +public @interface ProtobufClass { +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufCustomedField.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufCustomedField.java new file mode 100644 index 0000000000..7a50de6336 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufCustomedField.java @@ -0,0 +1,23 @@ +package com.taobao.arthas.protobuf.annotation;/** + * @author: 風楪 + * @date: 2024/7/25 上午12:21 + */ + +import com.baidu.bjf.remoting.protobuf.FieldType; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author: FengYe + * @date: 2024/7/25 上午12:21 + * @description: ProtobufField 用于自定义标识字段;当类上添加 ProtobufClass 时,该注解不生效 + */ +@Target({ ElementType.FIELD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface ProtobufCustomedField { + int order() default 0; + FieldType fieldType() default FieldType.DEFAULT; +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufIgnore.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufIgnore.java new file mode 100644 index 0000000000..4ea603cf86 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufIgnore.java @@ -0,0 +1,19 @@ +package com.taobao.arthas.protobuf.annotation;/** + * @author: 風楪 + * @date: 2024/7/25 上午12:44 + */ + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author: FengYe + * @date: 2024/7/25 上午12:44 + * @description: ProtobufIgnore + */ +@Target({ ElementType.TYPE, ElementType.FIELD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface ProtobufIgnore { +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java new file mode 100644 index 0000000000..c96d00eca1 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java @@ -0,0 +1,61 @@ +package com.taobao.arthas.protobuf.utils;/** + * @author: 風楪 + * @date: 2024/7/25 上午12:33 + */ + +import com.baidu.bjf.remoting.protobuf.annotation.Ignore; +import com.sun.org.apache.bcel.internal.generic.RETURN; +import com.taobao.arthas.protobuf.ProtobufField; +import com.taobao.arthas.protobuf.annotation.ProtobufCustomedField; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +/** + * @author: FengYe + * @date: 2024/7/25 上午12:33 + * @description: FieldUtil + */ +public class FieldUtil { + + public static List getProtobufFieldList(Class clazz, boolean enableCustomedField) { + // 获取所有的 java field + List fields = new ArrayList<>(); + Field[] fieldsArray = clazz.getFields(); + for (Field field : fieldsArray) { + if (enableCustomedField) { + ProtobufCustomedField annotation = field.getAnnotation(ProtobufCustomedField.class); + if (annotation != null) { + fields.add(field); + } + } else { + fields.add(field); + } + } + + // 转化为 protobuf field + List protobufFields = new ArrayList<>(); + for (Field field : fields) { + if (field.getAnnotation(Ignore.class) != null || Modifier.isTransient(field.getModifiers())) { + continue; + } + + String fieldName = field.getName(); + String filedTypeName = field.getType().getName(); + + // protobuf 不支持除字节数组以外任何数组 + if (filedTypeName.startsWith("[")) { + if ((!filedTypeName.equals(byte[].class.getName())) && (!filedTypeName.equals(Byte[].class.getName()))) { + throw new RuntimeException("Array type of field '" + fieldName + "' on class '" + + field.getDeclaringClass().getName() + "' is not support, please use List instead."); + } + } + + + } + //TODO + return null; + } +} From 29eb16f00d2e51ed99ab4ee406e2a7cdddecf6e9 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Mon, 29 Jul 2024 01:42:43 +0800 Subject: [PATCH 10/48] update: add protobuf codec --- .../taobao/arthas/protobuf/ProtobufField.java | 230 +++++++++++++++++- ...ldType.java => ProtobufFieldTypeEnum.java} | 10 +- .../taobao/arthas/protobuf/ProtobufProxy.java | 38 ++- .../protobuf/annotation/EnableZigZap.java | 19 ++ ...ield.java => ProtobufCustomizedField.java} | 8 +- .../arthas/protobuf/utils/FieldUtil.java | 226 +++++++++++++++-- .../service/req/ArthasSampleRequest.java | 94 +------ .../src/main/resources/class_template.tpl | 33 +-- 8 files changed, 514 insertions(+), 144 deletions(-) rename arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/{ProtobufFieldType.java => ProtobufFieldTypeEnum.java} (94%) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/EnableZigZap.java rename arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/{ProtobufCustomedField.java => ProtobufCustomizedField.java} (61%) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java index 700daf9eaa..aeb387cc9d 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java @@ -3,9 +3,13 @@ * @date: 2024/7/25 上午12:14 */ -import com.google.protobuf.WireFormat; - import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * @author: FengYe @@ -14,9 +18,229 @@ */ public class ProtobufField { + /** + * 序号 + */ private int order; + /** + * protobuf 字段类型 + */ + private ProtobufFieldTypeEnum protobufFieldType; + + /** + * java 字段类型 + */ private Field javaField; - private ProtobufFieldType protobufFieldType; + /** + * 是否为 List + */ + private boolean isList; + + /** + * 是否为 Map + */ + private boolean isMap; + + /** + * List & Map key 的泛型类型 + */ + private Class genericKeyType; + + /** + * Map value 的泛型类型 + */ + private Class genericValueType; + + /** + * 是否是通配符类型 + */ + private boolean wildcardType; + + /** + * 处理 List 和 Map 类型字段 + * + * @param field + */ + public void parseListOrMap(Field field) { + Class cls = field.getType(); + boolean needCheckGenericType = false; + if (List.class.isAssignableFrom(cls) || Set.class.isAssignableFrom(cls)) { + isList = true; + needCheckGenericType = true; + } + if (Map.class.isAssignableFrom(cls)) { + isMap = true; + needCheckGenericType = true; + } + + if (!needCheckGenericType) { + return; + } + + Type type = field.getGenericType(); + if (type instanceof ParameterizedType) { + ParameterizedType ptype = (ParameterizedType) type; + + Type[] actualTypeArguments = ptype.getActualTypeArguments(); + + if (actualTypeArguments != null) { + + int length = actualTypeArguments.length; + if (isList) { + if (length != 1) { + throw new RuntimeException( + "List must use generic definiation like List, please check field name '" + + field.getName() + " at class " + field.getDeclaringClass().getName()); + } + } else if (isMap) { + if (length != 2) { + throw new RuntimeException( + "Map must use generic definiation like Map, please check field name '" + + field.getName() + " at class " + field.getDeclaringClass().getName()); + } + } + + Type targetType = actualTypeArguments[0]; + if (targetType instanceof Class) { + genericKeyType = (Class) targetType; + } else if (targetType instanceof ParameterizedType) { + boolean mapKey = false; + if (isMap) { + mapKey = true; + } + throw new RuntimeException(noSubParameterizedType(field, mapKey)); + } else if (WildcardType.class.isAssignableFrom(targetType.getClass())) { + wildcardType = true; + WildcardType wildcardType = (WildcardType) targetType; + + Type[] upperBounds = wildcardType.getUpperBounds(); + if (upperBounds != null && upperBounds.length == 1) { + if (upperBounds[0] instanceof Class) { + genericKeyType = (Class) upperBounds[0]; + } + } + } + + if (actualTypeArguments.length > 1) { + targetType = actualTypeArguments[1]; + if (targetType instanceof Class) { + genericValueType = (Class) targetType; + } else if (targetType instanceof ParameterizedType) { + boolean mapKey = false; + if (isMap) { + mapKey = true; + } + throw new RuntimeException(noSubParameterizedType(field, mapKey)); + } else if (WildcardType.class.isAssignableFrom(targetType.getClass())) { + wildcardType = true; + WildcardType wildcardType = (WildcardType) targetType; + + Type[] upperBounds = wildcardType.getUpperBounds(); + if (upperBounds != null && upperBounds.length == 1) { + if (upperBounds[0] instanceof Class) { + genericValueType = (Class) upperBounds[0]; + } + } + } + } + + } + } + } + + /** + * No sub parameterized type. + * + * @param field the field + * @param listOrMap the list or map + * @return the string + */ + private String noSubParameterizedType(Field field, boolean listOrMap) { + String key = "List"; + if (listOrMap) { + key = "Map"; + } + return key + " can not has sub parameterized type please check field name '" + field.getName() + " at class " + + field.getDeclaringClass().getName(); + + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + public ProtobufFieldTypeEnum getProtobufFieldType() { + return protobufFieldType; + } + + public void setProtobufFieldType(ProtobufFieldTypeEnum protobufFieldType) { + this.protobufFieldType = protobufFieldType; + } + + public boolean isList() { + return isList; + } + + public void setList(boolean list) { + isList = list; + } + + public boolean isMap() { + return isMap; + } + + public void setMap(boolean map) { + isMap = map; + } + + public Class getGenericKeyType() { + return genericKeyType; + } + + public void setGenericKeyType(Class genericKeyType) { + this.genericKeyType = genericKeyType; + } + + public Class getGenericValueType() { + return genericValueType; + } + + public void setGenericValueType(Class genericValueType) { + this.genericValueType = genericValueType; + } + + public Field getJavaField() { + return javaField; + } + + public void setJavaField(Field javaField) { + this.javaField = javaField; + } + + public boolean isWildcardType() { + return wildcardType; + } + + public void setWildcardType(boolean wildcardType) { + this.wildcardType = wildcardType; + } + + @Override + public String toString() { + return "ProtobufField{" + + "order=" + order + + ", protobufFieldType=" + protobufFieldType + + ", isList=" + isList + + ", isMap=" + isMap + + ", genericKeyType=" + genericKeyType + + ", genericValueType=" + genericValueType + + ", wildcardType=" + wildcardType + + '}'; + } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufFieldType.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufFieldTypeEnum.java similarity index 94% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufFieldType.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufFieldTypeEnum.java index a59ad2b814..2cde8ee9f8 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufFieldType.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufFieldTypeEnum.java @@ -10,7 +10,7 @@ * @date: 2024/7/17 下午10:02 * @description: ProtoBufFieldType */ -public enum ProtobufFieldType { +public enum ProtobufFieldTypeEnum { /** * types defined in .proto file. */ @@ -128,7 +128,7 @@ public String getType() { * @return java original type */ public String getJavaType() { - if (this == ProtobufFieldType.ENUM) { + if (this == ProtobufFieldTypeEnum.ENUM) { return Enum.class.getName(); } return javaType; @@ -141,8 +141,8 @@ public String getJavaType() { * @param type protobuf type * @param wireFormat protobuf wire format type */ - ProtobufFieldType(String javaType, String type, String wireFormat, - String toPrimitiveType, WireFormat.FieldType internalFieldType, String defaultValue) { + ProtobufFieldTypeEnum(String javaType, String type, String wireFormat, + String toPrimitiveType, WireFormat.FieldType internalFieldType, String defaultValue) { this.javaType = javaType; this.type = type; this.wireFormat = wireFormat; @@ -161,7 +161,7 @@ public boolean isPrimitive() { || this == FIXED64 || this == FLOAT || this == INT64 || this == SFIXED32 || this == SFIXED64 || this == SINT32 - || this == SINT64 || this == BOOL || this == ProtobufFieldType.UINT32 || this == ProtobufFieldType.UINT64) { + || this == SINT64 || this == BOOL || this == ProtobufFieldTypeEnum.UINT32 || this == ProtobufFieldTypeEnum.UINT64) { return true; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java index 5015128756..b5b85e6abd 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java @@ -5,8 +5,11 @@ import com.baidu.bjf.remoting.protobuf.Codec; +import com.taobao.arthas.protobuf.annotation.EnableZigZap; import com.taobao.arthas.protobuf.annotation.ProtobufClass; +import com.taobao.arthas.protobuf.utils.FieldUtil; import com.taobao.arthas.protobuf.utils.MiniTemplator; +import com.taobao.arthas.service.req.ArthasSampleRequest; import java.io.IOException; import java.lang.reflect.Field; @@ -31,11 +34,14 @@ public class ProtobufProxy { public ProtobufProxy(Class clazz) { Objects.requireNonNull(clazz); + if (clazz.getAnnotation(ProtobufClass.class) == null) { + throw new IllegalArgumentException("class is not annotated with @ProtobufClass"); + } this.clazz = clazz; loadProtobufField(); } - public ProtobufCodec getCodecCacheSide(Class clazz) { + public ProtobufCodec getCodecCacheSide() { ProtobufCodec codec = codecCache.get(clazz.getName()); if (codec != null) { return codec; @@ -50,7 +56,7 @@ public ProtobufCodec getCodecCacheSide(Class clazz) { } } - public ProtobufCodec create(Class clazz) throws Exception { + private ProtobufCodec create(Class clazz) throws Exception { String path = Objects.requireNonNull(clazz.getResource(TEMPLATE_FILE)).getPath(); miniTemplator = new MiniTemplator(path); @@ -84,17 +90,33 @@ private void processImportBlock() { } public void processEncodeBlock() { - + for (ProtobufField protobufField : protobufFields) { + boolean isList = protobufField.isList(); + boolean isMap = protobufField.isMap(); + + ProtobufFieldTypeEnum protobufFieldType = protobufField.getProtobufFieldType(); + String encodeFieldGetter = FieldUtil.getAccessMethod("target", protobufField.getJavaField(), clazz, protobufField.isWildcardType()); + String encodeFileType = isList ? "Collection" : protobufField.getProtobufFieldType().getJavaType(); + String encodeFieldName = "f_" + protobufField.getOrder(); + + miniTemplator.setVariable("encodeFileType",encodeFileType); + miniTemplator.setVariable("encodeFieldName",encodeFieldName); + miniTemplator.setVariable("encodeFieldGetter",encodeFieldGetter); + } } private void loadProtobufField() { - List fields = new ArrayList<>(); - ProtobufClass annotation = clazz.getAnnotation(ProtobufClass.class); - if (annotation != null) { + protobufFields = FieldUtil.getProtobufFieldList(clazz, + clazz.getAnnotation(EnableZigZap.class) != null + ); + } - } else { - //todo 添加指定字段映射关系 + public static void main(String[] args) { + List protobufFieldList = FieldUtil.getProtobufFieldList(ArthasSampleRequest.class, false); + for (ProtobufField protobufField : protobufFieldList) { + String target = FieldUtil.getAccessMethod("target", protobufField.getJavaField(), ArthasSampleRequest.class, protobufField.isWildcardType()); + System.out.println(target); } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/EnableZigZap.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/EnableZigZap.java new file mode 100644 index 0000000000..b9d3ac83b1 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/EnableZigZap.java @@ -0,0 +1,19 @@ +package com.taobao.arthas.protobuf.annotation;/** + * @author: 風楪 + * @date: 2024/7/28 下午7:27 + */ + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author: FengYe + * @date: 2024/7/28 下午7:27 + * @description: EnableZigZap 是否启用 zigzap 编码 + */ +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +public @interface EnableZigZap { +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufCustomedField.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufCustomizedField.java similarity index 61% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufCustomedField.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufCustomizedField.java index 7a50de6336..ec3124299a 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufCustomedField.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufCustomizedField.java @@ -3,7 +3,7 @@ * @date: 2024/7/25 上午12:21 */ -import com.baidu.bjf.remoting.protobuf.FieldType; +import com.taobao.arthas.protobuf.ProtobufFieldTypeEnum; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -13,11 +13,11 @@ /** * @author: FengYe * @date: 2024/7/25 上午12:21 - * @description: ProtobufField 用于自定义标识字段;当类上添加 ProtobufClass 时,该注解不生效 + * @description: ProtobufField 用于自定义标识字段 */ @Target({ ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) -public @interface ProtobufCustomedField { +public @interface ProtobufCustomizedField { int order() default 0; - FieldType fieldType() default FieldType.DEFAULT; + ProtobufFieldTypeEnum protoBufFieldType() default ProtobufFieldTypeEnum.DEFAULT; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java index c96d00eca1..6f7acc27bb 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java @@ -4,14 +4,22 @@ */ import com.baidu.bjf.remoting.protobuf.annotation.Ignore; -import com.sun.org.apache.bcel.internal.generic.RETURN; +import com.baidu.bjf.remoting.protobuf.code.CodedConstant; +import com.baidu.bjf.remoting.protobuf.utils.ClassHelper; +import com.baidu.bjf.remoting.protobuf.utils.FieldInfo; +import com.baidu.bjf.remoting.protobuf.utils.FieldUtils; import com.taobao.arthas.protobuf.ProtobufField; -import com.taobao.arthas.protobuf.annotation.ProtobufCustomedField; +import com.taobao.arthas.protobuf.ProtobufFieldTypeEnum; +import com.taobao.arthas.protobuf.annotation.ProtobufCustomizedField; +import com.taobao.arthas.protobuf.annotation.ProtobufIgnore; +import com.taobao.arthas.service.req.ArthasSampleRequest; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.*; +import java.util.logging.Level; /** * @author: FengYe @@ -20,31 +28,71 @@ */ public class FieldUtil { - public static List getProtobufFieldList(Class clazz, boolean enableCustomedField) { + public static final String PACKAGE_SEPARATOR = "."; + + public static final Map, ProtobufFieldTypeEnum> TYPE_MAPPER; + + static { + TYPE_MAPPER = new HashMap, ProtobufFieldTypeEnum>(); + + TYPE_MAPPER.put(int.class, ProtobufFieldTypeEnum.INT32); + TYPE_MAPPER.put(Integer.class, ProtobufFieldTypeEnum.INT32); + TYPE_MAPPER.put(short.class, ProtobufFieldTypeEnum.INT32); + TYPE_MAPPER.put(Short.class, ProtobufFieldTypeEnum.INT32); + TYPE_MAPPER.put(Byte.class, ProtobufFieldTypeEnum.INT32); + TYPE_MAPPER.put(byte.class, ProtobufFieldTypeEnum.INT32); + TYPE_MAPPER.put(long.class, ProtobufFieldTypeEnum.INT64); + TYPE_MAPPER.put(Long.class, ProtobufFieldTypeEnum.INT64); + TYPE_MAPPER.put(String.class, ProtobufFieldTypeEnum.STRING); + TYPE_MAPPER.put(byte[].class, ProtobufFieldTypeEnum.BYTES); + TYPE_MAPPER.put(Byte[].class, ProtobufFieldTypeEnum.BYTES); + TYPE_MAPPER.put(Float.class, ProtobufFieldTypeEnum.FLOAT); + TYPE_MAPPER.put(float.class, ProtobufFieldTypeEnum.FLOAT); + TYPE_MAPPER.put(double.class, ProtobufFieldTypeEnum.DOUBLE); + TYPE_MAPPER.put(Double.class, ProtobufFieldTypeEnum.DOUBLE); + TYPE_MAPPER.put(Boolean.class, ProtobufFieldTypeEnum.BOOL); + TYPE_MAPPER.put(boolean.class, ProtobufFieldTypeEnum.BOOL); + TYPE_MAPPER.put(Date.class, ProtobufFieldTypeEnum.DATE); + TYPE_MAPPER.put(BigDecimal.class, ProtobufFieldTypeEnum.BIGDECIMAL); + TYPE_MAPPER.put(BigInteger.class, ProtobufFieldTypeEnum.BIGINTEGER); + } + + + /** + * 将指定类的所有 java 字段转化为 protobuf 字段 + * 字段编号逻辑:优先处理自定义的字段,其余字段在最大的自定义字段基础上递增 + * + * @param clazz + * @param enableZigZap + * @return + */ + public static List getProtobufFieldList(Class clazz, boolean enableZigZap) { // 获取所有的 java field List fields = new ArrayList<>(); - Field[] fieldsArray = clazz.getFields(); + Field[] fieldsArray = clazz.getDeclaredFields(); for (Field field : fieldsArray) { - if (enableCustomedField) { - ProtobufCustomedField annotation = field.getAnnotation(ProtobufCustomedField.class); - if (annotation != null) { - fields.add(field); - } - } else { + if (field.getAnnotation(ProtobufIgnore.class) == null) { fields.add(field); } } // 转化为 protobuf field - List protobufFields = new ArrayList<>(); + List res = new ArrayList<>(); + List unOrderFields = new ArrayList<>(); + Set orders = new HashSet<>(); + int maxOrder = 0; + for (Field field : fields) { + Class fieldType = field.getType(); + String fieldName = field.getName(); + String filedTypeName = fieldType.getName(); + ProtobufCustomizedField customizedField = field.getAnnotation(ProtobufCustomizedField.class); + int order = 0; + if (field.getAnnotation(Ignore.class) != null || Modifier.isTransient(field.getModifiers())) { continue; } - String fieldName = field.getName(); - String filedTypeName = field.getType().getName(); - // protobuf 不支持除字节数组以外任何数组 if (filedTypeName.startsWith("[")) { if ((!filedTypeName.equals(byte[].class.getName())) && (!filedTypeName.equals(Byte[].class.getName()))) { @@ -53,9 +101,153 @@ public static List getProtobufFieldList(Class clazz, boolean e } } + ProtobufField protobufField = new ProtobufField(); + protobufField.parseListOrMap(field); + protobufField.setJavaField(field); + + ProtobufFieldTypeEnum protobufFieldType = ProtobufFieldTypeEnum.DEFAULT; + if (customizedField != null) { + order = customizedField.order(); + protobufFieldType = customizedField.protoBufFieldType(); + } + + // 如果不是自定义字段,则通过 javaType 和 protobufType 映射关系来决定 + if (protobufFieldType == ProtobufFieldTypeEnum.DEFAULT) { + if (protobufField.isList()) { + fieldType = protobufField.getGenericKeyType(); + } + if (fieldType == null) { + fieldType = Object.class; + } + + protobufFieldType = TYPE_MAPPER.get(fieldType); + if (protobufFieldType == null) { + if (Enum.class.isAssignableFrom(fieldType)) { + protobufFieldType = ProtobufFieldTypeEnum.ENUM; + } else if (protobufField.isMap()) { + protobufFieldType = ProtobufFieldTypeEnum.MAP; + } else { + protobufFieldType = ProtobufFieldTypeEnum.OBJECT; + } + } + + // 处理 zigzap 编码 + if (enableZigZap) { + if (protobufFieldType == ProtobufFieldTypeEnum.INT32) { + protobufFieldType = ProtobufFieldTypeEnum.SINT32; // to convert to sint32 to enable zagzip + } else if (protobufFieldType == ProtobufFieldTypeEnum.INT64) { + protobufFieldType = ProtobufFieldTypeEnum.SINT64; // to convert to sint64 to enable zagzip + } + } + } + protobufField.setProtobufFieldType(protobufFieldType); + + // 如果是自定义字段,则取自定义值中的order,否则记录到未排序的 list 中,等待后续处理 + if (order > 0) { + if (orders.contains(order)) { + throw new RuntimeException( + "order id '" + order + "' from field name '" + fieldName + "' is duplicate"); + } + orders.add(order); + protobufField.setOrder(order); + maxOrder = Math.max(maxOrder, order); + } else { + unOrderFields.add(protobufField); + } + + res.add(protobufField); + } + + if (unOrderFields.isEmpty()) { + return res; + } + + for (ProtobufField protobufField : unOrderFields) { + protobufField.setOrder(++maxOrder); + } + + return res; + } + + public static Field findField(Class clazz, String name, Class type) { + if (clazz == null) { + throw new IllegalArgumentException("Class must not be null"); + } + if (name == null && type == null) { + throw new IllegalArgumentException( + "Either name or type of the field must be specified"); + } + Class searchType = clazz; + while (!Object.class.equals(searchType) && searchType != null) { + Field[] fields = searchType.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + if ((name == null || name.equals(field.getName())) + && (type == null || type.equals(field.getType()))) { + return field; + } + } + searchType = searchType.getSuperclass(); + } + return null; + } + + public static Object getField(Object t, String name) { + Field field = findField(t.getClass(), name, null); + if (field == null) { + return null; + } + field.setAccessible(true); + try { + return field.get(t); + } catch (Exception e) { + //todo log + } + return null; + } + + /** + * 获取目标的访问方法字符串,如果目标已经声明 getter 则返回 getter,否则使用 FieldUtil.getField + * + * @param target + * @param field + * @param clazz + * @param wildcardType + * @return + */ + public static String getAccessMethod(String target, Field field, Class clazz, boolean wildcardType) { + if (field.getModifiers() == Modifier.PUBLIC && !wildcardType) { + return target + PACKAGE_SEPARATOR + field.getName(); + } + + String getter; + if ("boolean".equalsIgnoreCase(field.getType().getCanonicalName())) { + getter = "is" + CodedConstant.capitalize(field.getName()); + } else { + getter = "get" + CodedConstant.capitalize(field.getName()); + } + + try { + clazz.getMethod(getter, new Class[0]); + return target + PACKAGE_SEPARATOR + getter + "()"; + } catch (Exception e) { + //todo log + } + String type = field.getType().getCanonicalName(); + if ("[B".equals(type) || "[Ljava.lang.Byte;".equals(type) || "java.lang.Byte[]".equals(type)) { + type = "byte[]"; } - //TODO + + // use reflection to get value + String code = "(" + FieldUtils.toObjectType(type) + ") "; + code += "FieldUtils.getField(" + target + ", \"" + field.getName() + "\")"; + + return code; + } + + public static String getMappedTypeSize(){ + //todo return null; } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java index 5f2eb61b51..d818e7ac10 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java @@ -3,11 +3,16 @@ * @date: 2024/7/14 上午4:28 */ +import com.baidu.bjf.remoting.protobuf.annotation.Protobuf; import com.baidu.bjf.remoting.protobuf.annotation.ProtobufClass; import com.google.protobuf.*; +import com.taobao.arthas.protobuf.annotation.ProtobufCustomizedField; +import com.taobao.arthas.protobuf.annotation.ProtobufIgnore; import java.io.IOException; import java.nio.ByteBuffer; +import java.util.List; +import java.util.Map; /** * @author: FengYe @@ -16,91 +21,8 @@ */ @ProtobufClass public class ArthasSampleRequest{ - @Override - public String toString() { - return "ArthasSampleRequest{" + -// "name='" + name + '\'' + - ", age=" + age + - ", price=" + price + - ", man=" + man + - '}'; - } -// -// public String getName() { -// return name; -// } -// -// public void setName(String name) { -// this.name = name; -// } - public Double getAge() { - return age; - } - - public void setAge(Double age) { - this.age = age; - } - - public Long getPrice() { - return price; - } - - public void setPrice(Long price) { - this.price = price; - } - - public Float getMan() { - return man; - } - - public void setMan(Float man) { - this.man = man; - } - -// private String name; - private Double age; - private Long price; - private Float man; - -// public ArthasSampleRequest(ByteBuffer byteBuffer){ -// CodedInputStream codedInputStream = CodedInputStream.newInstance(byteBuffer); -// try { -// // 读取标签 -// int tag; -// while ((tag = codedInputStream.readTag()) != 0) { -// int fieldNumber = WireFormat.getTagFieldNumber(tag); -// int wireType = WireFormat.getTagWireType(tag); -// -// System.out.println("Field Number: " + fieldNumber); -// System.out.println("Wire Type: " + wireType); -// -// -// // 根据字段编号和类型读取对应的数据 -// switch (wireType) { -// case WireFormat.WIRETYPE_VARINT: -// long varintValue = codedInputStream.readInt64(); -// System.out.println("Varint Value: " + varintValue); -// break; -// case WireFormat.WIRETYPE_FIXED32: -// int fixed32Value = codedInputStream.readFixed32(); -// System.out.println("Fixed32 Value: " + fixed32Value); -// break; -// case WireFormat.WIRETYPE_FIXED64: -// long fixed64Value = codedInputStream.readFixed64(); -// System.out.println("Fixed64 Value: " + fixed64Value); -// break; -// case WireFormat.WIRETYPE_LENGTH_DELIMITED: -// int length = codedInputStream.readRawVarint32(); -// byte[] bytes = codedInputStream.readRawBytes(length); -// System.out.println("Length-delimited Value: " + new String(bytes)); -// break; -// default: -// throw new IOException("Unsupported wire type: " + wireType); -// } -// } -// } catch (IOException e) { -// e.printStackTrace(); -// } -// } + private String name; + private double age; + private long price; } diff --git a/arthas-grpc-server/src/main/resources/class_template.tpl b/arthas-grpc-server/src/main/resources/class_template.tpl index 76ea118627..0ef374c117 100644 --- a/arthas-grpc-server/src/main/resources/class_template.tpl +++ b/arthas-grpc-server/src/main/resources/class_template.tpl @@ -9,9 +9,17 @@ public class ${className} implements ${codecClassName}<${targetProxyClassName}>, public static final long serialVersionUID = 1L; private ${descriptorClsName} descriptor; - public byte[] encode(${targetProxyClassName} t) throws IOException { + public byte[] encode(${targetProxyClassName} target) throws IOException { CodecOutputByteArray output = CodecOutputByteArray.get(); - doWriteTo(t, output.getCodedOutputStream()); + + + ${encodeFieldType} ${encodeFieldName} = null; + if (!CodedConstant.isNull(${encodeFieldGetter})) { + ${encodeFieldName} = ${writeValueToField}; + ${encodeWriteFieldValue} + } + + return output.getData(); } @@ -20,35 +28,18 @@ public class ${className} implements ${codecClassName}<${targetProxyClassName}>, return readFrom(input); } - public int size(${targetProxyClassName} t) throws IOException { + public int size(${targetProxyClassName} target) throws IOException { int size = 0; ${encodeFieldType} ${encodeFieldName} = null; if (!CodedConstant.isNull(${encodeFieldGetter})) { - ${encodeFieldName} = ${writeValueToField}; + ${encodeFieldName} = ${encodeFieldGetter}; size += ${calcSize} } - ${checkNull} return size; } - public void doWriteTo(${targetProxyClassName} t, CodedOutputStream output) - throws IOException { - - ${encodeFieldType} ${encodeFieldName} = null; - if (!CodedConstant.isNull(${encodeFieldGetter})) { - ${encodeFieldName} = ${writeValueToField}; - ${encodeWriteFieldValue} - } - - } - - public void writeTo(${targetProxyClassName} t, CodedOutputStream output) - throws IOException { - doWriteTo(t, output); - } - public ${targetProxyClassName} readFrom(CodedInputStream input) throws IOException { ${targetProxyClassName} ret = new ${targetProxyClassName}(); From faff33fa54a24c0dd07dfb21158cc4fa50b5897b Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Tue, 30 Jul 2024 02:16:33 +0800 Subject: [PATCH 11/48] update: add protobuf codec --- .../taobao/arthas/protobuf/ProtobufCodec.java | 4 + .../taobao/arthas/protobuf/ProtobufField.java | 10 ++ .../taobao/arthas/protobuf/ProtobufProxy.java | 37 +++--- ...eZigZap.java => ProtobufEnableZigZap.java} | 2 +- .../protobuf/annotation/ProtobufPacked.java | 20 +++ .../arthas/protobuf/utils/FieldUtil.java | 123 ++++++++++++++++-- 6 files changed, 168 insertions(+), 28 deletions(-) rename arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/{EnableZigZap.java => ProtobufEnableZigZap.java} (91%) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufPacked.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufCodec.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufCodec.java index 3f4ce336a2..38648013da 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufCodec.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufCodec.java @@ -3,6 +3,8 @@ * @date: 2024/7/17 下午9:44 */ +import java.io.IOException; + /** * @author: FengYe * @date: 2024/7/17 下午9:44 @@ -12,4 +14,6 @@ public interface ProtobufCodec { byte[] encode(T t); T decode(byte[] bytes); + + int size(T t) throws IOException; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java index aeb387cc9d..34dcedf536 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java @@ -58,6 +58,8 @@ public class ProtobufField { */ private boolean wildcardType; + private boolean packed; + /** * 处理 List 和 Map 类型字段 * @@ -231,6 +233,14 @@ public void setWildcardType(boolean wildcardType) { this.wildcardType = wildcardType; } + public boolean isPacked() { + return packed; + } + + public void setPacked(boolean packed) { + this.packed = packed; + } + @Override public String toString() { return "ProtobufField{" + diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java index b5b85e6abd..dc7885a5b1 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java @@ -4,15 +4,12 @@ */ -import com.baidu.bjf.remoting.protobuf.Codec; -import com.taobao.arthas.protobuf.annotation.EnableZigZap; +import com.taobao.arthas.protobuf.annotation.ProtobufEnableZigZap; import com.taobao.arthas.protobuf.annotation.ProtobufClass; import com.taobao.arthas.protobuf.utils.FieldUtil; import com.taobao.arthas.protobuf.utils.MiniTemplator; import com.taobao.arthas.service.req.ArthasSampleRequest; -import java.io.IOException; -import java.lang.reflect.Field; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -26,22 +23,17 @@ public class ProtobufProxy { private static final Map codecCache = new ConcurrentHashMap(); - private Class clazz; + private static Class clazz; - private MiniTemplator miniTemplator; + private static MiniTemplator miniTemplator; - private List protobufFields; + private static List protobufFields; public ProtobufProxy(Class clazz) { - Objects.requireNonNull(clazz); - if (clazz.getAnnotation(ProtobufClass.class) == null) { - throw new IllegalArgumentException("class is not annotated with @ProtobufClass"); - } - this.clazz = clazz; - loadProtobufField(); + } - public ProtobufCodec getCodecCacheSide() { + public ProtobufCodec getCodecCacheSide(Class clazz) { ProtobufCodec codec = codecCache.get(clazz.getName()); if (codec != null) { return codec; @@ -56,7 +48,14 @@ public ProtobufCodec getCodecCacheSide() { } } - private ProtobufCodec create(Class clazz) throws Exception { + public static ProtobufCodec create(Class clazz) throws Exception { + Objects.requireNonNull(clazz); + if (clazz.getAnnotation(ProtobufClass.class) == null) { + throw new IllegalArgumentException("class is not annotated with @ProtobufClass"); + } + ProtobufProxy.clazz = clazz; + loadProtobufField(); + String path = Objects.requireNonNull(clazz.getResource(TEMPLATE_FILE)).getPath(); miniTemplator = new MiniTemplator(path); @@ -72,7 +71,7 @@ private ProtobufCodec create(Class clazz) throws Exception { return null; } - private void processImportBlock() { + private static void processImportBlock() { Set imports = new HashSet<>(); imports.add("java.util.*"); imports.add("java.io.IOException"); @@ -89,7 +88,7 @@ private void processImportBlock() { } } - public void processEncodeBlock() { + private static void processEncodeBlock() { for (ProtobufField protobufField : protobufFields) { boolean isList = protobufField.isList(); boolean isMap = protobufField.isMap(); @@ -106,9 +105,9 @@ public void processEncodeBlock() { } - private void loadProtobufField() { + private static void loadProtobufField() { protobufFields = FieldUtil.getProtobufFieldList(clazz, - clazz.getAnnotation(EnableZigZap.class) != null + clazz.getAnnotation(ProtobufEnableZigZap.class) != null ); } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/EnableZigZap.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufEnableZigZap.java similarity index 91% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/EnableZigZap.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufEnableZigZap.java index b9d3ac83b1..c6566dccbd 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/EnableZigZap.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufEnableZigZap.java @@ -15,5 +15,5 @@ */ @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) -public @interface EnableZigZap { +public @interface ProtobufEnableZigZap { } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufPacked.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufPacked.java new file mode 100644 index 0000000000..65a7ba270e --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufPacked.java @@ -0,0 +1,20 @@ +package com.taobao.arthas.protobuf.annotation;/** + * @author: 風楪 + * @date: 2024/7/30 上午2:01 + */ + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author: FengYe + * @date: 2024/7/30 上午2:01 + * @description: Packed 是否将 List 打包。对于基础类型的 List,打包可以减少无意义的 tag,提高压缩率 + * + */ +@Target({ ElementType.FIELD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface ProtobufPacked { +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java index 6f7acc27bb..ee2dbed203 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java @@ -3,23 +3,29 @@ * @date: 2024/7/25 上午12:33 */ + +import com.baidu.bjf.remoting.protobuf.EnumReadable; import com.baidu.bjf.remoting.protobuf.annotation.Ignore; import com.baidu.bjf.remoting.protobuf.code.CodedConstant; -import com.baidu.bjf.remoting.protobuf.utils.ClassHelper; -import com.baidu.bjf.remoting.protobuf.utils.FieldInfo; import com.baidu.bjf.remoting.protobuf.utils.FieldUtils; +import com.google.protobuf.ByteString; +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.MapEntry; +import com.google.protobuf.WireFormat; +import com.taobao.arthas.protobuf.ProtobufCodec; import com.taobao.arthas.protobuf.ProtobufField; import com.taobao.arthas.protobuf.ProtobufFieldTypeEnum; +import com.taobao.arthas.protobuf.ProtobufProxy; +import com.taobao.arthas.protobuf.annotation.ProtobufPacked; import com.taobao.arthas.protobuf.annotation.ProtobufCustomizedField; import com.taobao.arthas.protobuf.annotation.ProtobufIgnore; -import com.taobao.arthas.service.req.ArthasSampleRequest; + import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.BigInteger; import java.util.*; -import java.util.logging.Level; /** * @author: FengYe @@ -89,7 +95,7 @@ public static List getProtobufFieldList(Class clazz, boolean e ProtobufCustomizedField customizedField = field.getAnnotation(ProtobufCustomizedField.class); int order = 0; - if (field.getAnnotation(Ignore.class) != null || Modifier.isTransient(field.getModifiers())) { + if (field.getAnnotation(ProtobufIgnore.class) != null || Modifier.isTransient(field.getModifiers())) { continue; } @@ -156,6 +162,11 @@ public static List getProtobufFieldList(Class clazz, boolean e } res.add(protobufField); + + // 如果使用 packed 注解则打包 + if (protobufField.isList() && (protobufField.getProtobufFieldType().isPrimitive() || protobufField.getProtobufFieldType().isEnum())) { + protobufField.setPacked(field.getAnnotation(ProtobufPacked.class) != null); + } } if (unOrderFields.isEmpty()) { @@ -241,13 +252,109 @@ public static String getAccessMethod(String target, Field field, Class clazz, // use reflection to get value String code = "(" + FieldUtils.toObjectType(type) + ") "; - code += "FieldUtils.getField(" + target + ", \"" + field.getName() + "\")"; + code += "FieldUtil.getField(" + target + ", \"" + field.getName() + "\")"; return code; } - public static String getMappedTypeSize(){ - //todo + public static String getMappedTypeSize(ProtobufField field) { + ProtobufFieldTypeEnum protobufFieldType = field.getProtobufFieldType(); + int order = field.getOrder(); + boolean isList = field.isList(); + boolean isMap = field.isMap(); + boolean packed = field.isPacked(); + String type = protobufFieldType.getType().toUpperCase(); + + if (isList) { + //todo + } + return null; } + + public static int getListSize(int order, Collection list, ProtobufFieldTypeEnum type, boolean packed) { + int size = 0; + if (list == null || list.isEmpty()) { + return size; + } + + int dataSize = 0; + for (Object object : list) { + dataSize += getObjectSize(order, object, type); + } + size += dataSize; + if (type != ProtobufFieldTypeEnum.OBJECT) { + if (packed) { + size += com.google.protobuf.CodedOutputStream.computeInt32SizeNoTag(dataSize); + int tag = CodedConstant.makeTag(order, WireFormat.WIRETYPE_LENGTH_DELIMITED); + size += com.google.protobuf.CodedOutputStream.computeUInt32SizeNoTag(tag); + } else { + size += list.size() * CodedOutputStream.computeTagSize(order); + } + } + return size; + } + + public static int getMapSize(int order, Map map,com.google.protobuf.WireFormat.FieldType keyType, + K defaultKey, com.google.protobuf.WireFormat.FieldType valueType, V defalutValue) { + int size = 0; + for (java.util.Map.Entry entry : map.entrySet()) { + MapEntry valuesDefaultEntry = MapEntry + . newDefaultInstance(null, keyType, defaultKey, valueType, defalutValue); + + MapEntry values = + valuesDefaultEntry.newBuilderForType().setKey(entry.getKey()).setValue(entry.getValue()).build(); + + size += com.google.protobuf.CodedOutputStream.computeMessageSize(order, values); + } + return size; + } + + public static int getObjectSize(int order, Object object, ProtobufFieldTypeEnum type) { + int size = 0; + if (object == null) { + return size; + } + + if (type == ProtobufFieldTypeEnum.OBJECT) { + try { + Class cls = object.getClass(); + ProtobufCodec target = ProtobufProxy.create(cls); + size = target.size(object); + size = size + CodedOutputStream.computeRawVarint32Size(size); + return size + CodedOutputStream.computeTagSize(order); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + if (type == ProtobufFieldTypeEnum.STRING) { + size = CodedOutputStream.computeStringSizeNoTag(String.valueOf(object)); + } else if (type == ProtobufFieldTypeEnum.BOOL) { + size = CodedOutputStream.computeBoolSizeNoTag(Boolean.valueOf(String.valueOf(object))); + } else if (type == ProtobufFieldTypeEnum.BYTES) { + byte[] bb = (byte[]) object; + size = CodedOutputStream.computeBytesSizeNoTag(ByteString.copyFrom(bb)); + } else if (type == ProtobufFieldTypeEnum.DOUBLE) { + size = CodedOutputStream.computeDoubleSizeNoTag(Double.valueOf(object.toString())); + } else if (type == ProtobufFieldTypeEnum.FIXED32 || type == ProtobufFieldTypeEnum.SFIXED32) { + size = CodedOutputStream.computeFixed32SizeNoTag(Integer.valueOf(object.toString())); + } else if (type == ProtobufFieldTypeEnum.INT32 || type == ProtobufFieldTypeEnum.SINT32 || type == ProtobufFieldTypeEnum.UINT32) { + size = CodedOutputStream.computeInt32SizeNoTag(Integer.valueOf(object.toString())); + } else if (type == ProtobufFieldTypeEnum.FIXED64 || type == ProtobufFieldTypeEnum.SFIXED64) { + size = CodedOutputStream.computeSFixed64SizeNoTag(Long.valueOf(object.toString())); + } else if (type == ProtobufFieldTypeEnum.INT64 || type == ProtobufFieldTypeEnum.SINT64 || type == ProtobufFieldTypeEnum.UINT64) { + size = CodedOutputStream.computeInt64SizeNoTag(Long.valueOf(object.toString())); + } else if (type == ProtobufFieldTypeEnum.FLOAT) { + size = CodedOutputStream.computeFloatSizeNoTag(Float.valueOf(object.toString())); + } else if (type == ProtobufFieldTypeEnum.ENUM) { + if (object instanceof EnumReadable) { + size = CodedOutputStream.computeInt32SizeNoTag(((EnumReadable) object).value()); + } else if (object instanceof Enum) { + size = CodedOutputStream.computeInt32SizeNoTag(((Enum) object).ordinal()); + } + } + + return size; + } } From 900c89f6c689a081ded0c3358095108b4b9da4d1 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Thu, 1 Aug 2024 00:51:16 +0800 Subject: [PATCH 12/48] update: add protobuf codec --- .../taobao/arthas/protobuf/ProtobufProxy.java | 6 +- .../arthas/protobuf/utils/FieldUtil.java | 202 ++++++++++++++++-- 2 files changed, 184 insertions(+), 24 deletions(-) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java index dc7885a5b1..58a221c64c 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java @@ -94,9 +94,9 @@ private static void processEncodeBlock() { boolean isMap = protobufField.isMap(); ProtobufFieldTypeEnum protobufFieldType = protobufField.getProtobufFieldType(); - String encodeFieldGetter = FieldUtil.getAccessMethod("target", protobufField.getJavaField(), clazz, protobufField.isWildcardType()); + String encodeFieldGetter = FieldUtil.getGetterDynamicString("target", protobufField.getJavaField(), clazz, protobufField.isWildcardType()); String encodeFileType = isList ? "Collection" : protobufField.getProtobufFieldType().getJavaType(); - String encodeFieldName = "f_" + protobufField.getOrder(); + String encodeFieldName = FieldUtil.getDynamicFieldName(protobufField.getOrder()); miniTemplator.setVariable("encodeFileType",encodeFileType); miniTemplator.setVariable("encodeFieldName",encodeFieldName); @@ -114,7 +114,7 @@ private static void loadProtobufField() { public static void main(String[] args) { List protobufFieldList = FieldUtil.getProtobufFieldList(ArthasSampleRequest.class, false); for (ProtobufField protobufField : protobufFieldList) { - String target = FieldUtil.getAccessMethod("target", protobufField.getJavaField(), ArthasSampleRequest.class, protobufField.isWildcardType()); + String target = FieldUtil.getGetterDynamicString("target", protobufField.getJavaField(), ArthasSampleRequest.class, protobufField.isWildcardType()); System.out.println(target); } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java index ee2dbed203..90a52adde6 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java @@ -5,9 +5,9 @@ import com.baidu.bjf.remoting.protobuf.EnumReadable; -import com.baidu.bjf.remoting.protobuf.annotation.Ignore; -import com.baidu.bjf.remoting.protobuf.code.CodedConstant; -import com.baidu.bjf.remoting.protobuf.utils.FieldUtils; +import com.baidu.bjf.remoting.protobuf.FieldType; +import com.baidu.bjf.remoting.protobuf.code.ICodeGenerator; +import com.baidu.bjf.remoting.protobuf.utils.ClassHelper; import com.google.protobuf.ByteString; import com.google.protobuf.CodedOutputStream; import com.google.protobuf.MapEntry; @@ -38,6 +38,22 @@ public class FieldUtil { public static final Map, ProtobufFieldTypeEnum> TYPE_MAPPER; + private static final Map PRIMITIVE_TYPE_MAPPING; + + static { + + PRIMITIVE_TYPE_MAPPING = new HashMap(); + + PRIMITIVE_TYPE_MAPPING.put(int.class.getSimpleName(), Integer.class.getSimpleName()); + PRIMITIVE_TYPE_MAPPING.put(long.class.getSimpleName(), Long.class.getSimpleName()); + PRIMITIVE_TYPE_MAPPING.put(short.class.getSimpleName(), Short.class.getSimpleName()); + PRIMITIVE_TYPE_MAPPING.put(boolean.class.getSimpleName(), Boolean.class.getSimpleName()); + PRIMITIVE_TYPE_MAPPING.put(double.class.getSimpleName(), Double.class.getSimpleName()); + PRIMITIVE_TYPE_MAPPING.put(float.class.getSimpleName(), Float.class.getSimpleName()); + PRIMITIVE_TYPE_MAPPING.put(char.class.getSimpleName(), Character.class.getSimpleName()); + PRIMITIVE_TYPE_MAPPING.put(byte.class.getSimpleName(), Byte.class.getSimpleName()); + } + static { TYPE_MAPPER = new HashMap, ProtobufFieldTypeEnum>(); @@ -226,16 +242,16 @@ public static Object getField(Object t, String name) { * @param wildcardType * @return */ - public static String getAccessMethod(String target, Field field, Class clazz, boolean wildcardType) { + public static String getGetterDynamicString(String target, Field field, Class clazz, boolean wildcardType) { if (field.getModifiers() == Modifier.PUBLIC && !wildcardType) { return target + PACKAGE_SEPARATOR + field.getName(); } String getter; if ("boolean".equalsIgnoreCase(field.getType().getCanonicalName())) { - getter = "is" + CodedConstant.capitalize(field.getName()); + getter = "is" + capitalize(field.getName()); } else { - getter = "get" + CodedConstant.capitalize(field.getName()); + getter = "get" + capitalize(field.getName()); } try { @@ -250,26 +266,138 @@ public static String getAccessMethod(String target, Field field, Class clazz, type = "byte[]"; } - // use reflection to get value - String code = "(" + FieldUtils.toObjectType(type) + ") "; + String code = "(" + toObjectType(type) + ") "; code += "FieldUtil.getField(" + target + ", \"" + field.getName() + "\")"; return code; } - public static String getMappedTypeSize(ProtobufField field) { + public static String getSizeDynamicString(ProtobufField field) { ProtobufFieldTypeEnum protobufFieldType = field.getProtobufFieldType(); int order = field.getOrder(); boolean isList = field.isList(); boolean isMap = field.isMap(); boolean packed = field.isPacked(); - String type = protobufFieldType.getType().toUpperCase(); + String typeName = protobufFieldType.getType().toUpperCase(); + String dynamicFieldName = getDynamicFieldName(order); + if (isList) { - //todo + return "FieldUtil.getListSize(" + order + "," + dynamicFieldName + "," + ProtobufFieldTypeEnum.class.getName() + "." + typeName + + "," + field.isPacked() + ");\n"; + } else if (isMap) { + return "FieldUtil.getMapSize(" + order + "," + dynamicFieldName + "," + getMapFieldGenericParameterString(field) + ");\n"; } - return null; + if (protobufFieldType == ProtobufFieldTypeEnum.OBJECT) { + return "FieldUtil.getObjectSize(" + order + "," + dynamicFieldName + ", " + ProtobufFieldTypeEnum.class.getName() + "." + + typeName + ");\n"; + } + + String javaType = protobufFieldType.getType(); + if (protobufFieldType == ProtobufFieldTypeEnum.STRING) { + javaType = "String"; + } + + if (protobufFieldType == ProtobufFieldTypeEnum.BYTES) { + javaType = "ByteArray"; + } + javaType = capitalize(javaType); + dynamicFieldName = dynamicFieldName + protobufFieldType.getToPrimitiveType(); + //todo check 感觉上面这个有点问题,测试的时候看下 + return "com.google.protobuf.CodedOutputStream.compute" + javaType + "Size(" + order + "," + dynamicFieldName + ")" + + ");\n"; + } + + private static String getMapFieldGenericParameterString(ProtobufField field) { + String wireFormatClassName = WireFormat.FieldType.class.getCanonicalName(); + ProtobufFieldTypeEnum fieldType = TYPE_MAPPER.get(field.getGenericKeyType()); + String keyClass; + String defaultKeyValue; + if (fieldType == null) { + if (Enum.class.isAssignableFrom(field.getGenericKeyType())) { + keyClass = wireFormatClassName + ".ENUM"; + Class declaringClass = field.getGenericKeyType(); + Field[] fields = declaringClass.getFields(); + if (fields.length > 0) { + defaultKeyValue = field.getGenericKeyType().getCanonicalName() + "." + + fields[0].getName(); + } else { + defaultKeyValue = "0"; + } + + } else { + keyClass = wireFormatClassName + ".MESSAGE"; + boolean hasDefaultConstructor = hasDefaultConstructor(field.getGenericKeyType()); + if (!hasDefaultConstructor) { + throw new IllegalArgumentException("Class '" + field.getGenericKeyType().getCanonicalName() + + "' must has default constructor method with no parameters."); + } + defaultKeyValue = + "new " + field.getGenericKeyType().getCanonicalName() + "()"; + } + } else { + keyClass = wireFormatClassName + "." + fieldType.toString(); + + defaultKeyValue = fieldType.getDefaultValue(); + } + + fieldType = TYPE_MAPPER.get(field.getGenericValueType()); + String valueClass; + String defaultValueValue; + if (fieldType == null) { + if (Enum.class.isAssignableFrom(field.getGenericValueType())) { + valueClass = wireFormatClassName + ".ENUM"; + Class declaringClass = field.getGenericValueType(); + Field[] fields = declaringClass.getFields(); + if (fields.length > 0) { + defaultValueValue = field.getGenericValueType().getCanonicalName() + + "." + fields[0].getName(); + } else { + defaultValueValue = "0"; + } + + } else { + valueClass = wireFormatClassName + ".MESSAGE"; + // check constructor + boolean hasDefaultConstructor = hasDefaultConstructor(field.getGenericValueType()); + if (!hasDefaultConstructor) { + throw new IllegalArgumentException("Class '" + field.getGenericValueType().getCanonicalName() + + "' must has default constructor method with no parameters."); + } + defaultValueValue = + "new " + field.getGenericValueType().getCanonicalName() + "()"; + } + } else { + valueClass = wireFormatClassName + "." + fieldType; + defaultValueValue = fieldType.getDefaultValue(); + } + String joinedSentence = keyClass + "," + defaultKeyValue + "," + valueClass + "," + defaultValueValue; + return joinedSentence; + } + + public static boolean hasDefaultConstructor(Class cls) { + if (cls == null) { + return false; + } + try { + cls.getConstructor(new Class[0]); + } catch (NoSuchMethodException e) { + return false; + } catch (SecurityException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } + return true; + } + + /** + * 通过 order 获取动态生成的字段名 + * + * @param order + * @return + */ + public static String getDynamicFieldName(int order) { + return "f_" + order; } public static int getListSize(int order, Collection list, ProtobufFieldTypeEnum type, boolean packed) { @@ -286,7 +414,7 @@ public static int getListSize(int order, Collection list, ProtobufFieldTypeEn if (type != ProtobufFieldTypeEnum.OBJECT) { if (packed) { size += com.google.protobuf.CodedOutputStream.computeInt32SizeNoTag(dataSize); - int tag = CodedConstant.makeTag(order, WireFormat.WIRETYPE_LENGTH_DELIMITED); + int tag = makeTag(order, WireFormat.WIRETYPE_LENGTH_DELIMITED); size += com.google.protobuf.CodedOutputStream.computeUInt32SizeNoTag(tag); } else { size += list.size() * CodedOutputStream.computeTagSize(order); @@ -295,12 +423,12 @@ public static int getListSize(int order, Collection list, ProtobufFieldTypeEn return size; } - public static int getMapSize(int order, Map map,com.google.protobuf.WireFormat.FieldType keyType, + public static int getMapSize(int order, Map map, com.google.protobuf.WireFormat.FieldType keyType, K defaultKey, com.google.protobuf.WireFormat.FieldType valueType, V defalutValue) { int size = 0; for (java.util.Map.Entry entry : map.entrySet()) { MapEntry valuesDefaultEntry = MapEntry - . newDefaultInstance(null, keyType, defaultKey, valueType, defalutValue); + .newDefaultInstance(null, keyType, defaultKey, valueType, defalutValue); MapEntry values = valuesDefaultEntry.newBuilderForType().setKey(entry.getKey()).setValue(entry.getValue()).build(); @@ -348,13 +476,45 @@ public static int getObjectSize(int order, Object object, ProtobufFieldTypeEnum } else if (type == ProtobufFieldTypeEnum.FLOAT) { size = CodedOutputStream.computeFloatSizeNoTag(Float.valueOf(object.toString())); } else if (type == ProtobufFieldTypeEnum.ENUM) { - if (object instanceof EnumReadable) { - size = CodedOutputStream.computeInt32SizeNoTag(((EnumReadable) object).value()); - } else if (object instanceof Enum) { - size = CodedOutputStream.computeInt32SizeNoTag(((Enum) object).ordinal()); - } + size = CodedOutputStream.computeInt32SizeNoTag(((Enum) object).ordinal()); } - return size; } + + /** + * 首字母大写 + * + * @param str + * @return + */ + public static String capitalize(String str) { + if (str == null || str.isEmpty()) { + return str; + } + return Character.toTitleCase(str.charAt(0)) + str.substring(1); + } + + /** + * 生成 protobuf tag + * + * @param fieldNumber + * @param wireType + * @return + */ + public static int makeTag(final int fieldNumber, final int wireType) { + return (fieldNumber << 3) | wireType; + } + + /** + * 基础类型转为包装对象 + * + * @param primitiveType + * @return + */ + public static String toObjectType(String primitiveType) { + if (PRIMITIVE_TYPE_MAPPING.containsKey(primitiveType)) { + return PRIMITIVE_TYPE_MAPPING.get(primitiveType); + } + return primitiveType; + } } From 8dd4c42a5d65ce32742a0cf402d39b2ae7eb32b5 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Fri, 2 Aug 2024 01:43:09 +0800 Subject: [PATCH 13/48] update: add protobuf codec --- .../taobao/arthas/protobuf/ProtobufProxy.java | 18 ++++++---- .../arthas/protobuf/utils/FieldUtil.java | 34 ++++++++++++++++++- .../src/main/resources/class_template.tpl | 14 ++++---- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java index 58a221c64c..4e0c7b2562 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java @@ -94,13 +94,19 @@ private static void processEncodeBlock() { boolean isMap = protobufField.isMap(); ProtobufFieldTypeEnum protobufFieldType = protobufField.getProtobufFieldType(); - String encodeFieldGetter = FieldUtil.getGetterDynamicString("target", protobufField.getJavaField(), clazz, protobufField.isWildcardType()); - String encodeFileType = isList ? "Collection" : protobufField.getProtobufFieldType().getJavaType(); - String encodeFieldName = FieldUtil.getDynamicFieldName(protobufField.getOrder()); + String dynamicFieldGetter = FieldUtil.getGetterDynamicString("target", protobufField.getJavaField(), clazz, protobufField.isWildcardType()); + String dynamicFieldType = isList ? "Collection" : protobufField.getProtobufFieldType().getJavaType(); + String dynamicFieldName = FieldUtil.getDynamicFieldName(protobufField.getOrder()); - miniTemplator.setVariable("encodeFileType",encodeFileType); - miniTemplator.setVariable("encodeFieldName",encodeFieldName); - miniTemplator.setVariable("encodeFieldGetter",encodeFieldGetter); + miniTemplator.setVariable("dynamicFieldType", dynamicFieldType); + miniTemplator.setVariable("dynamicFieldName", dynamicFieldName); + miniTemplator.setVariable("dynamicFieldGetter", dynamicFieldGetter); + String sizeDynamicString = FieldUtil.getSizeDynamicString(protobufField); + miniTemplator.setVariable("sizeDynamicString", sizeDynamicString); + + //todo + + miniTemplator.addBlock("encodeFields"); } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java index 90a52adde6..04e2770f6f 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java @@ -277,7 +277,6 @@ public static String getSizeDynamicString(ProtobufField field) { int order = field.getOrder(); boolean isList = field.isList(); boolean isMap = field.isMap(); - boolean packed = field.isPacked(); String typeName = protobufFieldType.getType().toUpperCase(); String dynamicFieldName = getDynamicFieldName(order); @@ -517,4 +516,37 @@ public static String toObjectType(String primitiveType) { } return primitiveType; } + + + public static boolean isNull(Object o) { + return o == null; + } + + public static boolean isNull(double o) { + return false; + } + + public static boolean isNull(int o) { + return false; + } + + public static boolean isNull(byte o) { + return false; + } + + public static boolean isNull(short o) { + return false; + } + + public static boolean isNull(long o) { + return false; + } + + public static boolean isNull(float o) { + return false; + } + + public static boolean isNull(char o) { + return false; + } } diff --git a/arthas-grpc-server/src/main/resources/class_template.tpl b/arthas-grpc-server/src/main/resources/class_template.tpl index 0ef374c117..68c7cf3974 100644 --- a/arthas-grpc-server/src/main/resources/class_template.tpl +++ b/arthas-grpc-server/src/main/resources/class_template.tpl @@ -13,9 +13,9 @@ public class ${className} implements ${codecClassName}<${targetProxyClassName}>, CodecOutputByteArray output = CodecOutputByteArray.get(); - ${encodeFieldType} ${encodeFieldName} = null; - if (!CodedConstant.isNull(${encodeFieldGetter})) { - ${encodeFieldName} = ${writeValueToField}; + ${dynamicFieldType} ${dynamicFieldName} = null; + if (!FieldUtil.isNull(${dynamicFieldGetter})) { + ${dynamicFieldName} = ${dynamicFieldGetter}; ${encodeWriteFieldValue} } @@ -31,10 +31,10 @@ public class ${className} implements ${codecClassName}<${targetProxyClassName}>, public int size(${targetProxyClassName} target) throws IOException { int size = 0; - ${encodeFieldType} ${encodeFieldName} = null; - if (!CodedConstant.isNull(${encodeFieldGetter})) { - ${encodeFieldName} = ${encodeFieldGetter}; - size += ${calcSize} + ${dynamicFieldType} ${dynamicFieldName} = null; + if (!CodedConstant.isNull(${dynamicFieldGetter})) { + ${dynamicFieldName} = ${dynamicFieldGetter}; + size += ${sizeDynamicString} } return size; From bc81e9837f62e0b074f14abb86b8678a906b1dee Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Sat, 3 Aug 2024 20:53:03 +0800 Subject: [PATCH 14/48] update: add protobuf codec --- .../taobao/arthas/protobuf/ProtobufProxy.java | 29 +- .../utils/CodedOutputStreamCache.java | 52 ++++ .../arthas/protobuf/utils/FieldUtil.java | 288 ++++++++++++++++-- .../src/main/resources/class_template.tpl | 9 +- 4 files changed, 344 insertions(+), 34 deletions(-) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/CodedOutputStreamCache.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java index 4e0c7b2562..57528c8150 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java @@ -4,12 +4,14 @@ */ +import com.baidu.bjf.remoting.protobuf.code.CodedConstant; import com.taobao.arthas.protobuf.annotation.ProtobufEnableZigZap; import com.taobao.arthas.protobuf.annotation.ProtobufClass; import com.taobao.arthas.protobuf.utils.FieldUtil; import com.taobao.arthas.protobuf.utils.MiniTemplator; import com.taobao.arthas.service.req.ArthasSampleRequest; +import java.io.IOException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -48,7 +50,7 @@ public ProtobufCodec getCodecCacheSide(Class clazz) { } } - public static ProtobufCodec create(Class clazz) throws Exception { + public static ProtobufCodec create(Class clazz) { Objects.requireNonNull(clazz); if (clazz.getAnnotation(ProtobufClass.class) == null) { throw new IllegalArgumentException("class is not annotated with @ProtobufClass"); @@ -57,7 +59,11 @@ public static ProtobufCodec create(Class clazz) throws Exception { loadProtobufField(); String path = Objects.requireNonNull(clazz.getResource(TEMPLATE_FILE)).getPath(); - miniTemplator = new MiniTemplator(path); + try { + miniTemplator = new MiniTemplator(path); + } catch (Exception e) { + throw new RuntimeException("miniTemplator init failed. " + path, e); + } miniTemplator.setVariable("package", "package " + clazz.getPackage().getName() + ";"); @@ -66,7 +72,8 @@ public static ProtobufCodec create(Class clazz) throws Exception { miniTemplator.setVariable("className", clazz.getName() + "$$ProxyClass"); miniTemplator.setVariable("codecClassName", ProtobufCodec.class.getName()); miniTemplator.setVariable("targetProxyClassName", clazz.getName()); - + processEncodeBlock(); + processDecodeBlock(); return null; } @@ -90,12 +97,8 @@ private static void processImportBlock() { private static void processEncodeBlock() { for (ProtobufField protobufField : protobufFields) { - boolean isList = protobufField.isList(); - boolean isMap = protobufField.isMap(); - - ProtobufFieldTypeEnum protobufFieldType = protobufField.getProtobufFieldType(); - String dynamicFieldGetter = FieldUtil.getGetterDynamicString("target", protobufField.getJavaField(), clazz, protobufField.isWildcardType()); - String dynamicFieldType = isList ? "Collection" : protobufField.getProtobufFieldType().getJavaType(); + String dynamicFieldGetter = FieldUtil.getGetterDynamicString(protobufField, clazz); + String dynamicFieldType = protobufField.isList() ? "Collection" : protobufField.getProtobufFieldType().getJavaType(); String dynamicFieldName = FieldUtil.getDynamicFieldName(protobufField.getOrder()); miniTemplator.setVariable("dynamicFieldType", dynamicFieldType); @@ -103,13 +106,15 @@ private static void processEncodeBlock() { miniTemplator.setVariable("dynamicFieldGetter", dynamicFieldGetter); String sizeDynamicString = FieldUtil.getSizeDynamicString(protobufField); miniTemplator.setVariable("sizeDynamicString", sizeDynamicString); - - //todo - + miniTemplator.setVariable("encodeWriteFieldValue", FieldUtil.getWriteByteDynamicString(protobufField)); miniTemplator.addBlock("encodeFields"); } } + private static void processDecodeBlock() { + + } + private static void loadProtobufField() { protobufFields = FieldUtil.getProtobufFieldList(clazz, diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/CodedOutputStreamCache.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/CodedOutputStreamCache.java new file mode 100644 index 0000000000..756943f602 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/CodedOutputStreamCache.java @@ -0,0 +1,52 @@ +package com.taobao.arthas.protobuf.utils;/** + * @author: 風楪 + * @date: 2024/8/3 下午7:20 + */ + +import com.google.protobuf.CodedOutputStream; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Stack; + +/** + * @author: FengYe + * @date: 2024/8/3 下午7:20 + * @description: 针对 CodedOutputStream 的缓存处理,避免创建大量 CodedOutputStream;使用 threadLocal 中的 stack 来缓存对象 + */ +public class CodedOutputStreamCache { + private static final ThreadLocal> instanceGetter = ThreadLocal.withInitial(Stack::new); + private final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + private final CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(byteArrayOutputStream, 0); + + // 每个线程 stack 中最多存储的 buffer 数量 + private static final int MAX_ELEMENT = 5; + + public static CodedOutputStreamCache get() { + Stack stack = instanceGetter.get(); + if (!stack.isEmpty()) { + return stack.pop(); + } + return new CodedOutputStreamCache(); + } + + public byte[] getData() throws IOException { + this.codedOutputStream.flush(); + byte[] bytes = this.byteArrayOutputStream.toByteArray(); + this.recycle(); + return bytes; + } + + public CodedOutputStream getCodedOutputStream() { + return codedOutputStream; + } + + private void recycle(){ + this.byteArrayOutputStream.reset(); + Stack stack = instanceGetter.get(); + if (stack.size() >= MAX_ELEMENT) { + return; + } + stack.push(this); + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java index 04e2770f6f..00cc942a24 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java @@ -4,8 +4,10 @@ */ +import com.baidu.bjf.remoting.protobuf.Codec; import com.baidu.bjf.remoting.protobuf.EnumReadable; import com.baidu.bjf.remoting.protobuf.FieldType; +import com.baidu.bjf.remoting.protobuf.code.CodecOutputByteArray; import com.baidu.bjf.remoting.protobuf.code.ICodeGenerator; import com.baidu.bjf.remoting.protobuf.utils.ClassHelper; import com.google.protobuf.ByteString; @@ -21,6 +23,7 @@ import com.taobao.arthas.protobuf.annotation.ProtobufIgnore; +import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.math.BigDecimal; @@ -34,12 +37,19 @@ */ public class FieldUtil { - public static final String PACKAGE_SEPARATOR = "."; public static final Map, ProtobufFieldTypeEnum> TYPE_MAPPER; private static final Map PRIMITIVE_TYPE_MAPPING; + private static final String dynamicTarget = "target"; + + public static final String PACKAGE_SEPARATOR = "."; + + private static final String LINE_BREAK = "\n"; + + private static final String CODE_OUTPUT_STREAM_OBJ_NAME = "output"; + static { PRIMITIVE_TYPE_MAPPING = new HashMap(); @@ -233,18 +243,13 @@ public static Object getField(Object t, String name) { return null; } - /** - * 获取目标的访问方法字符串,如果目标已经声明 getter 则返回 getter,否则使用 FieldUtil.getField - * - * @param target - * @param field - * @param clazz - * @param wildcardType - * @return - */ - public static String getGetterDynamicString(String target, Field field, Class clazz, boolean wildcardType) { + + public static String getGetterDynamicString(ProtobufField protobufField, Class dynamicTargetClass) { + Field field = protobufField.getJavaField(); + boolean wildcardType = protobufField.isWildcardType(); + if (field.getModifiers() == Modifier.PUBLIC && !wildcardType) { - return target + PACKAGE_SEPARATOR + field.getName(); + return dynamicTarget + PACKAGE_SEPARATOR + field.getName(); } String getter; @@ -255,8 +260,8 @@ public static String getGetterDynamicString(String target, Field field, Class } try { - clazz.getMethod(getter, new Class[0]); - return target + PACKAGE_SEPARATOR + getter + "()"; + dynamicTargetClass.getMethod(getter, new Class[0]); + return dynamicTarget + PACKAGE_SEPARATOR + getter + "()"; } catch (Exception e) { //todo log } @@ -267,11 +272,17 @@ public static String getGetterDynamicString(String target, Field field, Class } String code = "(" + toObjectType(type) + ") "; - code += "FieldUtil.getField(" + target + ", \"" + field.getName() + "\")"; + code += "FieldUtil.getField(" + dynamicTarget + ", \"" + field.getName() + "\")"; return code; } + /** + * 获取计算 size 动态字符串 + * + * @param field + * @return + */ public static String getSizeDynamicString(ProtobufField field) { ProtobufFieldTypeEnum protobufFieldType = field.getProtobufFieldType(); int order = field.getOrder(); @@ -283,14 +294,14 @@ public static String getSizeDynamicString(ProtobufField field) { if (isList) { return "FieldUtil.getListSize(" + order + "," + dynamicFieldName + "," + ProtobufFieldTypeEnum.class.getName() + "." + typeName - + "," + field.isPacked() + ");\n"; + + "," + field.isPacked() + ");" + LINE_BREAK; } else if (isMap) { - return "FieldUtil.getMapSize(" + order + "," + dynamicFieldName + "," + getMapFieldGenericParameterString(field) + ");\n"; + return "FieldUtil.getMapSize(" + order + "," + dynamicFieldName + "," + getMapFieldGenericParameterString(field) + ");" + LINE_BREAK; } if (protobufFieldType == ProtobufFieldTypeEnum.OBJECT) { return "FieldUtil.getObjectSize(" + order + "," + dynamicFieldName + ", " + ProtobufFieldTypeEnum.class.getName() + "." - + typeName + ");\n"; + + typeName + ");" + LINE_BREAK; } String javaType = protobufFieldType.getType(); @@ -305,7 +316,246 @@ public static String getSizeDynamicString(ProtobufField field) { dynamicFieldName = dynamicFieldName + protobufFieldType.getToPrimitiveType(); //todo check 感觉上面这个有点问题,测试的时候看下 return "com.google.protobuf.CodedOutputStream.compute" + javaType + "Size(" + order + "," + dynamicFieldName + ")" - + ");\n"; + + ");" + LINE_BREAK; + } + + /** + * 获取写入 CodedOutputStream 动态字符串 + * + * @param protobufField + * @return + */ + public static String getWriteByteDynamicString(ProtobufField protobufField) { + ProtobufFieldTypeEnum protobufFieldType = protobufField.getProtobufFieldType(); + int order = protobufField.getOrder(); + String dynamicFieldName = getDynamicFieldName(protobufField.getOrder()); + StringBuilder sb = new StringBuilder(); + sb.append("if (").append(dynamicFieldName).append(" != null){").append(LINE_BREAK); + + if (protobufField.isList()) { + String typeString = protobufFieldType.getType().toUpperCase(); + sb.append("Field.writeList(").append(CODE_OUTPUT_STREAM_OBJ_NAME).append(","); + sb.append(order).append(",").append(ProtobufFieldTypeEnum.class.getName()).append(".").append(typeString); + sb.append(",").append(dynamicFieldName).append(",").append(Boolean.valueOf(protobufField.isPacked())).append(")") + .append(";" + LINE_BREAK).append("}").append(LINE_BREAK); + return sb.toString(); + } else if (protobufField.isMap()) { + sb.append("Field.writeMap(").append(CODE_OUTPUT_STREAM_OBJ_NAME).append(","); + sb.append(order).append(",").append(dynamicFieldName); + + String joinedSentence = getMapFieldGenericParameterString(protobufField); + sb.append(",").append(joinedSentence); + + sb.append(")").append(";" + LINE_BREAK).append("}").append(LINE_BREAK); + return sb.toString(); + } else { + dynamicFieldName = dynamicFieldName + protobufFieldType.getToPrimitiveType(); + } + + if (protobufFieldType == ProtobufFieldTypeEnum.OBJECT) { + String typeString = protobufFieldType.getType().toUpperCase(); + sb.append("Field.writeObject(").append(CODE_OUTPUT_STREAM_OBJ_NAME).append(","); + sb.append(order).append(",").append(ProtobufFieldTypeEnum.class.getName()).append(".").append(typeString); + sb.append(",").append(dynamicFieldName).append(", false)").append(";" + LINE_BREAK).append("}") + .append(LINE_BREAK); + return sb.toString(); + } + + if (protobufFieldType == ProtobufFieldTypeEnum.STRING) { + sb.append(CODE_OUTPUT_STREAM_OBJ_NAME).append(".writeString(").append(order); + sb.append(", ").append(dynamicFieldName).append(")").append(";" + LINE_BREAK).append("}") + .append(LINE_BREAK); + return sb.toString(); + } + + if (protobufFieldType == ProtobufFieldTypeEnum.BYTES) { + sb.append(CODE_OUTPUT_STREAM_OBJ_NAME).append(".writeByteArray(").append(order); + sb.append(", ").append(dynamicFieldName).append(")").append(";" + LINE_BREAK).append("}") + .append(LINE_BREAK); + return sb.toString(); + } + + String t = protobufFieldType.getType(); + t = capitalize(t); + + sb.append(CODE_OUTPUT_STREAM_OBJ_NAME).append(".write").append(t).append("(").append(order); + sb.append(", ").append(dynamicFieldName).append(")").append(";" + LINE_BREAK).append("}") + .append(LINE_BREAK); + return sb.toString(); + } + + public static void writeList(CodedOutputStream out, int order, ProtobufFieldTypeEnum type, Collection list) + throws IOException { + writeList(out, order, type, list, false); + } + + /** + * java list 写入 CodedOutputStream + */ + public static void writeList(CodedOutputStream out, int order, ProtobufFieldTypeEnum type, Collection list, boolean packed) + throws IOException { + if (list == null || list.isEmpty()) { + return; + } + + CodedOutputStreamCache output = CodedOutputStreamCache.get(); + for (Object object : list) { + if (object == null) { + throw new NullPointerException("List can not include Null value."); + } + writeObject(output.getCodedOutputStream(), order, type, object, true, !packed); + } + byte[] byteArray = output.getData(); + + if (packed) { + out.writeUInt32NoTag(makeTag(order, WireFormat.WIRETYPE_LENGTH_DELIMITED)); + out.writeUInt32NoTag(byteArray.length); + } + + out.write(byteArray, 0, byteArray.length); + + } + + /** + * java map 写入 output + */ + public static void writeMap(CodedOutputStream output, int order, Map map, + com.google.protobuf.WireFormat.FieldType keyType, K defaultKey, + com.google.protobuf.WireFormat.FieldType valueType, V defalutValue) throws IOException { + MapEntry valuesDefaultEntry = MapEntry + .newDefaultInstance(null, keyType, defaultKey, valueType, defalutValue); + for (java.util.Map.Entry entry : map.entrySet()) { + MapEntry values = + valuesDefaultEntry.newBuilderForType().setKey(entry.getKey()).setValue(entry.getValue()).build(); + output.writeMessage(order, values); + } + } + + /** + * java object 写入 CodedOutputStream + */ + public static void writeObject(CodedOutputStream out, int order, ProtobufFieldTypeEnum type, Object o, boolean list, + boolean withTag) throws IOException { + if (o == null) { + return; + } + + if (type == ProtobufFieldTypeEnum.OBJECT) { + + Class cls = o.getClass(); + ProtobufCodec target = ProtobufProxy.create(cls); + + if (withTag) { + out.writeUInt32NoTag(makeTag(order, WireFormat.WIRETYPE_LENGTH_DELIMITED)); + } + + byte[] byteArray = target.encode(o); + out.writeUInt32NoTag(byteArray.length); + out.write(byteArray, 0, byteArray.length); + + return; + } + + if (type == ProtobufFieldTypeEnum.BOOL) { + if (withTag) { + out.writeBool(order, (Boolean) o); + } else { + out.writeBoolNoTag((Boolean) o); + } + } else if (type == ProtobufFieldTypeEnum.BYTES) { + byte[] bb = (byte[]) o; + if (withTag) { + out.writeBytes(order, ByteString.copyFrom(bb)); + } else { + out.writeBytesNoTag(ByteString.copyFrom(bb)); + } + } else if (type == ProtobufFieldTypeEnum.DOUBLE) { + if (withTag) { + out.writeDouble(order, (Double) o); + } else { + out.writeDoubleNoTag((Double) o); + } + } else if (type == ProtobufFieldTypeEnum.FIXED32) { + if (withTag) { + out.writeFixed32(order, (Integer) o); + } else { + out.writeFixed32NoTag((Integer) o); + } + } else if (type == ProtobufFieldTypeEnum.FIXED64) { + if (withTag) { + out.writeFixed64(order, (Long) o); + } else { + out.writeFixed64NoTag((Long) o); + } + } else if (type == ProtobufFieldTypeEnum.FLOAT) { + if (withTag) { + out.writeFloat(order, (Float) o); + } else { + out.writeFloatNoTag((Float) o); + } + } else if (type == ProtobufFieldTypeEnum.INT32) { + if (withTag) { + out.writeInt32(order, (Integer) o); + } else { + out.writeInt32NoTag((Integer) o); + } + } else if (type == ProtobufFieldTypeEnum.INT64) { + if (withTag) { + out.writeInt64(order, (Long) o); + } else { + out.writeInt64NoTag((Long) o); + } + } else if (type == ProtobufFieldTypeEnum.SFIXED32) { + if (withTag) { + out.writeSFixed32(order, (Integer) o); + } else { + out.writeSFixed32NoTag((Integer) o); + } + } else if (type == ProtobufFieldTypeEnum.SFIXED64) { + if (withTag) { + out.writeSFixed64(order, (Long) o); + } else { + out.writeSFixed64NoTag((Long) o); + } + } else if (type == ProtobufFieldTypeEnum.SINT32) { + if (withTag) { + out.writeSInt32(order, (Integer) o); + } else { + out.writeSInt32NoTag((Integer) o); + } + } else if (type == ProtobufFieldTypeEnum.SINT64) { + if (withTag) { + out.writeSInt64(order, (Long) o); + } else { + out.writeSInt64NoTag((Long) o); + } + } else if (type == ProtobufFieldTypeEnum.STRING) { + if (withTag) { + out.writeBytes(order, ByteString.copyFromUtf8(String.valueOf(o))); + } else { + out.writeBytesNoTag(ByteString.copyFromUtf8(String.valueOf(o))); + } + } else if (type == ProtobufFieldTypeEnum.UINT32) { + if (withTag) { + out.writeUInt32(order, (Integer) o); + } else { + out.writeUInt32NoTag((Integer) o); + } + } else if (type == ProtobufFieldTypeEnum.UINT64) { + if (withTag) { + out.writeUInt64(order, (Long) o); + } else { + out.writeUInt64NoTag((Long) o); + } + } else if (type == ProtobufFieldTypeEnum.ENUM) { + int value; + value = ((Enum) o).ordinal(); + if (withTag) { + out.writeEnum(order, value); + } else { + out.writeEnumNoTag(value); + } + } } private static String getMapFieldGenericParameterString(ProtobufField field) { diff --git a/arthas-grpc-server/src/main/resources/class_template.tpl b/arthas-grpc-server/src/main/resources/class_template.tpl index 68c7cf3974..c04e8ba504 100644 --- a/arthas-grpc-server/src/main/resources/class_template.tpl +++ b/arthas-grpc-server/src/main/resources/class_template.tpl @@ -10,8 +10,13 @@ public class ${className} implements ${codecClassName}<${targetProxyClassName}>, private ${descriptorClsName} descriptor; public byte[] encode(${targetProxyClassName} target) throws IOException { - CodecOutputByteArray output = CodecOutputByteArray.get(); + CodedOutputStreamCache outputCache = CodedOutputStreamCache.get(); + doWriteTo(target, outputCache.getCodedOutputStream()); + return outputCache.getData(); + } + public void doWriteTo(${targetProxyClassName} t, CodedOutputStream output) + throws IOException { ${dynamicFieldType} ${dynamicFieldName} = null; if (!FieldUtil.isNull(${dynamicFieldGetter})) { @@ -19,8 +24,6 @@ public class ${className} implements ${codecClassName}<${targetProxyClassName}>, ${encodeWriteFieldValue} } - - return output.getData(); } public ${targetProxyClassName} decode(byte[] bb) throws IOException { From 1640f19f810710c7eb6b7d7909be08aabcfbce1e Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Sun, 4 Aug 2024 06:10:00 +0800 Subject: [PATCH 15/48] update: add protobuf codec --- .../taobao/arthas/protobuf/ProtobufProxy.java | 47 +++- .../arthas/protobuf/utils/FieldUtil.java | 225 ++++++++++++++++-- .../service/req/ArthasSampleRequest.java | 16 ++ arthas-grpc-server/src/main/proto/Test.proto | 10 +- .../src/main/resources/class_template.tpl | 4 +- 5 files changed, 277 insertions(+), 25 deletions(-) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java index 57528c8150..dcd6c49b6d 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java @@ -4,7 +4,11 @@ */ +import com.baidu.bjf.remoting.protobuf.FieldType; +import com.baidu.bjf.remoting.protobuf.code.ClassCode; import com.baidu.bjf.remoting.protobuf.code.CodedConstant; +import com.baidu.bjf.remoting.protobuf.utils.ClassHelper; +import com.baidu.bjf.remoting.protobuf.utils.FieldInfo; import com.taobao.arthas.protobuf.annotation.ProtobufEnableZigZap; import com.taobao.arthas.protobuf.annotation.ProtobufClass; import com.taobao.arthas.protobuf.utils.FieldUtil; @@ -112,7 +116,40 @@ private static void processEncodeBlock() { } private static void processDecodeBlock() { + // 初始化 list、map、enum + StringBuilder initListMapFields = new StringBuilder(); + for (ProtobufField protobufField : protobufFields) { + boolean isList = protobufField.isList(); + boolean isMap = protobufField.isMap(); + String e = ""; + if (isList) { + if (FieldInfo.isListType(protobufField.getJavaField())) { + e = "new ArrayList()"; + } else if (FieldInfo.isSetType(protobufField.getJavaField())) { + e = "new HashSet()"; + } + } else if (isMap) { + e = "new HashMap()"; + } + if (isList || isMap) { + initListMapFields.append(FieldUtil.getInitListMapFieldDynamicString(protobufField, e)); + } + + if (protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.ENUM) { + String clsName = protobufField.getJavaField().getType().getCanonicalName(); + if (!isList) { + String express = + "CodedConstant.getEnumValue(" + clsName + ".class, " + clsName + ".values()[0].name())"; + // add set get method + String setToField = getSetToField("ret", field.getField(), cls, express, isList, field.isMap(), + false, field.isWildcardType()); + miniTemplator.setVariable("enumInitialize", setToField); + miniTemplator.addBlock("enumFields"); + } + } + } + miniTemplator.setVariable("initListMapFields", initListMapFields.toString()); } @@ -123,11 +160,11 @@ private static void loadProtobufField() { } public static void main(String[] args) { - List protobufFieldList = FieldUtil.getProtobufFieldList(ArthasSampleRequest.class, false); - for (ProtobufField protobufField : protobufFieldList) { - String target = FieldUtil.getGetterDynamicString("target", protobufField.getJavaField(), ArthasSampleRequest.class, protobufField.isWildcardType()); - System.out.println(target); - } +// List protobufFieldList = FieldUtil.getProtobufFieldList(ArthasSampleRequest.class, false); +// for (ProtobufField protobufField : protobufFieldList) { +// String target = FieldUtil.getGetterDynamicString("target", protobufField.getJavaField(), ArthasSampleRequest.class, protobufField.isWildcardType()); +// System.out.println(target); +// } } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java index 00cc942a24..77d4a46123 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java @@ -4,12 +4,6 @@ */ -import com.baidu.bjf.remoting.protobuf.Codec; -import com.baidu.bjf.remoting.protobuf.EnumReadable; -import com.baidu.bjf.remoting.protobuf.FieldType; -import com.baidu.bjf.remoting.protobuf.code.CodecOutputByteArray; -import com.baidu.bjf.remoting.protobuf.code.ICodeGenerator; -import com.baidu.bjf.remoting.protobuf.utils.ClassHelper; import com.google.protobuf.ByteString; import com.google.protobuf.CodedOutputStream; import com.google.protobuf.MapEntry; @@ -42,12 +36,14 @@ public class FieldUtil { private static final Map PRIMITIVE_TYPE_MAPPING; - private static final String dynamicTarget = "target"; + private static final String DYNAMIC_TARGET = "target"; public static final String PACKAGE_SEPARATOR = "."; private static final String LINE_BREAK = "\n"; + private static final String JAVA_LINE_BREAK = ";" + LINE_BREAK; + private static final String CODE_OUTPUT_STREAM_OBJ_NAME = "output"; static { @@ -206,6 +202,25 @@ public static List getProtobufFieldList(Class clazz, boolean e return res; } + /** + * 在 clazz 上寻找字段名为 name 的字段 + * + * @param clazz + * @param name + * @return + */ + public static Field findField(Class clazz, String name) { + return findField(clazz, name, null); + } + + /** + * 在 clazz 上寻找字段名为 name、字段类型为 type 的字段 + * + * @param clazz + * @param name + * @param type + * @return + */ public static Field findField(Class clazz, String name, Class type) { if (clazz == null) { throw new IllegalArgumentException("Class must not be null"); @@ -229,6 +244,13 @@ public static Field findField(Class clazz, String name, Class type) { return null; } + /** + * 获取对象 t 上的字段名为 name 的字段值 + * + * @param t + * @param name + * @return + */ public static Object getField(Object t, String name) { Field field = findField(t.getClass(), name, null); if (field == null) { @@ -243,13 +265,26 @@ public static Object getField(Object t, String name) { return null; } + public static void setField(Object t, String name, Object value) { + Field field = findField(t.getClass(), name); + if (field == null) { + return; + } + field.setAccessible(true); + try { + field.set(t, value); + } catch (Exception e) { + //todo log + } + } + public static String getGetterDynamicString(ProtobufField protobufField, Class dynamicTargetClass) { Field field = protobufField.getJavaField(); boolean wildcardType = protobufField.isWildcardType(); if (field.getModifiers() == Modifier.PUBLIC && !wildcardType) { - return dynamicTarget + PACKAGE_SEPARATOR + field.getName(); + return DYNAMIC_TARGET + PACKAGE_SEPARATOR + field.getName(); } String getter; @@ -261,7 +296,7 @@ public static String getGetterDynamicString(ProtobufField protobufField, Class[0]); - return dynamicTarget + PACKAGE_SEPARATOR + getter + "()"; + return DYNAMIC_TARGET + PACKAGE_SEPARATOR + getter + "()"; } catch (Exception e) { //todo log } @@ -272,7 +307,7 @@ public static String getGetterDynamicString(ProtobufField protobufField, Class dynamicTargetClass, String express) { + StringBuilder sb = new StringBuilder(); + boolean isMap = protobufField.isMap(); + boolean isList = protobufField.isList(); + boolean isWildcardType = protobufField.isWildcardType(); + boolean isPacked = protobufField.isPacked(); + Field javaField = protobufField.getJavaField(); + + + if (isList || isMap) { + sb.append("if ((").append(getGetterDynamicString(protobufField, dynamicTargetClass)).append(") == null) {") + .append(LINE_BREAK); + } + + String collectionTypetoCreate = ""; + String collectionType = ""; + if (List.class.isAssignableFrom(javaField.getType())) { + collectionTypetoCreate = "new ArrayList()"; + collectionType = "List"; + } else if (Set.class.isAssignableFrom(javaField.getType())) { + collectionTypetoCreate = "new HashSet()"; + collectionType = "Set"; + } + + // if field of public modifier we can access directly + if (Modifier.isPublic(javaField.getModifiers()) && !isWildcardType) { + if (isList) { + // should initialize list + sb.append(DYNAMIC_TARGET).append(PACKAGE_SEPARATOR).append(javaField.getName()).append("= ") + .append(collectionTypetoCreate).append(JAVA_LINE_BREAK).append("}") + .append(LINE_BREAK); + if (express != null) { + if (isPacked) { + sb.append("while (input.getBytesUntilLimit() > 0) {").append(LINE_BREAK); + } + sb.append(DYNAMIC_TARGET).append(PACKAGE_SEPARATOR).append(javaField.getName()).append(".add(") + .append(express).append(")"); + if (isPacked) { + sb.append(";}").append(LINE_BREAK); + } + } + return sb.toString(); + } else if (isMap) { + sb.append(DYNAMIC_TARGET).append(PACKAGE_SEPARATOR).append(javaField.getName()) + .append("= new HashMap()").append(JAVA_LINE_BREAK).append("}") + .append(LINE_BREAK); + return sb.append(express).toString(); + } + // if date type + if (javaField.getType().equals(Date.class)) { + express = "new Date(" + express + ")"; + } + return DYNAMIC_TARGET + PACKAGE_SEPARATOR + javaField.getName() + "=" + express + LINE_BREAK; + } + String setter = "set" + capitalize(javaField.getName()); + // check method exist + try { + dynamicTargetClass.getMethod(setter, new Class[]{javaField.getType()}); + if (isList) { + sb.append(collectionType).append(" __list = ").append(collectionTypetoCreate) + .append(JAVA_LINE_BREAK); + sb.append(DYNAMIC_TARGET).append(PACKAGE_SEPARATOR).append(setter).append("(__list)") + .append(JAVA_LINE_BREAK).append("}").append(LINE_BREAK); + + if (express != null) { + if (isPacked) { + sb.append("while (input.getBytesUntilLimit() > 0) {").append(LINE_BREAK); + } + sb.append("(").append(getGetterDynamicString(protobufField, dynamicTargetClass)).append(").add(") + .append(express).append(")"); + if (isPacked) { + sb.append(";}").append(LINE_BREAK); + } + } + return sb.toString(); + } else if (isMap) { + sb.append("Map __map = new HashMap()").append(JAVA_LINE_BREAK); + sb.append(DYNAMIC_TARGET).append(PACKAGE_SEPARATOR).append(setter).append("(__map)") + .append(JAVA_LINE_BREAK).append("}").append(LINE_BREAK); + return sb + express; + } + + // fix date type + if (javaField.getType().equals(Date.class)) { + express = "new Date(" + express + ")"; + } + + return DYNAMIC_TARGET + PACKAGE_SEPARATOR + setter + "(" + express + ")\n"; + } catch (Exception e) { + //todo log + } + + if (isList) { + sb.append(collectionType).append(" __list = ").append(collectionTypetoCreate) + .append(JAVA_LINE_BREAK); + sb.append("FieldUtil.setField(").append(DYNAMIC_TARGET).append(", \"").append(javaField.getName()) + .append("\", __list)").append(JAVA_LINE_BREAK).append("}").append(LINE_BREAK); + if (express != null) { + if (isPacked) { + sb.append("while (input.getBytesUntilLimit() > 0) {").append(LINE_BREAK); + } + sb.append("(").append(getGetterDynamicString(protobufField, dynamicTargetClass)).append(").add(") + .append(express).append(")"); + if (isPacked) { + sb.append(";}").append(LINE_BREAK); + } + } + return sb.toString(); + } else if (isMap) { + sb.append("Map __map = new HashMap()").append(JAVA_LINE_BREAK); + sb.append("FieldUtil.setField(").append(DYNAMIC_TARGET).append(", \"").append(javaField.getName()) + .append("\", __map)").append(JAVA_LINE_BREAK).append("}").append(LINE_BREAK); + return sb + express; + } + + // use reflection to get value + String code = ""; + if (express != null) { + // if date type + if (javaField.getType().equals(Date.class)) { + express = "new Date(" + express + ")"; + } + + code = "FieldUtil.setField(" + DYNAMIC_TARGET + ", \"" + javaField.getName() + "\", " + express + ")" + + LINE_BREAK; + } + return code; + } + + /** + * 获取初始化 list、map 字段的动态字符串 + * + * @param protobufField + * @return + */ + public static String getInitListMapFieldDynamicString(ProtobufField protobufField, String express) { + return "FieldUtil.setField(" + DYNAMIC_TARGET + ", \"" + protobufField.getJavaField().getName() + "\", " + express + ");" + + LINE_BREAK; + } + + public static int getListSize(int order, Collection list, ProtobufFieldTypeEnum type, boolean packed) { int size = 0; if (list == null || list.isEmpty()) { @@ -687,6 +871,14 @@ public static int getMapSize(int order, Map map, com.google.protobu return size; } + /** + * 获取 object protobuf size + * + * @param order + * @param object + * @param type + * @return + */ public static int getObjectSize(int order, Object object, ProtobufFieldTypeEnum type) { int size = 0; if (object == null) { @@ -767,7 +959,6 @@ public static String toObjectType(String primitiveType) { return primitiveType; } - public static boolean isNull(Object o) { return o == null; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java index d818e7ac10..9b23f66b59 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java @@ -25,4 +25,20 @@ public class ArthasSampleRequest{ private String name; private double age; private long price; + private StatusEnum status; + private List testList; + + + enum StatusEnum{ + START(1,"开始"), + STOP(2,"结束"); + + StatusEnum(int code, String desc) { + this.code = code; + this.desc = desc; + } + + private int code; + private String desc; + } } diff --git a/arthas-grpc-server/src/main/proto/Test.proto b/arthas-grpc-server/src/main/proto/Test.proto index ec613de843..f9ffe3d740 100644 --- a/arthas-grpc-server/src/main/proto/Test.proto +++ b/arthas-grpc-server/src/main/proto/Test.proto @@ -2,6 +2,12 @@ syntax = "proto3"; package helloworld; + +enum StatusEnum { + START = 0; + STOP = 1; +} + service HelloService { rpc SayHello(HelloRequest) returns (HelloReply); } @@ -10,9 +16,11 @@ message HelloRequest { string name = 1; double age = 2; int64 price = 3; - float man = 4; + StatusEnum status = 4; + repeated string testList = 5; } + message HelloReply { string message = 1; } diff --git a/arthas-grpc-server/src/main/resources/class_template.tpl b/arthas-grpc-server/src/main/resources/class_template.tpl index c04e8ba504..dbe84651cb 100644 --- a/arthas-grpc-server/src/main/resources/class_template.tpl +++ b/arthas-grpc-server/src/main/resources/class_template.tpl @@ -44,7 +44,7 @@ public class ${className} implements ${codecClassName}<${targetProxyClassName}>, } public ${targetProxyClassName} readFrom(CodedInputStream input) throws IOException { - ${targetProxyClassName} ret = new ${targetProxyClassName}(); + ${targetProxyClassName} target = new ${targetProxyClassName}(); ${initListMapFields} @@ -78,7 +78,7 @@ public class ${className} implements ${codecClassName}<${targetProxyClassName}>, throw e; } - return ret; + return target; } From 4d960aeb196667606e991802dcd4a43c7d968c18 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Mon, 5 Aug 2024 02:37:50 +0800 Subject: [PATCH 16/48] update: add protobuf codec --- .../taobao/arthas/protobuf/ProtobufField.java | 14 ++ .../taobao/arthas/protobuf/ProtobufProxy.java | 229 +++++++++++++++++- .../arthas/protobuf/utils/FieldUtil.java | 53 +++- .../src/main/resources/class_template.tpl | 1 - 4 files changed, 278 insertions(+), 19 deletions(-) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java index 34dcedf536..e2b3b9a353 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java @@ -169,6 +169,20 @@ private String noSubParameterizedType(Field field, boolean listOrMap) { } + public boolean isEnumValueType() { + if (genericValueType != null) { + return Enum.class.isAssignableFrom(genericValueType); + } + return false; + } + + public boolean isEnumKeyType() { + if (genericKeyType != null) { + return Enum.class.isAssignableFrom(genericKeyType); + } + return false; + } + public int getOrder() { return order; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java index dcd6c49b6d..ff866bf1a5 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java @@ -9,6 +9,7 @@ import com.baidu.bjf.remoting.protobuf.code.CodedConstant; import com.baidu.bjf.remoting.protobuf.utils.ClassHelper; import com.baidu.bjf.remoting.protobuf.utils.FieldInfo; +import com.google.protobuf.WireFormat; import com.taobao.arthas.protobuf.annotation.ProtobufEnableZigZap; import com.taobao.arthas.protobuf.annotation.ProtobufClass; import com.taobao.arthas.protobuf.utils.FieldUtil; @@ -121,35 +122,239 @@ private static void processDecodeBlock() { for (ProtobufField protobufField : protobufFields) { boolean isList = protobufField.isList(); boolean isMap = protobufField.isMap(); - String e = ""; + String express = ""; if (isList) { if (FieldInfo.isListType(protobufField.getJavaField())) { - e = "new ArrayList()"; + express = "new ArrayList()"; } else if (FieldInfo.isSetType(protobufField.getJavaField())) { - e = "new HashSet()"; + express = "new HashSet()"; } } else if (isMap) { - e = "new HashMap()"; + express = "new HashMap()"; } if (isList || isMap) { - initListMapFields.append(FieldUtil.getInitListMapFieldDynamicString(protobufField, e)); + initListMapFields.append(FieldUtil.getInitListMapFieldDynamicString(protobufField, express)); } if (protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.ENUM) { String clsName = protobufField.getJavaField().getType().getCanonicalName(); if (!isList) { - String express = - "CodedConstant.getEnumValue(" + clsName + ".class, " + clsName + ".values()[0].name())"; + express = "FieldUtil.getEnumValue(" + clsName + ".class, " + clsName + ".values()[0].name())"; // add set get method - String setToField = getSetToField("ret", field.getField(), cls, express, isList, field.isMap(), - false, field.isWildcardType()); + String setToField = FieldUtil.getSetFieldDynamicString(protobufField,clazz,express); miniTemplator.setVariable("enumInitialize", setToField); miniTemplator.addBlock("enumFields"); } } } - miniTemplator.setVariable("initListMapFields", initListMapFields.toString()); + + //todo 继续看下 + + //处理字段赋值 + StringBuilder code = new StringBuilder(); + // 处理field解析 + for (ProtobufField protobufField : protobufFields) { + boolean isList = protobufField.isList(); + String t = protobufField.getProtobufFieldType().getType(); + t = FieldUtil.capitalize(t); + + boolean listTypeCheck = false; + String express; + String objectDecodeExpress = ""; + String objectDecodeExpressSuffix = ""; + + String decodeOrder = "-1"; + if (protobufField.getProtobufFieldType() != ProtobufFieldTypeEnum.DEFAULT) { + decodeOrder = FieldUtil.makeTag(protobufField.getOrder(), + protobufField.getProtobufFieldType().getInternalFieldType().getWireType()) + ""; + } else { + decodeOrder = "FieldUtil.makeTag(" + protobufField.getOrder() + ",WireFormat." + + protobufField.getProtobufFieldType().getWireFormat() + ")"; + } + miniTemplator.setVariable("decodeOrder", decodeOrder); + + // enumeration type + if (protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.ENUM) { + String clsName = ClassHelper.getInternalName(protobufField.getJavaField().getType().getCanonicalName()); + if (isList) { + if (protobufField.getGenericKeyType() != null) { + Class cls = protobufField.getGenericKeyType(); + clsName = ClassHelper.getInternalName(cls.getCanonicalName()); + } + } + express = "FieldUtil.getEnumValue(" + clsName + ".class, FieldUtil.getEnumName(" + clsName + + ".values()," + "input.read" + t + "()))"; + } else { + // here is the trick way to process BigDecimal and BigInteger + if (protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.BIGDECIMAL || protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.BIGINTEGER) { + express = "new " + protobufField.getProtobufFieldType().getJavaType() + "(input.read" + t + "())"; + } else { + express = "input.read" + t + "()"; + } + + } + + // if List type and element is object message type + if (isList && protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.OBJECT) { + if (protobufField.getGenericKeyType() != null) { + Class cls = protobufField.getGenericKeyType(); + + checkObjectType(protobufField, cls); + + code.append("codec = ProtobufProxy.create(").append(cls.getCanonicalName()).append(".class"); + String spath = "ProtobufProxy.OUTPUT_PATH.get()"; + code.append(",").append(spath); + code.append(")").append(ClassCode.JAVA_LINE_BREAK); + objectDecodeExpress = code.toString(); + code.setLength(0); + + objectDecodeExpress += "int length = input.readRawVarint32()" + ClassCode.JAVA_LINE_BREAK; + objectDecodeExpress += "final int oldLimit = input.pushLimit(length)" + ClassCode.JAVA_LINE_BREAK; + listTypeCheck = true; + express = "(" + cls.getCanonicalName() + ") codec.readFrom(input)"; + + } + } else if (protobufField.isMap()) { + + String getMapCommand = getMapCommand(protobufField); + + if (protobufField.isEnumKeyType()) { + String enumClassName = protobufField.getGenericKeyType().getCanonicalName(); + code.append("EnumHandler<").append(enumClassName).append("> keyhandler"); + code.append("= new EnumHandler"); + code.append("<").append(enumClassName).append(">() {"); + code.append(ClassCode.LINE_BREAK); + code.append("public ").append(enumClassName).append(" handle(int value) {"); + code.append(ClassCode.LINE_BREAK); + code.append("String enumName = FieldUtil.getEnumName(").append(enumClassName) + .append(".values(), value)"); + code.append(ClassCode.JAVA_LINE_BREAK); + code.append("return ").append(enumClassName).append(".valueOf(enumName)"); + code.append(ClassCode.JAVA_LINE_BREAK); + code.append("}}"); + code.append(ClassCode.JAVA_LINE_BREAK); + } + + if (protobufField.isEnumValueType()) { + String enumClassName = protobufField.getGenericValueType().getCanonicalName(); + code.append("EnumHandler<").append(enumClassName).append("> handler"); + code.append("= new EnumHandler"); + code.append("<").append(enumClassName).append(">() {"); + code.append(ClassCode.LINE_BREAK); + code.append("public ").append(enumClassName).append(" handle(int value) {"); + code.append(ClassCode.LINE_BREAK); + code.append("String enumName = FieldUtil.getEnumName(").append(enumClassName) + .append(".values(), value)"); + code.append(ClassCode.JAVA_LINE_BREAK); + code.append("return ").append(enumClassName).append(".valueOf(enumName)"); + code.append(ClassCode.JAVA_LINE_BREAK); + code.append("}}"); + code.append(ClassCode.JAVA_LINE_BREAK); + } + + objectDecodeExpress = code.toString(); + code.setLength(0); + + express = "CodedConstant.putMapValue(input, " + getMapCommand + ","; + express += FieldUtil.getMapFieldGenericParameterString(protobufField); + if (protobufField.isEnumKeyType()) { + express += ", keyhandler"; + } else { + express += ", null"; + } + if (protobufField.isEnumValueType()) { + express += ", handler"; + } else { + express += ", null"; + } + express += ")"; + + } else if (protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.OBJECT) { // if object + // message + // type + Class cls = protobufField.getJavaField().getType(); + checkObjectType(protobufField, cls); + String name = ClassHelper.getInternalName(cls.getCanonicalName()); // need + // to + // parse + // nested + // class + code.append("codec = ProtobufProxy.create(").append(name).append(".class"); + + String spath = "ProtobufProxy.OUTPUT_PATH.get()"; + code.append(",").append(spath); + code.append(")").append(ClassCode.JAVA_LINE_BREAK); + objectDecodeExpress = code.toString(); + code.setLength(0); + + objectDecodeExpress += "int length = input.readRawVarint32()" + ClassCode.JAVA_LINE_BREAK; + objectDecodeExpress += "final int oldLimit = input.pushLimit(length)" + ClassCode.JAVA_LINE_BREAK; + + listTypeCheck = true; + express = "(" + name + ") codec.readFrom(input)"; + } + + if (protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.BYTES) { + express += ".toByteArray()"; + } + + String decodeFieldSetValue = FieldUtil.getSetFieldDynamicString(protobufField,clazz,express) + FieldUtil.JAVA_LINE_BREAK; + + if (listTypeCheck) { + objectDecodeExpressSuffix += "input.checkLastTagWas(0)" + ClassCode.JAVA_LINE_BREAK; + objectDecodeExpressSuffix += "input.popLimit(oldLimit)" + ClassCode.JAVA_LINE_BREAK; + } + + String objectPackedDecodeExpress = ""; + // read packed type + if (isList) { + ProtobufFieldTypeEnum protobufFieldType = protobufField.getProtobufFieldType(); + if (protobufFieldType.isPrimitive() || protobufFieldType.isEnum()) { + code.append("if (tag == ") + .append(FieldUtil.makeTag(protobufField.getOrder(), WireFormat.WIRETYPE_LENGTH_DELIMITED)); + code.append(") {").append(ClassCode.LINE_BREAK); + + code.append("int length = input.readRawVarint32()").append(ClassCode.JAVA_LINE_BREAK); + code.append("int limit = input.pushLimit(length)").append(ClassCode.JAVA_LINE_BREAK); + + code.append(FieldUtil.getSetFieldDynamicString(protobufField,clazz,express)); + + code.append("input.popLimit(limit)").append(ClassCode.JAVA_LINE_BREAK); + + code.append("continue").append(ClassCode.JAVA_LINE_BREAK); + code.append("}").append(ClassCode.LINE_BREAK); + + objectPackedDecodeExpress = code.toString(); + } + } + miniTemplator.setVariable("objectPackedDecodeExpress", objectPackedDecodeExpress); + miniTemplator.setVariable("objectDecodeExpress", objectDecodeExpress); + miniTemplator.setVariable("objectDecodeExpressSuffix", objectDecodeExpressSuffix); + miniTemplator.setVariable("decodeFieldSetValue", decodeFieldSetValue); + miniTemplator.addBlock("decodeFields"); + } + + } + + private static void checkObjectType(ProtobufField protobufField, Class cls) { + if (FieldInfo.isPrimitiveType(cls)) { + throw new RuntimeException("invalid generic type for List as Object type, current type is '" + cls.getName() + + "' on field name '" + protobufField.getJavaField().getDeclaringClass().getName() + "#" + + protobufField.getJavaField().getName()); + } + } + + private static String getMapCommand(ProtobufField protobufField) { + String keyGeneric; + keyGeneric = protobufField.getGenericKeyType().getCanonicalName(); + + String valueGeneric; + valueGeneric = protobufField.getGenericValueType().getCanonicalName(); + String getMapCommand = "(Map<" + keyGeneric; + getMapCommand = getMapCommand + ", " + valueGeneric + ">)"; + getMapCommand = getMapCommand + FieldUtil.getGetterDynamicString(protobufField, clazz); + return getMapCommand; } @@ -159,12 +364,12 @@ private static void loadProtobufField() { ); } - public static void main(String[] args) { +// public static void main(String[] args) { // List protobufFieldList = FieldUtil.getProtobufFieldList(ArthasSampleRequest.class, false); // for (ProtobufField protobufField : protobufFieldList) { // String target = FieldUtil.getGetterDynamicString("target", protobufField.getJavaField(), ArthasSampleRequest.class, protobufField.isWildcardType()); // System.out.println(target); // } - } +// } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java index 77d4a46123..f241c32a5a 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java @@ -4,6 +4,12 @@ */ +import com.baidu.bjf.remoting.protobuf.EnumReadable; +import com.baidu.bjf.remoting.protobuf.FieldType; +import com.baidu.bjf.remoting.protobuf.utils.ClassHelper; +import com.baidu.bjf.remoting.protobuf.utils.FieldInfo; +import com.baidu.bjf.remoting.protobuf.utils.ProtobufProxyUtils; +import com.baidu.bjf.remoting.protobuf.utils.StringUtils; import com.google.protobuf.ByteString; import com.google.protobuf.CodedOutputStream; import com.google.protobuf.MapEntry; @@ -34,17 +40,19 @@ public class FieldUtil { public static final Map, ProtobufFieldTypeEnum> TYPE_MAPPER; - private static final Map PRIMITIVE_TYPE_MAPPING; + public static final Map PRIMITIVE_TYPE_MAPPING; - private static final String DYNAMIC_TARGET = "target"; + public static final String DYNAMIC_TARGET = "target"; public static final String PACKAGE_SEPARATOR = "."; - private static final String LINE_BREAK = "\n"; + public static final String LINE_BREAK = "\n"; - private static final String JAVA_LINE_BREAK = ";" + LINE_BREAK; + public static final String JAVA_LINE_BREAK = ";" + LINE_BREAK; - private static final String CODE_OUTPUT_STREAM_OBJ_NAME = "output"; + public static final String CODE_OUTPUT_STREAM_OBJ_NAME = "output"; + + public static final String WIREFORMAT_CLSNAME = com.google.protobuf.WireFormat.FieldType.class.getCanonicalName(); static { @@ -593,7 +601,7 @@ public static void writeObject(CodedOutputStream out, int order, ProtobufFieldTy } } - private static String getMapFieldGenericParameterString(ProtobufField field) { + public static String getMapFieldGenericParameterString(ProtobufField field) { String wireFormatClassName = WireFormat.FieldType.class.getCanonicalName(); ProtobufFieldTypeEnum fieldType = TYPE_MAPPER.get(field.getGenericKeyType()); String keyClass; @@ -821,6 +829,39 @@ public static String getSetFieldDynamicString(ProtobufField protobufField, Class return code; } + public static int getEnumOrdinal(Enum en) { + if (en != null) { + return en.ordinal(); + } + return -1; + } + + public static > T getEnumValue(Class enumType, String name) { + if (StringUtils.isEmpty(name)) { + return null; + } + + try { + T v = Enum.valueOf(enumType, name); + return v; + } catch (IllegalArgumentException e) { + return null; + } + } + + public static String getEnumName(Enum[] e, int value) { + if (e != null) { + int toCompareValue; + for (Enum en : e) { + toCompareValue = en.ordinal(); + if (value == toCompareValue) { + return en.name(); + } + } + } + return ""; + } + /** * 获取初始化 list、map 字段的动态字符串 * diff --git a/arthas-grpc-server/src/main/resources/class_template.tpl b/arthas-grpc-server/src/main/resources/class_template.tpl index dbe84651cb..dc12228468 100644 --- a/arthas-grpc-server/src/main/resources/class_template.tpl +++ b/arthas-grpc-server/src/main/resources/class_template.tpl @@ -64,7 +64,6 @@ public class ${className} implements ${codecClassName}<${targetProxyClassName}>, ${objectDecodeExpress} ${decodeFieldSetValue} ${objectDecodeExpressSuffix} - ${deocdeCheckNull} continue; } ${objectPackedDecodeExpress} From f72022201bda44802dfc7e1003a15dd9a0a91e29 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Tue, 6 Aug 2024 01:30:15 +0800 Subject: [PATCH 17/48] update: add protobuf codec --- .../taobao/arthas/protobuf/ProtobufField.java | 18 +++++ .../taobao/arthas/protobuf/ProtobufProxy.java | 71 ++++++++----------- .../arthas/protobuf/utils/EnumHandler.java | 13 ++++ .../arthas/protobuf/utils/FieldUtil.java | 54 ++++++++++++-- .../service/req/ArthasSampleRequest.java | 6 +- arthas-grpc-server/src/main/proto/Test.proto | 5 +- 6 files changed, 121 insertions(+), 46 deletions(-) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/EnumHandler.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java index e2b3b9a353..6203b88d5e 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java @@ -183,6 +183,24 @@ public boolean isEnumKeyType() { return false; } + public static boolean isListType(Field field) { + return List.class.isAssignableFrom(field.getType()); + } + + public static boolean isSetType(Field field) { + return Set.class.isAssignableFrom(field.getType()); + } + + public static boolean isPrimitiveType(Class c) { + if (c.isPrimitive()) { + return true; + } + if (c.getName().equals(String.class.getName())) { + return true; + } + return false; + } + public int getOrder() { return order; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java index ff866bf1a5..e90dcec6b4 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java @@ -4,11 +4,6 @@ */ -import com.baidu.bjf.remoting.protobuf.FieldType; -import com.baidu.bjf.remoting.protobuf.code.ClassCode; -import com.baidu.bjf.remoting.protobuf.code.CodedConstant; -import com.baidu.bjf.remoting.protobuf.utils.ClassHelper; -import com.baidu.bjf.remoting.protobuf.utils.FieldInfo; import com.google.protobuf.WireFormat; import com.taobao.arthas.protobuf.annotation.ProtobufEnableZigZap; import com.taobao.arthas.protobuf.annotation.ProtobufClass; @@ -124,9 +119,9 @@ private static void processDecodeBlock() { boolean isMap = protobufField.isMap(); String express = ""; if (isList) { - if (FieldInfo.isListType(protobufField.getJavaField())) { + if (ProtobufField.isListType(protobufField.getJavaField())) { express = "new ArrayList()"; - } else if (FieldInfo.isSetType(protobufField.getJavaField())) { + } else if (ProtobufField.isSetType(protobufField.getJavaField())) { express = "new HashSet()"; } } else if (isMap) { @@ -176,11 +171,11 @@ private static void processDecodeBlock() { // enumeration type if (protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.ENUM) { - String clsName = ClassHelper.getInternalName(protobufField.getJavaField().getType().getCanonicalName()); + String clsName = protobufField.getJavaField().getType().getCanonicalName(); if (isList) { if (protobufField.getGenericKeyType() != null) { Class cls = protobufField.getGenericKeyType(); - clsName = ClassHelper.getInternalName(cls.getCanonicalName()); + clsName = cls.getCanonicalName(); } } express = "FieldUtil.getEnumValue(" + clsName + ".class, FieldUtil.getEnumName(" + clsName @@ -203,14 +198,12 @@ private static void processDecodeBlock() { checkObjectType(protobufField, cls); code.append("codec = ProtobufProxy.create(").append(cls.getCanonicalName()).append(".class"); - String spath = "ProtobufProxy.OUTPUT_PATH.get()"; - code.append(",").append(spath); - code.append(")").append(ClassCode.JAVA_LINE_BREAK); + code.append(")").append(FieldUtil.JAVA_LINE_BREAK); objectDecodeExpress = code.toString(); code.setLength(0); - objectDecodeExpress += "int length = input.readRawVarint32()" + ClassCode.JAVA_LINE_BREAK; - objectDecodeExpress += "final int oldLimit = input.pushLimit(length)" + ClassCode.JAVA_LINE_BREAK; + objectDecodeExpress += "int length = input.readRawVarint32()" + FieldUtil.JAVA_LINE_BREAK; + objectDecodeExpress += "final int oldLimit = input.pushLimit(length)" + FieldUtil.JAVA_LINE_BREAK; listTypeCheck = true; express = "(" + cls.getCanonicalName() + ") codec.readFrom(input)"; @@ -224,16 +217,16 @@ private static void processDecodeBlock() { code.append("EnumHandler<").append(enumClassName).append("> keyhandler"); code.append("= new EnumHandler"); code.append("<").append(enumClassName).append(">() {"); - code.append(ClassCode.LINE_BREAK); + code.append(FieldUtil.LINE_BREAK); code.append("public ").append(enumClassName).append(" handle(int value) {"); - code.append(ClassCode.LINE_BREAK); + code.append(FieldUtil.LINE_BREAK); code.append("String enumName = FieldUtil.getEnumName(").append(enumClassName) .append(".values(), value)"); - code.append(ClassCode.JAVA_LINE_BREAK); + code.append(FieldUtil.JAVA_LINE_BREAK); code.append("return ").append(enumClassName).append(".valueOf(enumName)"); - code.append(ClassCode.JAVA_LINE_BREAK); + code.append(FieldUtil.JAVA_LINE_BREAK); code.append("}}"); - code.append(ClassCode.JAVA_LINE_BREAK); + code.append(FieldUtil.JAVA_LINE_BREAK); } if (protobufField.isEnumValueType()) { @@ -241,22 +234,22 @@ private static void processDecodeBlock() { code.append("EnumHandler<").append(enumClassName).append("> handler"); code.append("= new EnumHandler"); code.append("<").append(enumClassName).append(">() {"); - code.append(ClassCode.LINE_BREAK); + code.append(FieldUtil.LINE_BREAK); code.append("public ").append(enumClassName).append(" handle(int value) {"); - code.append(ClassCode.LINE_BREAK); + code.append(FieldUtil.LINE_BREAK); code.append("String enumName = FieldUtil.getEnumName(").append(enumClassName) .append(".values(), value)"); - code.append(ClassCode.JAVA_LINE_BREAK); + code.append(FieldUtil.JAVA_LINE_BREAK); code.append("return ").append(enumClassName).append(".valueOf(enumName)"); - code.append(ClassCode.JAVA_LINE_BREAK); + code.append(FieldUtil.JAVA_LINE_BREAK); code.append("}}"); - code.append(ClassCode.JAVA_LINE_BREAK); + code.append(FieldUtil.JAVA_LINE_BREAK); } objectDecodeExpress = code.toString(); code.setLength(0); - express = "CodedConstant.putMapValue(input, " + getMapCommand + ","; + express = "Field.putMapValue(input, " + getMapCommand + ","; express += FieldUtil.getMapFieldGenericParameterString(protobufField); if (protobufField.isEnumKeyType()) { express += ", keyhandler"; @@ -275,21 +268,19 @@ private static void processDecodeBlock() { // type Class cls = protobufField.getJavaField().getType(); checkObjectType(protobufField, cls); - String name = ClassHelper.getInternalName(cls.getCanonicalName()); // need + String name = cls.getCanonicalName(); // need // to // parse // nested // class code.append("codec = ProtobufProxy.create(").append(name).append(".class"); - String spath = "ProtobufProxy.OUTPUT_PATH.get()"; - code.append(",").append(spath); - code.append(")").append(ClassCode.JAVA_LINE_BREAK); + code.append(")").append(FieldUtil.JAVA_LINE_BREAK); objectDecodeExpress = code.toString(); code.setLength(0); - objectDecodeExpress += "int length = input.readRawVarint32()" + ClassCode.JAVA_LINE_BREAK; - objectDecodeExpress += "final int oldLimit = input.pushLimit(length)" + ClassCode.JAVA_LINE_BREAK; + objectDecodeExpress += "int length = input.readRawVarint32()" + FieldUtil.JAVA_LINE_BREAK; + objectDecodeExpress += "final int oldLimit = input.pushLimit(length)" + FieldUtil.JAVA_LINE_BREAK; listTypeCheck = true; express = "(" + name + ") codec.readFrom(input)"; @@ -302,8 +293,8 @@ private static void processDecodeBlock() { String decodeFieldSetValue = FieldUtil.getSetFieldDynamicString(protobufField,clazz,express) + FieldUtil.JAVA_LINE_BREAK; if (listTypeCheck) { - objectDecodeExpressSuffix += "input.checkLastTagWas(0)" + ClassCode.JAVA_LINE_BREAK; - objectDecodeExpressSuffix += "input.popLimit(oldLimit)" + ClassCode.JAVA_LINE_BREAK; + objectDecodeExpressSuffix += "input.checkLastTagWas(0)" + FieldUtil.JAVA_LINE_BREAK; + objectDecodeExpressSuffix += "input.popLimit(oldLimit)" + FieldUtil.JAVA_LINE_BREAK; } String objectPackedDecodeExpress = ""; @@ -313,17 +304,17 @@ private static void processDecodeBlock() { if (protobufFieldType.isPrimitive() || protobufFieldType.isEnum()) { code.append("if (tag == ") .append(FieldUtil.makeTag(protobufField.getOrder(), WireFormat.WIRETYPE_LENGTH_DELIMITED)); - code.append(") {").append(ClassCode.LINE_BREAK); + code.append(") {").append(FieldUtil.LINE_BREAK); - code.append("int length = input.readRawVarint32()").append(ClassCode.JAVA_LINE_BREAK); - code.append("int limit = input.pushLimit(length)").append(ClassCode.JAVA_LINE_BREAK); + code.append("int length = input.readRawVarint32()").append(FieldUtil.JAVA_LINE_BREAK); + code.append("int limit = input.pushLimit(length)").append(FieldUtil.JAVA_LINE_BREAK); code.append(FieldUtil.getSetFieldDynamicString(protobufField,clazz,express)); - code.append("input.popLimit(limit)").append(ClassCode.JAVA_LINE_BREAK); + code.append("input.popLimit(limit)").append(FieldUtil.JAVA_LINE_BREAK); - code.append("continue").append(ClassCode.JAVA_LINE_BREAK); - code.append("}").append(ClassCode.LINE_BREAK); + code.append("continue").append(FieldUtil.JAVA_LINE_BREAK); + code.append("}").append(FieldUtil.LINE_BREAK); objectPackedDecodeExpress = code.toString(); } @@ -338,7 +329,7 @@ private static void processDecodeBlock() { } private static void checkObjectType(ProtobufField protobufField, Class cls) { - if (FieldInfo.isPrimitiveType(cls)) { + if (ProtobufField.isPrimitiveType(cls)) { throw new RuntimeException("invalid generic type for List as Object type, current type is '" + cls.getName() + "' on field name '" + protobufField.getJavaField().getDeclaringClass().getName() + "#" + protobufField.getJavaField().getName()); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/EnumHandler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/EnumHandler.java new file mode 100644 index 0000000000..90064ad05c --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/EnumHandler.java @@ -0,0 +1,13 @@ +package com.taobao.arthas.protobuf.utils;/** + * @author: 風楪 + * @date: 2024/8/6 上午1:19 + */ + +/** + * @author: FengYe + * @date: 2024/8/6 上午1:19 + * @description: EnumHandler 处理 enum 泛型类型 + */ +public interface EnumHandler { + V handle(int value); +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java index f241c32a5a..8275957ce6 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java @@ -10,10 +10,7 @@ import com.baidu.bjf.remoting.protobuf.utils.FieldInfo; import com.baidu.bjf.remoting.protobuf.utils.ProtobufProxyUtils; import com.baidu.bjf.remoting.protobuf.utils.StringUtils; -import com.google.protobuf.ByteString; -import com.google.protobuf.CodedOutputStream; -import com.google.protobuf.MapEntry; -import com.google.protobuf.WireFormat; +import com.google.protobuf.*; import com.taobao.arthas.protobuf.ProtobufCodec; import com.taobao.arthas.protobuf.ProtobufField; import com.taobao.arthas.protobuf.ProtobufFieldTypeEnum; @@ -24,6 +21,7 @@ import java.io.IOException; +import java.lang.Enum; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.math.BigDecimal; @@ -286,6 +284,54 @@ public static void setField(Object t, String name, Object value) { } } + /** + * 读取 input put 进 map + * @param input + * @param map + * @param keyType + * @param defaultKey + * @param valueType + * @param defalutValue + * @param + * @param + * @throws IOException + */ + public static void putMapValue(CodedInputStream input, Map map, + com.google.protobuf.WireFormat.FieldType keyType, K defaultKey, + com.google.protobuf.WireFormat.FieldType valueType, V defalutValue) throws IOException { + putMapValue(input, map, keyType, defaultKey, valueType, defalutValue, null); + } + + public static void putMapValue(CodedInputStream input, Map map, + com.google.protobuf.WireFormat.FieldType keyType, K defaultKey, + com.google.protobuf.WireFormat.FieldType valueType, V defalutValue, EnumHandler handler) + throws IOException { + putMapValue(input, map, keyType, defaultKey, valueType, defalutValue, null, handler); + + } + + public static void putMapValue(CodedInputStream input, Map map, + com.google.protobuf.WireFormat.FieldType keyType, K defaultKey, + com.google.protobuf.WireFormat.FieldType valueType, V defalutValue, EnumHandler keyHandler, EnumHandler valHandler) + throws IOException { + MapEntry valuesDefaultEntry = MapEntry + . newDefaultInstance(null, keyType, defaultKey, valueType, defalutValue); + + MapEntry values = + input.readMessage(valuesDefaultEntry.getParserForType(), null); + + Object value = values.getValue(); + Object key = values.getKey(); + if (keyHandler != null) { + key = keyHandler.handle((int) key); + } + + if (valHandler != null) { + value = valHandler.handle((int) value); + } + map.put((K) key, (V) value); + } + public static String getGetterDynamicString(ProtobufField protobufField, Class dynamicTargetClass) { Field field = protobufField.getJavaField(); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java index 9b23f66b59..cef758b1d5 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java @@ -26,7 +26,7 @@ public class ArthasSampleRequest{ private double age; private long price; private StatusEnum status; - private List testList; + private List testList; enum StatusEnum{ @@ -41,4 +41,8 @@ enum StatusEnum{ private int code; private String desc; } + + static class TestClass{ + private String name; + } } diff --git a/arthas-grpc-server/src/main/proto/Test.proto b/arthas-grpc-server/src/main/proto/Test.proto index f9ffe3d740..366dd0ea3c 100644 --- a/arthas-grpc-server/src/main/proto/Test.proto +++ b/arthas-grpc-server/src/main/proto/Test.proto @@ -17,9 +17,12 @@ message HelloRequest { double age = 2; int64 price = 3; StatusEnum status = 4; - repeated string testList = 5; + repeated TestClass testList = 5; } +message TestClass{ + string name = 1; +} message HelloReply { string message = 1; From be1d85ba55c6d9b217a5b34f98e79375076131ac Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Thu, 8 Aug 2024 01:45:50 +0800 Subject: [PATCH 18/48] update: add protobuf codec --- .../src/main/java/com/taobao/arthas/Main.java | 2 ++ .../taobao/arthas/protobuf/ProtobufProxy.java | 21 +++++++------------ .../service/req/ArthasSampleRequest.java | 2 +- .../src/main/resources/class_template.tpl | 13 +----------- 4 files changed, 11 insertions(+), 27 deletions(-) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java index c00d2d95e8..7366ab07ae 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java @@ -1,5 +1,6 @@ package com.taobao.arthas; +import com.taobao.arthas.protobuf.*; import com.taobao.arthas.protobuf.utils.MiniTemplator; import java.io.IOException; @@ -15,6 +16,7 @@ public class Main { private static final String TEMPLATE_FILE = "/class_template.tpl"; public static void main(String[] args) throws IOException { + String path = Objects.requireNonNull(Main.class.getResource(TEMPLATE_FILE)).getPath(); MiniTemplator miniTemplator = new MiniTemplator(path); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java index e90dcec6b4..d78b11e8fb 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java @@ -75,6 +75,8 @@ public static ProtobufCodec create(Class clazz) { processEncodeBlock(); processDecodeBlock(); + String s = miniTemplator.generateOutput(); + return null; } @@ -83,10 +85,7 @@ private static void processImportBlock() { imports.add("java.util.*"); imports.add("java.io.IOException"); imports.add("java.lang.reflect.*"); -// imports.add("com.baidu.bjf.remoting.protobuf.FieldType"); // fix the class ambiguous of FieldType -// imports.add("com.baidu.bjf.remoting.protobuf.code.*"); -// imports.add("com.baidu.bjf.remoting.protobuf.utils.*"); -// imports.add("com.baidu.bjf.remoting.protobuf.*"); + imports.add("import com.taobao.arthas.protobuf.*"); imports.add("com.google.protobuf.*"); imports.add(clazz.getName()); for (String pkg : imports) { @@ -144,8 +143,6 @@ private static void processDecodeBlock() { } miniTemplator.setVariable("initListMapFields", initListMapFields.toString()); - //todo 继续看下 - //处理字段赋值 StringBuilder code = new StringBuilder(); // 处理field解析 @@ -249,7 +246,7 @@ private static void processDecodeBlock() { objectDecodeExpress = code.toString(); code.setLength(0); - express = "Field.putMapValue(input, " + getMapCommand + ","; + express = "FieldUtil.putMapValue(input, " + getMapCommand + ","; express += FieldUtil.getMapFieldGenericParameterString(protobufField); if (protobufField.isEnumKeyType()) { express += ", keyhandler"; @@ -355,12 +352,8 @@ private static void loadProtobufField() { ); } -// public static void main(String[] args) { -// List protobufFieldList = FieldUtil.getProtobufFieldList(ArthasSampleRequest.class, false); -// for (ProtobufField protobufField : protobufFieldList) { -// String target = FieldUtil.getGetterDynamicString("target", protobufField.getJavaField(), ArthasSampleRequest.class, protobufField.isWildcardType()); -// System.out.println(target); -// } -// } + public static void main(String[] args) { + ProtobufCodec protobufCodec = ProtobufProxy.create(ArthasSampleRequest.class); + } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java index cef758b1d5..0f037d78e2 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java @@ -4,8 +4,8 @@ */ import com.baidu.bjf.remoting.protobuf.annotation.Protobuf; -import com.baidu.bjf.remoting.protobuf.annotation.ProtobufClass; import com.google.protobuf.*; +import com.taobao.arthas.protobuf.annotation.ProtobufClass; import com.taobao.arthas.protobuf.annotation.ProtobufCustomizedField; import com.taobao.arthas.protobuf.annotation.ProtobufIgnore; diff --git a/arthas-grpc-server/src/main/resources/class_template.tpl b/arthas-grpc-server/src/main/resources/class_template.tpl index dc12228468..9815d169ba 100644 --- a/arthas-grpc-server/src/main/resources/class_template.tpl +++ b/arthas-grpc-server/src/main/resources/class_template.tpl @@ -7,7 +7,6 @@ import ${importBlock}; public class ${className} implements ${codecClassName}<${targetProxyClassName}>, Serializable { public static final long serialVersionUID = 1L; - private ${descriptorClsName} descriptor; public byte[] encode(${targetProxyClassName} target) throws IOException { CodedOutputStreamCache outputCache = CodedOutputStreamCache.get(); @@ -79,17 +78,7 @@ public class ${className} implements ${codecClassName}<${targetProxyClassName}>, return target; - } - - - public com.google.protobuf.Descriptors.Descriptor getDescriptor() throws IOException { - if (this.descriptor != null) { - return this.descriptor; - } - com.google.protobuf.Descriptors.Descriptor descriptor = - CodedConstant.getDescriptor(${targetProxyClassName}.class); - return (this.descriptor = descriptor); - } + } } \ No newline at end of file From 5871052bbbbb121e158ac62368f83bc012dd382a Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Fri, 9 Aug 2024 02:27:32 +0800 Subject: [PATCH 19/48] update: add protobuf codec --- .../taobao/arthas/protobuf/ProtobufCodec.java | 8 +- .../taobao/arthas/protobuf/ProtobufProxy.java | 35 ++- .../arthas/protobuf/utils/FieldUtil.java | 40 ++- .../protobuf/utils/ProtoBufClassCompiler.java | 292 ++++++++++++++++++ .../service/req/ArthasSampleRequest.java | 1 + .../src/main/resources/class_template.tpl | 6 +- 6 files changed, 369 insertions(+), 13 deletions(-) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/ProtoBufClassCompiler.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufCodec.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufCodec.java index 38648013da..101b07e86e 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufCodec.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufCodec.java @@ -3,6 +3,8 @@ * @date: 2024/7/17 下午9:44 */ +import com.google.protobuf.CodedInputStream; + import java.io.IOException; /** @@ -11,9 +13,11 @@ * @description: Codec */ public interface ProtobufCodec { - byte[] encode(T t); + byte[] encode(T t) throws IOException; - T decode(byte[] bytes); + T decode(byte[] bytes) throws IOException; int size(T t) throws IOException; + + T readFrom(CodedInputStream intput) throws IOException; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java index d78b11e8fb..26a72c37cc 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java @@ -4,14 +4,20 @@ */ +import com.baidu.bjf.remoting.protobuf.Codec; +import com.baidu.bjf.remoting.protobuf.utils.ClassHelper; +import com.baidu.bjf.remoting.protobuf.utils.StringUtils; import com.google.protobuf.WireFormat; import com.taobao.arthas.protobuf.annotation.ProtobufEnableZigZap; import com.taobao.arthas.protobuf.annotation.ProtobufClass; import com.taobao.arthas.protobuf.utils.FieldUtil; import com.taobao.arthas.protobuf.utils.MiniTemplator; +import com.taobao.arthas.protobuf.utils.ProtoBufClassCompiler; import com.taobao.arthas.service.req.ArthasSampleRequest; import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -50,7 +56,7 @@ public ProtobufCodec getCodecCacheSide(Class clazz) { } } - public static ProtobufCodec create(Class clazz) { + public static ProtobufCodec create(Class clazz) { Objects.requireNonNull(clazz); if (clazz.getAnnotation(ProtobufClass.class) == null) { throw new IllegalArgumentException("class is not annotated with @ProtobufClass"); @@ -69,13 +75,31 @@ public static ProtobufCodec create(Class clazz) { processImportBlock(); - miniTemplator.setVariable("className", clazz.getName() + "$$ProxyClass"); + miniTemplator.setVariable("className", FieldUtil.getClassName(clazz) + "$$ProxyClass"); miniTemplator.setVariable("codecClassName", ProtobufCodec.class.getName()); miniTemplator.setVariable("targetProxyClassName", clazz.getName()); processEncodeBlock(); processDecodeBlock(); - String s = miniTemplator.generateOutput(); + String code = miniTemplator.generateOutput(); + + ProtoBufClassCompiler protoBufClassCompiler = new ProtoBufClassCompiler(ProtoBufClassCompiler.class.getClassLoader()); + String fullClassName = FieldUtil.getFullClassName(clazz)+"$$ProxyClass"; + Class newClass = protoBufClassCompiler.compile(fullClassName, code, clazz.getClassLoader()); + + try { + ProtobufCodec newInstance = (ProtobufCodec)newClass.getDeclaredConstructor(new Class[0]).newInstance(new Object[0]); + + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + return null; } @@ -85,7 +109,9 @@ private static void processImportBlock() { imports.add("java.util.*"); imports.add("java.io.IOException"); imports.add("java.lang.reflect.*"); - imports.add("import com.taobao.arthas.protobuf.*"); + imports.add("com.taobao.arthas.protobuf.*"); + imports.add("com.taobao.arthas.protobuf.utils.*"); + imports.add("com.taobao.arthas.protobuf.annotation.*"); imports.add("com.google.protobuf.*"); imports.add(clazz.getName()); for (String pkg : imports) { @@ -345,7 +371,6 @@ private static String getMapCommand(ProtobufField protobufField) { return getMapCommand; } - private static void loadProtobufField() { protobufFields = FieldUtil.getProtobufFieldList(clazz, clazz.getAnnotation(ProtobufEnableZigZap.class) != null diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java index 8275957ce6..309110b31c 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java @@ -286,6 +286,7 @@ public static void setField(Object t, String name, Object value) { /** * 读取 input put 进 map + * * @param input * @param map * @param keyType @@ -315,7 +316,7 @@ public static void putMapValue(CodedInputStream input, Map map, com.google.protobuf.WireFormat.FieldType valueType, V defalutValue, EnumHandler keyHandler, EnumHandler valHandler) throws IOException { MapEntry valuesDefaultEntry = MapEntry - . newDefaultInstance(null, keyType, defaultKey, valueType, defalutValue); + .newDefaultInstance(null, keyType, defaultKey, valueType, defalutValue); MapEntry values = input.readMessage(valuesDefaultEntry.getParserForType(), null); @@ -404,7 +405,7 @@ public static String getSizeDynamicString(ProtobufField field) { javaType = capitalize(javaType); dynamicFieldName = dynamicFieldName + protobufFieldType.getToPrimitiveType(); //todo check 感觉上面这个有点问题,测试的时候看下 - return "com.google.protobuf.CodedOutputStream.compute" + javaType + "Size(" + order + "," + dynamicFieldName + ")" + return "com.google.protobuf.CodedOutputStream.compute" + javaType + "Size(" + order + "," + dynamicFieldName + ");" + LINE_BREAK; } @@ -423,7 +424,7 @@ public static String getWriteByteDynamicString(ProtobufField protobufField) { if (protobufField.isList()) { String typeString = protobufFieldType.getType().toUpperCase(); - sb.append("Field.writeList(").append(CODE_OUTPUT_STREAM_OBJ_NAME).append(","); + sb.append("FieldUtil.writeList(").append(CODE_OUTPUT_STREAM_OBJ_NAME).append(","); sb.append(order).append(",").append(ProtobufFieldTypeEnum.class.getName()).append(".").append(typeString); sb.append(",").append(dynamicFieldName).append(",").append(Boolean.valueOf(protobufField.isPacked())).append(")") .append(JAVA_LINE_BREAK).append("}").append(LINE_BREAK); @@ -1046,6 +1047,39 @@ public static String toObjectType(String primitiveType) { return primitiveType; } + public static String getFullClassName(Class cls) { + if (StringUtils.isEmpty(getPackage(cls))) { + return getClassName(cls); + } + + return getPackage(cls) + ClassHelper.PACKAGE_SEPARATOR + getClassName(cls); + } + + public static String getPackage(Class cls) { + Package pkg = cls.getPackage(); + // maybe null if package is blank or dynamic load class + if (pkg == null) { + String fullName = cls.getName(); + int index = fullName.lastIndexOf(PACKAGE_SEPARATOR); + if (index != -1) { + return fullName.substring(0, index); + } + return ""; + } + + return pkg.getName(); + } + + public static String getClassName(Class cls) { + if (cls.isMemberClass()) { + String name = cls.getName(); + name = StringUtils.substringAfterLast(name, PACKAGE_SEPARATOR); + return name; + } + + return cls.getSimpleName(); + } + public static boolean isNull(Object o) { return o == null; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/ProtoBufClassCompiler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/ProtoBufClassCompiler.java new file mode 100644 index 0000000000..5f60da3749 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/ProtoBufClassCompiler.java @@ -0,0 +1,292 @@ +package com.taobao.arthas.protobuf.utils;/** + * @author: 風楪 + * @date: 2024/8/9 01:21 + */ + +import com.baidu.bjf.remoting.protobuf.utils.ClassHelper; +import com.baidu.bjf.remoting.protobuf.utils.compiler.ClassUtils; +import com.baidu.bjf.remoting.protobuf.utils.compiler.JdkCompiler; +import com.taobao.arthas.protobuf.ProtobufProxy; + +import javax.tools.*; +import java.io.*; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.*; + +/** + * @author: FengYe + * @date: 2024/8/9 01:21 + * @description: ProtoBufClassCompiler + */ +public class ProtoBufClassCompiler { + + private final Map fileObjects = new HashMap(); + + private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + + private final ClassLoaderImpl classLoader; + + private final JavaFileManagerImpl javaFileManager; + + private volatile List options; + + private static final String jdkVersion = "1.8"; + + + public ProtoBufClassCompiler(final ClassLoader loader) { + options = new ArrayList(); + options.add("-source"); + options.add(jdkVersion); + options.add("-target"); + options.add(jdkVersion); + + DiagnosticCollector diagnosticCollector = new DiagnosticCollector(); + StandardJavaFileManager manager = + compiler.getStandardFileManager(diagnosticCollector, null, StandardCharsets.UTF_8); + + classLoader = AccessController.doPrivileged(new PrivilegedAction() { + public ClassLoaderImpl run() { + return new ClassLoaderImpl(loader); + } + }); + + javaFileManager = new JavaFileManagerImpl(manager, classLoader); + } + + public synchronized Class compile(String className, String code, ClassLoader classLoader) { + FileOutputStream fos = null; + code = code.trim(); + try { + return Class.forName(className, true, classLoader); + } catch (ClassNotFoundException e) { + if (!code.endsWith("}")) { + throw new IllegalStateException("The java code not endsWith \"}\", code: \n" + code + "\n"); + } + try { + return doCompile(className, code); + } catch (RuntimeException t) { + throw t; + } catch (Throwable t) { + throw new IllegalStateException("Failed to compile class, cause: " + t.getMessage() + ", class: " + + className + ", code: \n" + code + "\n, stack: " + ClassUtils.toString(t)); + } + } + } + + public synchronized Class doCompile(String name, String sourceCode) throws Throwable { + int i = name.lastIndexOf('.'); + String packageName = i < 0 ? "" : name.substring(0, i); + String className = i < 0 ? name : name.substring(i + 1); + JavaFileObjectImpl javaFileObject = new JavaFileObjectImpl(className, sourceCode); + fileObjects.put(new URI(StandardLocation.SOURCE_PATH.getName() + "/" + packageName + "/" + className + ".java"), javaFileObject); + javaFileManager.putFileForInput(StandardLocation.SOURCE_PATH, packageName, + className + ClassUtils.JAVA_EXTENSION, javaFileObject); + + DiagnosticCollector diagnosticCollector = new DiagnosticCollector(); + Boolean result = compiler.getTask(null, javaFileManager, diagnosticCollector, options, null, + Arrays.asList(new JavaFileObject[]{javaFileObject})).call(); + if (result == null || !result.booleanValue()) { + throw new IllegalStateException( + "Compilation failed. class: " + name + ", diagnostics: " + diagnosticCollector.getDiagnostics()); + } + + Class retClass = classLoader.loadClass(name); + + return retClass; + } + + private static final class JavaFileManagerImpl extends ForwardingJavaFileManager { + + private final ClassLoaderImpl classLoader; + + private final Map fileObjects = new HashMap(); + + public JavaFileManagerImpl(JavaFileManager fileManager, ClassLoaderImpl classLoader) { + super(fileManager); + this.classLoader = classLoader; + } + + @Override + public FileObject getFileForInput(Location location, String packageName, String relativeName) + throws IOException { + FileObject o = fileObjects.get(uri(location, packageName, relativeName)); + if (o != null) { + return o; + } + return super.getFileForInput(location, packageName, relativeName); + } + + public void putFileForInput(StandardLocation location, String packageName, String relativeName, + JavaFileObject file) { + fileObjects.put(uri(location, packageName, relativeName), file); + } + + private URI uri(Location location, String packageName, String relativeName) { + return ClassUtils.toURI(location.getName() + '/' + packageName + '/' + relativeName); + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String qualifiedName, JavaFileObject.Kind kind, + FileObject outputFile) throws IOException { + JavaFileObject file = new JavaFileObjectImpl(qualifiedName, kind); + classLoader.add(qualifiedName, file); + return file; + } + + @Override + public ClassLoader getClassLoader(JavaFileManager.Location location) { + return classLoader; + } + + @Override + public String inferBinaryName(Location loc, JavaFileObject file) { + if (file instanceof JavaFileObjectImpl) { + return file.getName(); + } + return super.inferBinaryName(loc, file); + } + + @Override + public Iterable list(Location location, String packageName, Set kinds, boolean recurse) + throws IOException { + Iterable result = super.list(location, packageName, kinds, recurse); + + ArrayList files = new ArrayList(); + + if (location == StandardLocation.CLASS_PATH && kinds.contains(JavaFileObject.Kind.CLASS)) { + for (JavaFileObject file : fileObjects.values()) { + if (file.getKind() == JavaFileObject.Kind.CLASS && file.getName().startsWith(packageName)) { + files.add(file); + } + } + + files.addAll(classLoader.files()); + } else if (location == StandardLocation.SOURCE_PATH && kinds.contains(JavaFileObject.Kind.SOURCE)) { + for (JavaFileObject file : fileObjects.values()) { + if (file.getKind() == JavaFileObject.Kind.SOURCE && file.getName().startsWith(packageName)) { + files.add(file); + } + } + } + + for (JavaFileObject file : result) { + files.add(file); + } + + return files; + } + } + + + private final class ClassLoaderImpl extends ClassLoader { + + private final Map classes = new HashMap(); + + ClassLoaderImpl(final ClassLoader parentClassLoader) { + super(parentClassLoader); + } + + Collection files() { + return Collections.unmodifiableCollection(classes.values()); + } + + public byte[] loadClassBytes(final String qualifiedClassName) { + JavaFileObject file = classes.get(qualifiedClassName); + if (file != null) { + byte[] bytes = ((JavaFileObjectImpl) file).getByteCode(); + return bytes; + } + return null; + } + + @Override + protected Class findClass(final String qualifiedClassName) throws ClassNotFoundException { + JavaFileObject file = classes.get(qualifiedClassName); + if (file != null) { + byte[] bytes = ((JavaFileObjectImpl) file).getByteCode(); + return defineClass(qualifiedClassName, bytes, 0, bytes.length); + } + try { + return ClassHelper.forNameWithCallerClassLoader(qualifiedClassName, getClass()); + } catch (ClassNotFoundException nf) { + return super.findClass(qualifiedClassName); + } + } + + void add(final String qualifiedClassName, final JavaFileObject javaFile) { + classes.put(qualifiedClassName, javaFile); + } + + @Override + protected synchronized Class loadClass(final String name, final boolean resolve) + throws ClassNotFoundException { + return super.loadClass(name, resolve); + } + + @Override + public InputStream getResourceAsStream(final String name) { + if (name.endsWith(ClassUtils.CLASS_EXTENSION)) { + String qualifiedClassName = + name.substring(0, name.length() - ClassUtils.CLASS_EXTENSION.length()).replace('/', '.'); + JavaFileObjectImpl file = (JavaFileObjectImpl) classes.get(qualifiedClassName); + if (file != null) { + return new ByteArrayInputStream(file.getByteCode()); + } + } + return super.getResourceAsStream(name); + } + } + + + private static final class JavaFileObjectImpl extends SimpleJavaFileObject { + + private ByteArrayOutputStream bytecode; + + private final CharSequence source; + + public JavaFileObjectImpl(final String baseName, final CharSequence source) throws URISyntaxException { + super(new URI(baseName + ClassUtils.JAVA_EXTENSION), Kind.SOURCE); + this.source = source; + } + + JavaFileObjectImpl(final String name, final Kind kind) { + super(ClassUtils.toURI(name), kind); + source = null; + } + + public JavaFileObjectImpl(URI uri, Kind kind) { + super(uri, kind); + source = null; + } + + @Override + public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws UnsupportedOperationException { + if (source == null) { + throw new UnsupportedOperationException("source == null"); + } + return source; + } + + @Override + public InputStream openInputStream() { + return new ByteArrayInputStream(getByteCode()); + } + + @Override + public OutputStream openOutputStream() { + return bytecode = new ByteArrayOutputStream(); + } + + public byte[] getByteCode() { + if (bytecode == null) { + return null; + } + return bytecode.toByteArray(); + } + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java index 0f037d78e2..543b3bcebf 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java @@ -20,6 +20,7 @@ * @description: ArthasSampleRequest */ @ProtobufClass +@com.baidu.bjf.remoting.protobuf.annotation.ProtobufClass public class ArthasSampleRequest{ private String name; diff --git a/arthas-grpc-server/src/main/resources/class_template.tpl b/arthas-grpc-server/src/main/resources/class_template.tpl index 9815d169ba..a2ff4bc984 100644 --- a/arthas-grpc-server/src/main/resources/class_template.tpl +++ b/arthas-grpc-server/src/main/resources/class_template.tpl @@ -14,7 +14,7 @@ public class ${className} implements ${codecClassName}<${targetProxyClassName}>, return outputCache.getData(); } - public void doWriteTo(${targetProxyClassName} t, CodedOutputStream output) + public void doWriteTo(${targetProxyClassName} target, CodedOutputStream output) throws IOException { ${dynamicFieldType} ${dynamicFieldName} = null; @@ -34,7 +34,7 @@ public class ${className} implements ${codecClassName}<${targetProxyClassName}>, int size = 0; ${dynamicFieldType} ${dynamicFieldName} = null; - if (!CodedConstant.isNull(${dynamicFieldGetter})) { + if (!FieldUtil.isNull(${dynamicFieldGetter})) { ${dynamicFieldName} = ${dynamicFieldGetter}; size += ${sizeDynamicString} } @@ -52,7 +52,7 @@ public class ${className} implements ${codecClassName}<${targetProxyClassName}>, try { boolean done = false; - Codec codec = null; + ProtobufCodec codec = null; while (!done) { int tag = input.readTag(); if (tag == 0) { From 500e64a91b2b0820a34816cb0d081b8cb509f353 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Sat, 10 Aug 2024 03:31:22 +0800 Subject: [PATCH 20/48] update: add protobuf codec --- .../com/taobao/arthas/h2/Http2Handler.java | 13 +++++++++- .../taobao/arthas/protobuf/ProtobufProxy.java | 24 +++++++++---------- .../service/req/ArthasSampleRequest.java | 20 ++++++++++------ 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java index 74f579e80e..5e0a11bff6 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java @@ -6,6 +6,7 @@ import com.baidu.bjf.remoting.protobuf.Codec; import com.baidu.bjf.remoting.protobuf.ProtobufProxy; import com.google.protobuf.CodedInputStream; +import com.taobao.arthas.protobuf.ProtobufCodec; import com.taobao.arthas.service.ArthasSampleService; import com.taobao.arthas.service.req.ArthasSampleRequest; import io.netty.buffer.ByteBuf; @@ -75,9 +76,19 @@ protected void channelRead0(ChannelHandlerContext ctx, Http2Frame frame) throws byte[] byteArray = new byte[byteBuf.readableBytes()]; byteBuf.readBytes(byteArray); - Codec codec = ProtobufProxy.create(ArthasSampleRequest.class); + Codec codec = ProtobufProxy.create(ArthasSampleRequest.class,true); + ProtobufCodec protobufCodec = com.taobao.arthas.protobuf.ProtobufProxy.create(ArthasSampleRequest.class); + ArthasSampleRequest decode = codec.decode(byteArray); + ArthasSampleRequest decode1 = protobufCodec.decode(byteArray); + + System.out.println(codec.decode(protobufCodec.encode(decode1))); + System.out.println(protobufCodec.decode(protobufCodec.encode(decode))); + System.out.println(codec.decode(codec.encode(decode1))); + System.out.println(protobufCodec.decode(codec.encode(decode1))); + System.out.println(decode); + System.out.println(decode1); } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java index 26a72c37cc..3717130274 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java @@ -59,7 +59,7 @@ public ProtobufCodec getCodecCacheSide(Class clazz) { public static ProtobufCodec create(Class clazz) { Objects.requireNonNull(clazz); if (clazz.getAnnotation(ProtobufClass.class) == null) { - throw new IllegalArgumentException("class is not annotated with @ProtobufClass"); + throw new IllegalArgumentException(clazz + "class is not annotated with @ProtobufClass"); } ProtobufProxy.clazz = clazz; loadProtobufField(); @@ -77,19 +77,20 @@ public static ProtobufCodec create(Class clazz) { miniTemplator.setVariable("className", FieldUtil.getClassName(clazz) + "$$ProxyClass"); miniTemplator.setVariable("codecClassName", ProtobufCodec.class.getName()); - miniTemplator.setVariable("targetProxyClassName", clazz.getName()); + miniTemplator.setVariable("targetProxyClassName", clazz.getCanonicalName()); processEncodeBlock(); processDecodeBlock(); String code = miniTemplator.generateOutput(); + System.out.println(code); ProtoBufClassCompiler protoBufClassCompiler = new ProtoBufClassCompiler(ProtoBufClassCompiler.class.getClassLoader()); - String fullClassName = FieldUtil.getFullClassName(clazz)+"$$ProxyClass"; + String fullClassName = FieldUtil.getFullClassName(clazz) + "$$ProxyClass"; Class newClass = protoBufClassCompiler.compile(fullClassName, code, clazz.getClassLoader()); try { - ProtobufCodec newInstance = (ProtobufCodec)newClass.getDeclaredConstructor(new Class[0]).newInstance(new Object[0]); - + ProtobufCodec newInstance = (ProtobufCodec) newClass.getDeclaredConstructor(new Class[0]).newInstance(new Object[0]); + return newInstance; } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { @@ -99,9 +100,6 @@ public static ProtobufCodec create(Class clazz) { } catch (NoSuchMethodException e) { throw new RuntimeException(e); } - - - return null; } private static void processImportBlock() { @@ -113,7 +111,7 @@ private static void processImportBlock() { imports.add("com.taobao.arthas.protobuf.utils.*"); imports.add("com.taobao.arthas.protobuf.annotation.*"); imports.add("com.google.protobuf.*"); - imports.add(clazz.getName()); + imports.add(clazz.getCanonicalName()); for (String pkg : imports) { miniTemplator.setVariable("importBlock", pkg); miniTemplator.addBlock("imports"); @@ -161,7 +159,7 @@ private static void processDecodeBlock() { if (!isList) { express = "FieldUtil.getEnumValue(" + clsName + ".class, " + clsName + ".values()[0].name())"; // add set get method - String setToField = FieldUtil.getSetFieldDynamicString(protobufField,clazz,express); + String setToField = FieldUtil.getSetFieldDynamicString(protobufField, clazz, express); miniTemplator.setVariable("enumInitialize", setToField); miniTemplator.addBlock("enumFields"); } @@ -206,7 +204,7 @@ private static void processDecodeBlock() { } else { // here is the trick way to process BigDecimal and BigInteger if (protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.BIGDECIMAL || protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.BIGINTEGER) { - express = "new " + protobufField.getProtobufFieldType().getJavaType() + "(input.read" + t + "())"; + express = "new " + protobufField.getProtobufFieldType().getJavaType() + "(input.read" + t + "())"; } else { express = "input.read" + t + "()"; } @@ -313,7 +311,7 @@ private static void processDecodeBlock() { express += ".toByteArray()"; } - String decodeFieldSetValue = FieldUtil.getSetFieldDynamicString(protobufField,clazz,express) + FieldUtil.JAVA_LINE_BREAK; + String decodeFieldSetValue = FieldUtil.getSetFieldDynamicString(protobufField, clazz, express) + FieldUtil.JAVA_LINE_BREAK; if (listTypeCheck) { objectDecodeExpressSuffix += "input.checkLastTagWas(0)" + FieldUtil.JAVA_LINE_BREAK; @@ -332,7 +330,7 @@ private static void processDecodeBlock() { code.append("int length = input.readRawVarint32()").append(FieldUtil.JAVA_LINE_BREAK); code.append("int limit = input.pushLimit(length)").append(FieldUtil.JAVA_LINE_BREAK); - code.append(FieldUtil.getSetFieldDynamicString(protobufField,clazz,express)); + code.append(FieldUtil.getSetFieldDynamicString(protobufField, clazz, express)); code.append("input.popLimit(limit)").append(FieldUtil.JAVA_LINE_BREAK); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java index 543b3bcebf..95128e9ffa 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java @@ -8,6 +8,7 @@ import com.taobao.arthas.protobuf.annotation.ProtobufClass; import com.taobao.arthas.protobuf.annotation.ProtobufCustomizedField; import com.taobao.arthas.protobuf.annotation.ProtobufIgnore; +import lombok.ToString; import java.io.IOException; import java.nio.ByteBuffer; @@ -19,9 +20,11 @@ * @date: 2024/7/14 上午4:28 * @description: ArthasSampleRequest */ -@ProtobufClass + @com.baidu.bjf.remoting.protobuf.annotation.ProtobufClass -public class ArthasSampleRequest{ +@ProtobufClass +@ToString +public class ArthasSampleRequest { private String name; private double age; @@ -29,10 +32,11 @@ public class ArthasSampleRequest{ private StatusEnum status; private List testList; - - enum StatusEnum{ - START(1,"开始"), - STOP(2,"结束"); + @com.baidu.bjf.remoting.protobuf.annotation.ProtobufClass + @ProtobufClass + public enum StatusEnum { + START(1, "开始"), + STOP(2, "结束"); StatusEnum(int code, String desc) { this.code = code; @@ -43,7 +47,9 @@ enum StatusEnum{ private String desc; } - static class TestClass{ + @com.baidu.bjf.remoting.protobuf.annotation.ProtobufClass + @ProtobufClass + public static class TestClass { private String name; } } From 2248532f1786dda2419446e1437f40e73eaf646f Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Mon, 12 Aug 2024 01:47:22 +0800 Subject: [PATCH 21/48] update: add grpc handler --- .../com/taobao/arthas/h2/Http2Handler.java | 123 +++++++++--------- .../taobao/arthas/protobuf/ProtobufProxy.java | 1 - .../service/req/ArthasSampleRequest.java | 40 ++++++ .../service/res/ArthasSampleResponse.java | 24 ++++ .../proto/{Test.proto => arthasSample.proto} | 10 +- 5 files changed, 129 insertions(+), 69 deletions(-) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/service/res/ArthasSampleResponse.java rename arthas-grpc-server/src/main/proto/{Test.proto => arthasSample.proto} (59%) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java index 5e0a11bff6..407b955aa1 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java @@ -4,11 +4,11 @@ */ import com.baidu.bjf.remoting.protobuf.Codec; -import com.baidu.bjf.remoting.protobuf.ProtobufProxy; -import com.google.protobuf.CodedInputStream; import com.taobao.arthas.protobuf.ProtobufCodec; +import com.taobao.arthas.protobuf.ProtobufProxy; import com.taobao.arthas.service.ArthasSampleService; import com.taobao.arthas.service.req.ArthasSampleRequest; +import com.taobao.arthas.service.res.ArthasSampleResponse; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; import io.netty.channel.ChannelHandlerContext; @@ -37,82 +37,79 @@ public class Http2Handler extends SimpleChannelInboundHandler { @Override protected void channelRead0(ChannelHandlerContext ctx, Http2Frame frame) throws IOException { if (frame instanceof Http2HeadersFrame) { - System.out.println("header"); - Http2HeadersFrame headersFrame = (Http2HeadersFrame) frame; - int id = headersFrame.stream().id(); - dataBuffer.put(id, ctx.alloc().buffer()); - - System.out.println("Received headers: " + headersFrame.headers()); - System.out.println(headersFrame.headers().path().toString()); + handleGrpcRequest((Http2HeadersFrame) frame, ctx); + } else if (frame instanceof Http2DataFrame) { + handleGrpcData((Http2DataFrame) frame,ctx); + } + } - // Respond to the client with headers - Http2Headers responseHeaders = new DefaultHttp2Headers() - .status("200") - .set("content-type", "text/plain; charset=UTF-8"); + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.printStackTrace(); + ctx.close(); + } - // 创建响应数据 - byte[] content = "Hello, HTTP/2 World!".getBytes(); - Http2DataFrame dataFrame = new DefaultHttp2DataFrame(ctx.alloc().buffer().writeBytes(content), true); + private void handleGrpcRequest(Http2HeadersFrame headersFrame, ChannelHandlerContext ctx) { + int id = headersFrame.stream().id(); + dataBuffer.put(id, ctx.alloc().buffer()); - // 发送响应头 - ctx.write(new DefaultHttp2HeadersFrame(responseHeaders).stream(headersFrame.stream())); + System.out.println("Received headers: " + headersFrame.headers()); + System.out.println(headersFrame.headers().path().toString()); - // 发送响应数据 - ctx.writeAndFlush(dataFrame.stream(headersFrame.stream())); + // Respond to the client with headers + Http2Headers responseHeaders = new DefaultHttp2Headers() + .status("200") + .set("content-type", "text/plain; charset=UTF-8"); - } else if (frame instanceof Http2DataFrame) { - Http2DataFrame dataFrame = (Http2DataFrame) frame; - byte[] data = new byte[dataFrame.content().readableBytes()]; - System.out.println(dataFrame.content().readableBytes()); - System.out.println(dataFrame.isEndStream()); - dataFrame.content().readBytes(data); + // 创建响应数据 + byte[] content = "Hello, HTTP/2 World!".getBytes(); + Http2DataFrame dataFrame = new DefaultHttp2DataFrame(ctx.alloc().buffer().writeBytes(content), true); -// Decompress if needed - byte[] decompressedData = decompressGzip(data); - ByteBuf byteBuf = dataBuffer.get(dataFrame.stream().id()); - byteBuf.writeBytes(decompressedData); - if (dataFrame.isEndStream()) { - byteBuf.readBytes(5); + // 发送响应头 + ctx.write(new DefaultHttp2HeadersFrame(responseHeaders).stream(headersFrame.stream())); - byte[] byteArray = new byte[byteBuf.readableBytes()]; - byteBuf.readBytes(byteArray); - Codec codec = ProtobufProxy.create(ArthasSampleRequest.class,true); - ProtobufCodec protobufCodec = com.taobao.arthas.protobuf.ProtobufProxy.create(ArthasSampleRequest.class); + // 发送响应数据 + ctx.writeAndFlush(dataFrame.stream(headersFrame.stream())); + } - ArthasSampleRequest decode = codec.decode(byteArray); - ArthasSampleRequest decode1 = protobufCodec.decode(byteArray); + private void handleGrpcData(Http2DataFrame dataFrame,ChannelHandlerContext ctx) throws IOException { + byte[] data = new byte[dataFrame.content().readableBytes()]; + System.out.println(dataFrame.content().readableBytes()); + System.out.println(dataFrame.isEndStream()); + dataFrame.content().readBytes(data); - System.out.println(codec.decode(protobufCodec.encode(decode1))); - System.out.println(protobufCodec.decode(protobufCodec.encode(decode))); - System.out.println(codec.decode(codec.encode(decode1))); - System.out.println(protobufCodec.decode(codec.encode(decode1))); +// Decompress if needed + byte[] decompressedData = decompressGzip(data); + ByteBuf byteBuf = dataBuffer.get(dataFrame.stream().id()); + byteBuf.writeBytes(decompressedData); - System.out.println(decode); - System.out.println(decode1); + byte[] responseData = null; + if (dataFrame.isEndStream()) { + byteBuf.readBytes(5); - } + byte[] byteArray = new byte[byteBuf.readableBytes()]; + byteBuf.readBytes(byteArray); + ProtobufCodec requestCodec = ProtobufProxy.create(ArthasSampleRequest.class); + ProtobufCodec responseCodec = ProtobufProxy.create(ArthasSampleResponse.class); + ArthasSampleRequest decode = requestCodec.decode(byteArray); -// -// byte[] responseData = "hello".getBytes(); -// -// // Send response -// Http2Headers responseHeaders = new DefaultHttp2Headers() -// .status("200") -// .set("content-type", "application/grpc"); -// ctx.write(new DefaultHttp2HeadersFrame(responseHeaders)); -// ctx.writeAndFlush(new DefaultHttp2DataFrame(ctx.alloc().buffer().writeBytes(responseData)).stream(dataFrame.stream())); -// System.out.println("finish"); -// if (((Http2DataFrame) frame).isEndStream()) { -// dataFrame.release(); -// } + ArthasSampleResponse arthasSampleResponse = new ArthasSampleResponse(); + arthasSampleResponse.setMessage(decode.getName()); + responseData = responseCodec.encode(arthasSampleResponse); } - } - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); + + // Send response + Http2Headers responseHeaders = new DefaultHttp2Headers() + .status("200") + .set("content-type", "application/grpc"); + ctx.write(new DefaultHttp2HeadersFrame(responseHeaders)); + ctx.writeAndFlush(new DefaultHttp2DataFrame(ctx.alloc().buffer().writeBytes(responseData)).stream(dataFrame.stream())); + System.out.println("finish"); + if (dataFrame.isEndStream()) { + dataFrame.release(); + } } private static byte[] decompressGzip(byte[] compressedData) throws IOException { diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java index 3717130274..3909ec254b 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java @@ -82,7 +82,6 @@ public static ProtobufCodec create(Class clazz) { processDecodeBlock(); String code = miniTemplator.generateOutput(); - System.out.println(code); ProtoBufClassCompiler protoBufClassCompiler = new ProtoBufClassCompiler(ProtoBufClassCompiler.class.getClassLoader()); String fullClassName = FieldUtil.getFullClassName(clazz) + "$$ProxyClass"; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java index 95128e9ffa..159b395dd7 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java @@ -52,4 +52,44 @@ public enum StatusEnum { public static class TestClass { private String name; } + + public List getTestList() { + return testList; + } + + public void setTestList(List testList) { + this.testList = testList; + } + + public StatusEnum getStatus() { + return status; + } + + public void setStatus(StatusEnum status) { + this.status = status; + } + + public long getPrice() { + return price; + } + + public void setPrice(long price) { + this.price = price; + } + + public double getAge() { + return age; + } + + public void setAge(double age) { + this.age = age; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/res/ArthasSampleResponse.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/res/ArthasSampleResponse.java new file mode 100644 index 0000000000..06a357e010 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/res/ArthasSampleResponse.java @@ -0,0 +1,24 @@ +package com.taobao.arthas.service.res;/** + * @author: 風楪 + * @date: 2024/8/11 22:11 + */ + +import com.taobao.arthas.protobuf.annotation.ProtobufClass; + +/** + * @author: FengYe + * @date: 2024/8/11 22:11 + * @description: ArthasSampleResponse + */ +@ProtobufClass +public class ArthasSampleResponse { + private String message; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/arthas-grpc-server/src/main/proto/Test.proto b/arthas-grpc-server/src/main/proto/arthasSample.proto similarity index 59% rename from arthas-grpc-server/src/main/proto/Test.proto rename to arthas-grpc-server/src/main/proto/arthasSample.proto index 366dd0ea3c..04c0b869ca 100644 --- a/arthas-grpc-server/src/main/proto/Test.proto +++ b/arthas-grpc-server/src/main/proto/arthasSample.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package helloworld; +package arthasSample; enum StatusEnum { @@ -8,11 +8,11 @@ enum StatusEnum { STOP = 1; } -service HelloService { - rpc SayHello(HelloRequest) returns (HelloReply); +service ArthasService { + rpc SayHello(ArthasSampleRequest) returns (ArthasSampleResponse); } -message HelloRequest { +message ArthasSampleRequest { string name = 1; double age = 2; int64 price = 3; @@ -24,6 +24,6 @@ message TestClass{ string name = 1; } -message HelloReply { +message ArthasSampleResponse { string message = 1; } From 77abc18aeda08755a88a58a82f1f8f79b5650127 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Tue, 13 Aug 2024 02:17:07 +0800 Subject: [PATCH 22/48] update: add grpc handler --- arthas-grpc-server/pom.xml | 49 +++++++++---------- .../src/main/java/com/taobao/arthas/Main.java | 26 +++++++--- .../com/taobao/arthas/h2/Http2Handler.java | 33 +++++++------ .../java/com/taobao/arthas/temp/TempImpl.java | 22 +++++++++ .../src/main/proto/arthasSample.proto | 2 +- 5 files changed, 85 insertions(+), 47 deletions(-) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/temp/TempImpl.java diff --git a/arthas-grpc-server/pom.xml b/arthas-grpc-server/pom.xml index 1f98315dbc..7edcc6739d 100644 --- a/arthas-grpc-server/pom.xml +++ b/arthas-grpc-server/pom.xml @@ -42,27 +42,29 @@ 4.1.111.Final - - com.google.protobuf - protobuf-java - 4.27.2 - + + + + + + - - - - - + + + + + + - - - - - - - - + + io.grpc + grpc-netty + + + io.grpc + grpc-services + @@ -86,12 +88,6 @@ provided - - com.squareup.wire - wire-runtime - 4.2.0 - - com.baidu jprotobuf @@ -110,13 +106,14 @@ ${basedir}/src/main/proto com.google.protobuf:protoc:3.11.0:exe:${os.detected.classifier} + grpc-java + io.grpc:protoc-gen-grpc-java:1.28.0:exe:${os.detected.classifier} compile - - + compile-custom diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java index 7366ab07ae..a17cdb74bc 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java @@ -1,7 +1,13 @@ package com.taobao.arthas; +import arthasSample.ArthasSample; import com.taobao.arthas.protobuf.*; import com.taobao.arthas.protobuf.utils.MiniTemplator; +import com.taobao.arthas.service.ArthasSampleService; +import com.taobao.arthas.service.impl.ArthasSampleServiceImpl; +import com.taobao.arthas.temp.TempImpl; +import io.grpc.Server; +import io.grpc.ServerBuilder; import java.io.IOException; import java.io.InputStream; @@ -15,14 +21,22 @@ public class Main { private static final String TEMPLATE_FILE = "/class_template.tpl"; - public static void main(String[] args) throws IOException { + private static Server server; - String path = Objects.requireNonNull(Main.class.getResource(TEMPLATE_FILE)).getPath(); + public static void main(String[] args) throws Exception { + Main service = new Main(); + service.start(); + service.blockUntilShutdown(); + } - MiniTemplator miniTemplator = new MiniTemplator(path); + public static void start() throws IOException { + ServerBuilder builder = ServerBuilder.forPort(9090) + .addService(new TempImpl()); + server = builder.build(); + server.start(); + } - miniTemplator.setVariable("importPackage","test"); - miniTemplator.addBlock("imports"); - System.out.println(miniTemplator.generateOutput()); + public void blockUntilShutdown() throws InterruptedException { + server.awaitTermination(); } } \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java index 407b955aa1..0fb2c0a06c 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java @@ -83,9 +83,9 @@ private void handleGrpcData(Http2DataFrame dataFrame,ChannelHandlerContext ctx) ByteBuf byteBuf = dataBuffer.get(dataFrame.stream().id()); byteBuf.writeBytes(decompressedData); - byte[] responseData = null; if (dataFrame.isEndStream()) { - byteBuf.readBytes(5); + int length = byteBuf.readInt(); + boolean b = byteBuf.readBoolean(); byte[] byteArray = new byte[byteBuf.readableBytes()]; byteBuf.readBytes(byteArray); @@ -94,21 +94,26 @@ private void handleGrpcData(Http2DataFrame dataFrame,ChannelHandlerContext ctx) ArthasSampleRequest decode = requestCodec.decode(byteArray); + System.out.println(decode); + ArthasSampleResponse arthasSampleResponse = new ArthasSampleResponse(); arthasSampleResponse.setMessage(decode.getName()); - responseData = responseCodec.encode(arthasSampleResponse); - } - + byte[] responseData = responseCodec.encode(arthasSampleResponse); + + // Send response + Http2Headers responseHeaders = new DefaultHttp2Headers() + .status("200") + .set("content-type", "application/grpc"); + ctx.write(new DefaultHttp2HeadersFrame(responseHeaders)); + ByteBuf buffer = ctx.alloc().buffer(); + buffer.writeInt(responseData.length); + buffer.writeBoolean(false); + buffer.writeBytes(responseData); + System.out.println(responseData.length); + ctx.writeAndFlush(new DefaultHttp2DataFrame(buffer,true).stream(dataFrame.stream())); +// dataFrame.retain(); + } else { - // Send response - Http2Headers responseHeaders = new DefaultHttp2Headers() - .status("200") - .set("content-type", "application/grpc"); - ctx.write(new DefaultHttp2HeadersFrame(responseHeaders)); - ctx.writeAndFlush(new DefaultHttp2DataFrame(ctx.alloc().buffer().writeBytes(responseData)).stream(dataFrame.stream())); - System.out.println("finish"); - if (dataFrame.isEndStream()) { - dataFrame.release(); } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/temp/TempImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/temp/TempImpl.java new file mode 100644 index 0000000000..62df4452ee --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/temp/TempImpl.java @@ -0,0 +1,22 @@ +package com.taobao.arthas.temp;/** + * @author: 風楪 + * @date: 2024/8/13 01:57 + */ + +import arthasSample.ArthasSample; +import arthasSample.ArthasTempServiceGrpc; +import io.grpc.stub.StreamObserver; + +/** + * @author: FengYe + * @date: 2024/8/13 01:57 + * @description: TempImpl + */ +public class TempImpl extends ArthasTempServiceGrpc.ArthasTempServiceImplBase { + @Override + public void sayHello(ArthasSample.ArthasSampleRequest request, StreamObserver responseObserver) { + ArthasSample.ArthasSampleResponse build = ArthasSample.ArthasSampleResponse.newBuilder().setMessage("Hello ArthasSample!").build(); + responseObserver.onNext(build); + responseObserver.onCompleted(); + } +} diff --git a/arthas-grpc-server/src/main/proto/arthasSample.proto b/arthas-grpc-server/src/main/proto/arthasSample.proto index 04c0b869ca..c74da27e31 100644 --- a/arthas-grpc-server/src/main/proto/arthasSample.proto +++ b/arthas-grpc-server/src/main/proto/arthasSample.proto @@ -8,7 +8,7 @@ enum StatusEnum { STOP = 1; } -service ArthasService { +service ArthasTempService { rpc SayHello(ArthasSampleRequest) returns (ArthasSampleResponse); } From ec65cb3f4b7cfbc1118458a08cbb14dab1b54892 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Tue, 20 Aug 2024 03:13:52 +0800 Subject: [PATCH 23/48] update: add grpc handler --- .../src/main/java/com/taobao/arthas/h2/Http2Handler.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java index 0fb2c0a06c..3a5fd3bc75 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java @@ -34,6 +34,8 @@ public class Http2Handler extends SimpleChannelInboundHandler { */ private ConcurrentHashMap dataBuffer = new ConcurrentHashMap<>(); + private final Http2FrameWriter frameWriter = new DefaultHttp2FrameWriter(); + @Override protected void channelRead0(ChannelHandlerContext ctx, Http2Frame frame) throws IOException { if (frame instanceof Http2HeadersFrame) { @@ -104,14 +106,15 @@ private void handleGrpcData(Http2DataFrame dataFrame,ChannelHandlerContext ctx) Http2Headers responseHeaders = new DefaultHttp2Headers() .status("200") .set("content-type", "application/grpc"); - ctx.write(new DefaultHttp2HeadersFrame(responseHeaders)); + DefaultHttp2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(responseHeaders); + ctx.write(headersFrame); ByteBuf buffer = ctx.alloc().buffer(); buffer.writeInt(responseData.length); buffer.writeBoolean(false); buffer.writeBytes(responseData); System.out.println(responseData.length); - ctx.writeAndFlush(new DefaultHttp2DataFrame(buffer,true).stream(dataFrame.stream())); -// dataFrame.retain(); + DefaultHttp2DataFrame stream = new DefaultHttp2DataFrame(buffer, true).stream(dataFrame.stream()); + ctx.writeAndFlush(stream); } else { } From f55e872512568d4531e3e3e23ee967f0b18d78ca Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Wed, 28 Aug 2024 01:35:36 +0800 Subject: [PATCH 24/48] update: add grpc handler implement grpc successfully!!! --- .../com/taobao/arthas/h2/Http2Handler.java | 75 +++++++++---------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java index 3a5fd3bc75..4f0501c235 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java @@ -3,22 +3,19 @@ * @date: 2024/7/7 下午9:58 */ -import com.baidu.bjf.remoting.protobuf.Codec; import com.taobao.arthas.protobuf.ProtobufCodec; import com.taobao.arthas.protobuf.ProtobufProxy; -import com.taobao.arthas.service.ArthasSampleService; import com.taobao.arthas.service.req.ArthasSampleRequest; import com.taobao.arthas.service.res.ArthasSampleResponse; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufInputStream; +import io.netty.buffer.PooledByteBufAllocator; +import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http2.*; -import io.netty.util.CharsetUtil; import java.io.*; -import java.util.HashMap; -import java.util.Map; +import java.nio.charset.StandardCharsets; import java.util.concurrent.ConcurrentHashMap; import java.util.zip.GZIPInputStream; @@ -34,17 +31,23 @@ public class Http2Handler extends SimpleChannelInboundHandler { */ private ConcurrentHashMap dataBuffer = new ConcurrentHashMap<>(); - private final Http2FrameWriter frameWriter = new DefaultHttp2FrameWriter(); + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + super.channelRead(ctx, msg); + } @Override protected void channelRead0(ChannelHandlerContext ctx, Http2Frame frame) throws IOException { - if (frame instanceof Http2HeadersFrame) { + if (frame instanceof Http2SettingsFrame) { + + } else if (frame instanceof Http2HeadersFrame) { handleGrpcRequest((Http2HeadersFrame) frame, ctx); } else if (frame instanceof Http2DataFrame) { - handleGrpcData((Http2DataFrame) frame,ctx); + handleGrpcData((Http2DataFrame) frame, ctx); } } + @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); @@ -54,30 +57,11 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { private void handleGrpcRequest(Http2HeadersFrame headersFrame, ChannelHandlerContext ctx) { int id = headersFrame.stream().id(); dataBuffer.put(id, ctx.alloc().buffer()); - System.out.println("Received headers: " + headersFrame.headers()); - System.out.println(headersFrame.headers().path().toString()); - - // Respond to the client with headers - Http2Headers responseHeaders = new DefaultHttp2Headers() - .status("200") - .set("content-type", "text/plain; charset=UTF-8"); - - // 创建响应数据 - byte[] content = "Hello, HTTP/2 World!".getBytes(); - Http2DataFrame dataFrame = new DefaultHttp2DataFrame(ctx.alloc().buffer().writeBytes(content), true); - - // 发送响应头 - ctx.write(new DefaultHttp2HeadersFrame(responseHeaders).stream(headersFrame.stream())); - - // 发送响应数据 - ctx.writeAndFlush(dataFrame.stream(headersFrame.stream())); } - private void handleGrpcData(Http2DataFrame dataFrame,ChannelHandlerContext ctx) throws IOException { + private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) throws IOException { byte[] data = new byte[dataFrame.content().readableBytes()]; - System.out.println(dataFrame.content().readableBytes()); - System.out.println(dataFrame.isEndStream()); dataFrame.content().readBytes(data); // Decompress if needed @@ -86,8 +70,11 @@ private void handleGrpcData(Http2DataFrame dataFrame,ChannelHandlerContext ctx) byteBuf.writeBytes(decompressedData); if (dataFrame.isEndStream()) { - int length = byteBuf.readInt(); + boolean b = byteBuf.readBoolean(); + int length = byteBuf.readInt(); + System.out.println(b); + System.out.println(length); byte[] byteArray = new byte[byteBuf.readableBytes()]; byteBuf.readBytes(byteArray); @@ -99,27 +86,37 @@ private void handleGrpcData(Http2DataFrame dataFrame,ChannelHandlerContext ctx) System.out.println(decode); ArthasSampleResponse arthasSampleResponse = new ArthasSampleResponse(); - arthasSampleResponse.setMessage(decode.getName()); + arthasSampleResponse.setMessage("Hello ArthasSample!"); byte[] responseData = responseCodec.encode(arthasSampleResponse); - // Send response - Http2Headers responseHeaders = new DefaultHttp2Headers() + + + Http2Headers endHeader = new DefaultHttp2Headers() .status("200") - .set("content-type", "application/grpc"); - DefaultHttp2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(responseHeaders); - ctx.write(headersFrame); + .set("content-type", "application/grpc") + .set("grpc-encoding", "identity") + .set("grpc-accept-encoding", "identity,deflate,gzip"); + ctx.write(new DefaultHttp2HeadersFrame(endHeader).stream(dataFrame.stream())); + ByteBuf buffer = ctx.alloc().buffer(); - buffer.writeInt(responseData.length); buffer.writeBoolean(false); + buffer.writeInt(responseData.length); buffer.writeBytes(responseData); System.out.println(responseData.length); - DefaultHttp2DataFrame stream = new DefaultHttp2DataFrame(buffer, true).stream(dataFrame.stream()); - ctx.writeAndFlush(stream); + DefaultHttp2DataFrame resDataFrame = new DefaultHttp2DataFrame(buffer).stream(dataFrame.stream()); + ctx.write(resDataFrame); + + + Http2Headers endStream = new DefaultHttp2Headers() + .set("grpc-status", "0"); + DefaultHttp2HeadersFrame endStreamFrame = new DefaultHttp2HeadersFrame(endStream, true).stream(dataFrame.stream()); + ctx.writeAndFlush(endStreamFrame); } else { } } + private static byte[] decompressGzip(byte[] compressedData) throws IOException { boolean isGzip = (compressedData.length > 2 && (compressedData[0] & 0xff) == 0x1f && (compressedData[1] & 0xff) == 0x8b); if (isGzip) { From 5732edd94279552b81ff2103fd39032302ee452d Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Thu, 5 Sep 2024 02:41:02 +0800 Subject: [PATCH 25/48] update: add grpc handler --- .../com/taobao/arthas/ArthasGrpcServer.java | 2 +- .../src/main/java/com/taobao/arthas/Main.java | 19 ++- .../com/taobao/arthas/grpc/GrpcRequest.java | 111 ++++++++++++++++++ .../com/taobao/arthas/grpc/GrpcResponse.java | 12 ++ .../arthas/{h2 => grpc}/Http2Handler.java | 63 +++------- .../com/taobao/arthas/utils/ByteUtil.java | 29 +++++ 6 files changed, 183 insertions(+), 53 deletions(-) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/GrpcRequest.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/GrpcResponse.java rename arthas-grpc-server/src/main/java/com/taobao/arthas/{h2 => grpc}/Http2Handler.java (62%) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/utils/ByteUtil.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java index abc7d267b6..91da88ff3d 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java @@ -3,7 +3,7 @@ * @date: 2024/7/3 上午12:30 */ -import com.taobao.arthas.h2.Http2Handler; +import com.taobao.arthas.grpc.Http2Handler; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java index a17cdb74bc..6885599eac 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java @@ -8,9 +8,12 @@ import com.taobao.arthas.temp.TempImpl; import io.grpc.Server; import io.grpc.ServerBuilder; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.Objects; /** @@ -24,9 +27,19 @@ public class Main { private static Server server; public static void main(String[] args) throws Exception { - Main service = new Main(); - service.start(); - service.blockUntilShutdown(); +// Main service = new Main(); +// service.start(); +// service.blockUntilShutdown(); + + ByteBuf buffer = Unpooled.buffer(); + buffer.writeInt(1); + buffer.writeBytes("hello".getBytes(StandardCharsets.UTF_8)); + + buffer.readInt(); + byte[] bytes = new byte[buffer.readableBytes()]; + buffer.readBytes(bytes); + + System.out.println(new String(bytes, StandardCharsets.UTF_8)); } public static void start() throws IOException { diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/GrpcRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/GrpcRequest.java new file mode 100644 index 0000000000..bce013f62f --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/GrpcRequest.java @@ -0,0 +1,111 @@ +package com.taobao.arthas.grpc;/** + * @author: 風楪 + * @date: 2024/9/4 23:07 + */ + +import com.taobao.arthas.utils.ByteUtil; +import io.netty.buffer.ByteBuf; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.GZIPInputStream; + +/** + * @author: FengYe + * @date: 2024/9/4 23:07 + * @description: GrpcRequest + */ +public class GrpcRequest { + + /** + * 请求对应的 streamId + */ + private Integer streamId; + + /** + * 请求的方法 + */ + private String method; + + /** + * 请求的类名(非全限定类名) + */ + private String className; + + /** + * protobuf的名称 + */ + private String protoName; + + /** + * 全限定类名 + */ + private String fullyQualifiedClassName; + + /** + * 数据 + */ + private ByteBuf data; + + int dataLength; + + private static final String fullyQualifiedClassNamePrefix = "com.taobao.arthas.service."; + + public GrpcRequest(Integer streamId, String method, String className, String protoName) { + this.streamId = streamId; + this.method = method; + this.className = className; + this.protoName = protoName; + this.fullyQualifiedClassName = fullyQualifiedClassNamePrefix + "protoName." + className; + this.data = ByteUtil.getByteBuf(); + } + + public void writeData(ByteBuf byteBuf) { + byte[] bytes = ByteUtil.getBytes(byteBuf); + if (bytes.length == 0) { + return; + } + byte[] decompressedData = decompressGzip(bytes); + if (decompressedData == null) { + return; + } + ByteBuf operateByteBuf = ByteUtil.getByteBuf(decompressedData); + boolean compressed = operateByteBuf.readBoolean(); + int length = operateByteBuf.readInt(); + dataLength += length; + System.out.println(length); + System.out.println(operateByteBuf.readableBytes()); + data.writeBytes(operateByteBuf); + } + + public byte[] readData() { + System.out.println(dataLength); + byte[] res = new byte[dataLength]; + data.readBytes(res); + return res; + } + + private byte[] decompressGzip(byte[] compressedData) { + boolean isGzip = (compressedData.length > 2 && (compressedData[0] & 0xff) == 0x1f && (compressedData[1] & 0xff) == 0x8b); + if (isGzip) { + try { + InputStream byteStream = new ByteArrayInputStream(compressedData); + GZIPInputStream gzipStream = new GZIPInputStream(byteStream); + byte[] buffer = new byte[1024]; + int len; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + while ((len = gzipStream.read(buffer)) != -1) { + out.write(buffer, 0, len); + } + return out.toByteArray(); + } catch (IOException e) { + System.err.println("Failed to decompress GZIP data: " + e.getMessage()); + } + return null; + } else { + return compressedData; + } + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/GrpcResponse.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/GrpcResponse.java new file mode 100644 index 0000000000..f934eacccc --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/GrpcResponse.java @@ -0,0 +1,12 @@ +package com.taobao.arthas.grpc;/** + * @author: 風楪 + * @date: 2024/9/5 02:05 + */ + +/** + * @author: FengYe + * @date: 2024/9/5 02:05 + * @description: GrpcResponse + */ +public class GrpcResponse { +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/Http2Handler.java similarity index 62% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/Http2Handler.java index 4f0501c235..0ffc51130d 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/h2/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/Http2Handler.java @@ -1,4 +1,4 @@ -package com.taobao.arthas.h2;/** +package com.taobao.arthas.grpc;/** * @author: 風楪 * @date: 2024/7/7 下午9:58 */ @@ -8,16 +8,12 @@ import com.taobao.arthas.service.req.ArthasSampleRequest; import com.taobao.arthas.service.res.ArthasSampleResponse; import io.netty.buffer.ByteBuf; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http2.*; import java.io.*; -import java.nio.charset.StandardCharsets; import java.util.concurrent.ConcurrentHashMap; -import java.util.zip.GZIPInputStream; /** * @author: FengYe @@ -29,7 +25,9 @@ public class Http2Handler extends SimpleChannelInboundHandler { /** * 暂存收到的所有请求的数据 */ - private ConcurrentHashMap dataBuffer = new ConcurrentHashMap<>(); + private ConcurrentHashMap dataBuffer = new ConcurrentHashMap<>(); + + private static final String HEADER_PATH = ":path"; @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { @@ -38,9 +36,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception @Override protected void channelRead0(ChannelHandlerContext ctx, Http2Frame frame) throws IOException { - if (frame instanceof Http2SettingsFrame) { - - } else if (frame instanceof Http2HeadersFrame) { + if (frame instanceof Http2HeadersFrame) { handleGrpcRequest((Http2HeadersFrame) frame, ctx); } else if (frame instanceof Http2DataFrame) { handleGrpcData((Http2DataFrame) frame, ctx); @@ -56,32 +52,25 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { private void handleGrpcRequest(Http2HeadersFrame headersFrame, ChannelHandlerContext ctx) { int id = headersFrame.stream().id(); - dataBuffer.put(id, ctx.alloc().buffer()); + String path = headersFrame.headers().get(HEADER_PATH).toString(); + // 去掉前面的斜杠,然后按斜杠分割 + String[] parts = path.substring(1).split("/"); + String[] serviceParts = parts[0].split("\\."); + dataBuffer.put(id, new GrpcRequest(headersFrame.stream().id(), parts[1], serviceParts[1], serviceParts[0])); System.out.println("Received headers: " + headersFrame.headers()); } private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) throws IOException { - byte[] data = new byte[dataFrame.content().readableBytes()]; - dataFrame.content().readBytes(data); - -// Decompress if needed - byte[] decompressedData = decompressGzip(data); - ByteBuf byteBuf = dataBuffer.get(dataFrame.stream().id()); - byteBuf.writeBytes(decompressedData); + GrpcRequest grpcRequest = dataBuffer.get(dataFrame.stream().id()); + grpcRequest.writeData(dataFrame.content()); if (dataFrame.isEndStream()) { - boolean b = byteBuf.readBoolean(); - int length = byteBuf.readInt(); - System.out.println(b); - System.out.println(length); - - byte[] byteArray = new byte[byteBuf.readableBytes()]; - byteBuf.readBytes(byteArray); + byte[] bytes = grpcRequest.readData(); ProtobufCodec requestCodec = ProtobufProxy.create(ArthasSampleRequest.class); ProtobufCodec responseCodec = ProtobufProxy.create(ArthasSampleResponse.class); - ArthasSampleRequest decode = requestCodec.decode(byteArray); + ArthasSampleRequest decode = requestCodec.decode(bytes); System.out.println(decode); @@ -90,7 +79,6 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) byte[] responseData = responseCodec.encode(arthasSampleResponse); - Http2Headers endHeader = new DefaultHttp2Headers() .status("200") .set("content-type", "application/grpc") @@ -115,27 +103,4 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) } } - - - private static byte[] decompressGzip(byte[] compressedData) throws IOException { - boolean isGzip = (compressedData.length > 2 && (compressedData[0] & 0xff) == 0x1f && (compressedData[1] & 0xff) == 0x8b); - if (isGzip) { - try { - InputStream byteStream = new ByteArrayInputStream(compressedData); - GZIPInputStream gzipStream = new GZIPInputStream(byteStream); - byte[] buffer = new byte[1024]; - int len; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - while ((len = gzipStream.read(buffer)) != -1) { - out.write(buffer, 0, len); - } - return out.toByteArray(); - } catch (IOException e) { - System.err.println("Failed to decompress GZIP data: " + e.getMessage()); - } - return null; - } else { - return compressedData; - } - } } \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/utils/ByteUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/utils/ByteUtil.java new file mode 100644 index 0000000000..b070c361d0 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/utils/ByteUtil.java @@ -0,0 +1,29 @@ +package com.taobao.arthas.utils;/** + * @author: 風楪 + * @date: 2024/9/5 00:51 + */ + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.PooledByteBufAllocator; + +/** + * @author: FengYe + * @date: 2024/9/5 00:51 + * @description: ByteUtil + */ +public class ByteUtil { + + public static ByteBuf getByteBuf() { + return PooledByteBufAllocator.DEFAULT.buffer(); + } + + public static ByteBuf getByteBuf(byte[] bytes) { + return PooledByteBufAllocator.DEFAULT.buffer(bytes.length).writeBytes(bytes); + } + + public static byte[] getBytes(ByteBuf buf) { + byte[] bytes = new byte[buf.readableBytes()]; + buf.readBytes(bytes); + return bytes; + } +} From c8f9310bb635b9636cc2666025d6abab46f33c83 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Fri, 6 Sep 2024 02:36:25 +0800 Subject: [PATCH 26/48] update: add grpc handler --- .../src/main/java/com/taobao/arthas/Main.java | 55 ---------------- .../com/taobao/arthas/grpc/GrpcResponse.java | 12 ---- .../{ => grpc/server}/ArthasGrpcServer.java | 13 +++- .../com/taobao/arthas/grpc/server/Main.java | 62 +++++++++++++++++++ .../grpc/server/handler/GrpcDispatcher.java | 52 ++++++++++++++++ .../{ => server/handler}/GrpcRequest.java | 32 +++------- .../grpc/server/handler/GrpcResponse.java | 42 +++++++++++++ .../{ => server/handler}/Http2Handler.java | 28 +++++---- .../server/handler/annotation/GrpcMethod.java | 20 ++++++ .../handler/annotation/GrpcService.java | 20 ++++++ .../server}/protobuf/ProtobufCodec.java | 2 +- .../server}/protobuf/ProtobufField.java | 2 +- .../protobuf/ProtobufFieldTypeEnum.java | 2 +- .../server}/protobuf/ProtobufProxy.java | 27 ++++---- .../protobuf/annotation/ProtobufClass.java | 2 +- .../annotation/ProtobufCustomizedField.java | 4 +- .../annotation/ProtobufEnableZigZap.java | 2 +- .../protobuf/annotation/ProtobufIgnore.java | 2 +- .../protobuf/annotation/ProtobufPacked.java | 2 +- .../utils/CodedOutputStreamCache.java | 2 +- .../server}/protobuf/utils/EnumHandler.java | 2 +- .../server}/protobuf/utils/FieldUtil.java | 20 +++--- .../server}/protobuf/utils/MiniTemplator.java | 2 +- .../protobuf/utils/ProtoBufClassCompiler.java | 5 +- .../server/service/ArthasSampleService.java | 16 +++++ .../service/impl/ArthasSampleServiceImpl.java | 25 ++++++++ .../service/req/ArthasSampleRequest.java | 14 +---- .../service/res/ArthasSampleResponse.java | 4 +- .../{ => grpc/server}/temp/TempImpl.java | 2 +- .../{ => grpc/server}/utils/ByteUtil.java | 2 +- .../arthas/grpc/server/utils/ReflectUtil.java | 38 ++++++++++++ .../arthas/service/ArthasSampleService.java | 13 ---- .../service/impl/ArthasSampleServiceImpl.java | 21 ------- 33 files changed, 350 insertions(+), 197 deletions(-) delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/GrpcResponse.java rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/ArthasGrpcServer.java (89%) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/Main.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java rename arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/{ => server/handler}/GrpcRequest.java (78%) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java rename arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/{ => server/handler}/Http2Handler.java (83%) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/protobuf/ProtobufCodec.java (89%) rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/protobuf/ProtobufField.java (99%) rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/protobuf/ProtobufFieldTypeEnum.java (99%) rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/protobuf/ProtobufProxy.java (95%) rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/protobuf/annotation/ProtobufClass.java (87%) rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/protobuf/annotation/ProtobufCustomizedField.java (81%) rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/protobuf/annotation/ProtobufEnableZigZap.java (87%) rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/protobuf/annotation/ProtobufIgnore.java (87%) rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/protobuf/annotation/ProtobufPacked.java (89%) rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/protobuf/utils/CodedOutputStreamCache.java (96%) rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/protobuf/utils/EnumHandler.java (79%) rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/protobuf/utils/FieldUtil.java (98%) rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/protobuf/utils/MiniTemplator.java (99%) rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/protobuf/utils/ProtoBufClassCompiler.java (98%) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/service/req/ArthasSampleRequest.java (71%) rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/service/res/ArthasSampleResponse.java (74%) rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/temp/TempImpl.java (93%) rename arthas-grpc-server/src/main/java/com/taobao/arthas/{ => grpc/server}/utils/ByteUtil.java (93%) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ReflectUtil.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/service/ArthasSampleService.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/service/impl/ArthasSampleServiceImpl.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java deleted file mode 100644 index 6885599eac..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/Main.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.taobao.arthas; - -import arthasSample.ArthasSample; -import com.taobao.arthas.protobuf.*; -import com.taobao.arthas.protobuf.utils.MiniTemplator; -import com.taobao.arthas.service.ArthasSampleService; -import com.taobao.arthas.service.impl.ArthasSampleServiceImpl; -import com.taobao.arthas.temp.TempImpl; -import io.grpc.Server; -import io.grpc.ServerBuilder; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.Objects; - -/** - * @author: 風楪 - * @date: 2024/6/30 上午1:22 - */ -public class Main { - - private static final String TEMPLATE_FILE = "/class_template.tpl"; - - private static Server server; - - public static void main(String[] args) throws Exception { -// Main service = new Main(); -// service.start(); -// service.blockUntilShutdown(); - - ByteBuf buffer = Unpooled.buffer(); - buffer.writeInt(1); - buffer.writeBytes("hello".getBytes(StandardCharsets.UTF_8)); - - buffer.readInt(); - byte[] bytes = new byte[buffer.readableBytes()]; - buffer.readBytes(bytes); - - System.out.println(new String(bytes, StandardCharsets.UTF_8)); - } - - public static void start() throws IOException { - ServerBuilder builder = ServerBuilder.forPort(9090) - .addService(new TempImpl()); - server = builder.build(); - server.start(); - } - - public void blockUntilShutdown() throws InterruptedException { - server.awaitTermination(); - } -} \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/GrpcResponse.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/GrpcResponse.java deleted file mode 100644 index f934eacccc..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/GrpcResponse.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.taobao.arthas.grpc;/** - * @author: 風楪 - * @date: 2024/9/5 02:05 - */ - -/** - * @author: FengYe - * @date: 2024/9/5 02:05 - * @description: GrpcResponse - */ -public class GrpcResponse { -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java similarity index 89% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java index 91da88ff3d..c94c099f18 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/ArthasGrpcServer.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java @@ -1,9 +1,11 @@ -package com.taobao.arthas;/** +package com.taobao.arthas.grpc.server;/** * @author: 風楪 * @date: 2024/7/3 上午12:30 */ -import com.taobao.arthas.grpc.Http2Handler; +import com.taobao.arthas.grpc.server.handler.Http2Handler; +import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; +import com.taobao.arthas.grpc.server.utils.ReflectUtil; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; @@ -18,8 +20,13 @@ import java.io.File; import java.io.IOException; +import java.lang.reflect.Method; +import java.net.URL; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; /** * @author: FengYe @@ -27,6 +34,8 @@ * @description: ArthasGrpcServer */ public class ArthasGrpcServer { + + public static void main(String[] args) throws Exception { //自签名生成密钥 SelfSignedCertificate ssc = new SelfSignedCertificate(); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/Main.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/Main.java new file mode 100644 index 0000000000..8067b02df9 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/Main.java @@ -0,0 +1,62 @@ +package com.taobao.arthas.grpc.server; + +import com.taobao.arthas.grpc.server.temp.TempImpl; +import io.grpc.Server; +import io.grpc.ServerBuilder; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +/** + * @author: 風楪 + * @date: 2024/6/30 上午1:22 + */ +public class Main { + + private static final String TEMPLATE_FILE = "/class_template.tpl"; + + private static Server server; + + public static void main(String[] args) throws Exception { +// Main service = new Main(); +// service.start(); +// service.blockUntilShutdown(); + findClasses("com.taobao.arthas.grpc.server.service.impl").forEach(System.out::println); + } + + private static List> findClasses(String packageName) { + List> classes = new ArrayList<>(); + String path = packageName.replace('.', '/'); + try { + URL resource = Thread.currentThread().getContextClassLoader().getResource(path); + if (resource != null) { + File directory = new File(resource.toURI()); + if (directory.exists()) { + for (File file : directory.listFiles()) { + if (file.isFile() && file.getName().endsWith(".class")) { + String className = packageName + '.' + file.getName().replace(".class", ""); + classes.add(Class.forName(className)); + } + } + } + } + } catch (Exception e) { + + } + return classes; + } + + public static void start() throws IOException { + ServerBuilder builder = ServerBuilder.forPort(9090) + .addService(new TempImpl()); + server = builder.build(); + server.start(); + } + + public void blockUntilShutdown() throws InterruptedException { + server.awaitTermination(); + } +} \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java new file mode 100644 index 0000000000..562be95027 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java @@ -0,0 +1,52 @@ +package com.taobao.arthas.grpc.server.handler;/** + * @author: 風楪 + * @date: 2024/9/6 01:12 + */ + +import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; +import com.taobao.arthas.grpc.server.utils.ReflectUtil; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; + +/** + * @author: FengYe + * @date: 2024/9/6 01:12 + * @description: GrpcDelegrate + */ +public class GrpcDispatcher { + + private static final String GRPC_SERVICE_PACKAGE_NAME = "com.taobao.arthas.grpc.server.service.impl"; + + public static void loadGrpcService(){ + List> classes = ReflectUtil.findClasses(GRPC_SERVICE_PACKAGE_NAME); + for (Class clazz : classes) { + if (clazz.isAnnotationPresent(GrpcService.class)) { + try { + Object instance = clazz.getDeclaredConstructor().newInstance(); +// Map ; + System.out.println("实例化类: " + clazz.getName()); + // 你可以在这里调用实例的方法或进行其他操作 + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + public void execute(String serviceName,String methodName, Object[] args){ +// // 创建一个 Lookup 对象 +// MethodHandles.Lookup lookup = MethodHandles.lookup(); +// +// // 获取方法句柄 +// MethodType methodType = MethodType.methodType(void.class, String.class); +// MethodHandle methodHandle = lookup.findVirtual(Example.class, "sayHello", methodType); +// +// // 调用方法句柄 +// methodHandle.invoke(example, "World"); // 输出: Hello, World + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/GrpcRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java similarity index 78% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/GrpcRequest.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java index bce013f62f..edba98ea0a 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/GrpcRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java @@ -1,9 +1,9 @@ -package com.taobao.arthas.grpc;/** +package com.taobao.arthas.grpc.server.handler;/** * @author: 風楪 * @date: 2024/9/4 23:07 */ -import com.taobao.arthas.utils.ByteUtil; +import com.taobao.arthas.grpc.server.utils.ByteUtil; import io.netty.buffer.ByteBuf; import java.io.ByteArrayInputStream; @@ -25,40 +25,26 @@ public class GrpcRequest { private Integer streamId; /** - * 请求的方法 - */ - private String method; - - /** - * 请求的类名(非全限定类名) + * 请求的 path */ - private String className; + private String path; /** - * protobuf的名称 - */ - private String protoName; - - /** - * 全限定类名 + * 请求的方法 */ - private String fullyQualifiedClassName; + private String method; /** * 数据 */ private ByteBuf data; - int dataLength; - - private static final String fullyQualifiedClassNamePrefix = "com.taobao.arthas.service."; + private int dataLength; - public GrpcRequest(Integer streamId, String method, String className, String protoName) { + public GrpcRequest(Integer streamId, String path,String method) { this.streamId = streamId; + this.path = path; this.method = method; - this.className = className; - this.protoName = protoName; - this.fullyQualifiedClassName = fullyQualifiedClassNamePrefix + "protoName." + className; this.data = ByteUtil.getByteBuf(); } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java new file mode 100644 index 0000000000..59a2214c8f --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java @@ -0,0 +1,42 @@ +package com.taobao.arthas.grpc.server.handler;/** + * @author: 風楪 + * @date: 2024/9/5 02:05 + */ + +import io.netty.buffer.ByteBuf; +import io.netty.handler.codec.http2.DefaultHttp2Headers; +import io.netty.handler.codec.http2.Http2Headers; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author: FengYe + * @date: 2024/9/5 02:05 + * @description: GrpcResponse + */ +public class GrpcResponse { + + private Map headers; + + private ByteBuf data; + + private T genericsData; + + { + headers = new HashMap<>(); + headers.put("content-type", "application/grpc"); + headers.put("grpc-encoding", "identity"); + headers.put("grpc-accept-encoding", "identity,deflate,gzip"); + } + + public Http2Headers getEndHeader() { + Http2Headers endHeader = new DefaultHttp2Headers().status("200"); + headers.forEach(endHeader::set); + return endHeader; + } + + public Http2Headers getEndStreamHeader() { + return new DefaultHttp2Headers().set("grpc-status", "0"); + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java similarity index 83% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/Http2Handler.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java index 0ffc51130d..c9f6f94142 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java @@ -1,12 +1,12 @@ -package com.taobao.arthas.grpc;/** +package com.taobao.arthas.grpc.server.handler;/** * @author: 風楪 * @date: 2024/7/7 下午9:58 */ -import com.taobao.arthas.protobuf.ProtobufCodec; -import com.taobao.arthas.protobuf.ProtobufProxy; -import com.taobao.arthas.service.req.ArthasSampleRequest; -import com.taobao.arthas.service.res.ArthasSampleResponse; +import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; +import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; +import com.taobao.arthas.grpc.server.service.req.ArthasSampleRequest; +import com.taobao.arthas.grpc.server.service.res.ArthasSampleResponse; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; @@ -55,8 +55,7 @@ private void handleGrpcRequest(Http2HeadersFrame headersFrame, ChannelHandlerCon String path = headersFrame.headers().get(HEADER_PATH).toString(); // 去掉前面的斜杠,然后按斜杠分割 String[] parts = path.substring(1).split("/"); - String[] serviceParts = parts[0].split("\\."); - dataBuffer.put(id, new GrpcRequest(headersFrame.stream().id(), parts[1], serviceParts[1], serviceParts[0])); + dataBuffer.put(id, new GrpcRequest(headersFrame.stream().id(), parts[0], parts[1])); System.out.println("Received headers: " + headersFrame.headers()); } @@ -66,6 +65,13 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) if (dataFrame.isEndStream()) { + + try { + + } catch (Exception e) { + throw new RuntimeException(e); + } + byte[] bytes = grpcRequest.readData(); ProtobufCodec requestCodec = ProtobufProxy.create(ArthasSampleRequest.class); ProtobufCodec responseCodec = ProtobufProxy.create(ArthasSampleResponse.class); @@ -80,10 +86,10 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) Http2Headers endHeader = new DefaultHttp2Headers() - .status("200") - .set("content-type", "application/grpc") - .set("grpc-encoding", "identity") - .set("grpc-accept-encoding", "identity,deflate,gzip"); + .status("200"); +// .set("content-type", "application/grpc") +// .set("grpc-encoding", "identity") +// .set("grpc-accept-encoding", "identity,deflate,gzip"); ctx.write(new DefaultHttp2HeadersFrame(endHeader).stream(dataFrame.stream())); ByteBuf buffer = ctx.alloc().buffer(); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java new file mode 100644 index 0000000000..ef35716913 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java @@ -0,0 +1,20 @@ +package com.taobao.arthas.grpc.server.handler.annotation;/** + * @author: 風楪 + * @date: 2024/9/6 01:57 + */ + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author: FengYe + * @date: 2024/9/6 01:57 + * @description: GrpcMethod + */ +@Target({ ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface GrpcMethod { + String value() default ""; +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java new file mode 100644 index 0000000000..c94327a773 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java @@ -0,0 +1,20 @@ +package com.taobao.arthas.grpc.server.handler.annotation;/** + * @author: 風楪 + * @date: 2024/9/6 01:57 + */ + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author: FengYe + * @date: 2024/9/6 01:57 + * @description: GrpcService + */ +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +public @interface GrpcService { + String path() default ""; +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufCodec.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufCodec.java similarity index 89% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufCodec.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufCodec.java index 101b07e86e..0bd6486258 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufCodec.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufCodec.java @@ -1,4 +1,4 @@ -package com.taobao.arthas.protobuf;/** +package com.taobao.arthas.grpc.server.protobuf;/** * @author: 風楪 * @date: 2024/7/17 下午9:44 */ diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufField.java similarity index 99% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufField.java index 6203b88d5e..69b1fcb4d9 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufField.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufField.java @@ -1,4 +1,4 @@ -package com.taobao.arthas.protobuf;/** +package com.taobao.arthas.grpc.server.protobuf;/** * @author: 風楪 * @date: 2024/7/25 上午12:14 */ diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufFieldTypeEnum.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufFieldTypeEnum.java similarity index 99% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufFieldTypeEnum.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufFieldTypeEnum.java index 2cde8ee9f8..36f3a1d1e7 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufFieldTypeEnum.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufFieldTypeEnum.java @@ -1,4 +1,4 @@ -package com.taobao.arthas.protobuf;/** +package com.taobao.arthas.grpc.server.protobuf;/** * @author: 風楪 * @date: 2024/7/17 下午10:02 */ diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java similarity index 95% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java index 3909ec254b..4a16baa218 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java @@ -1,22 +1,17 @@ -package com.taobao.arthas.protobuf;/** +package com.taobao.arthas.grpc.server.protobuf;/** * @author: 風楪 * @date: 2024/7/17 下午9:57 */ -import com.baidu.bjf.remoting.protobuf.Codec; -import com.baidu.bjf.remoting.protobuf.utils.ClassHelper; -import com.baidu.bjf.remoting.protobuf.utils.StringUtils; import com.google.protobuf.WireFormat; -import com.taobao.arthas.protobuf.annotation.ProtobufEnableZigZap; -import com.taobao.arthas.protobuf.annotation.ProtobufClass; -import com.taobao.arthas.protobuf.utils.FieldUtil; -import com.taobao.arthas.protobuf.utils.MiniTemplator; -import com.taobao.arthas.protobuf.utils.ProtoBufClassCompiler; -import com.taobao.arthas.service.req.ArthasSampleRequest; - -import java.io.IOException; -import java.lang.reflect.Field; +import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufEnableZigZap; +import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; +import com.taobao.arthas.grpc.server.protobuf.utils.FieldUtil; +import com.taobao.arthas.grpc.server.protobuf.utils.MiniTemplator; +import com.taobao.arthas.grpc.server.protobuf.utils.ProtoBufClassCompiler; +import com.taobao.arthas.grpc.server.service.req.ArthasSampleRequest; + import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -106,9 +101,9 @@ private static void processImportBlock() { imports.add("java.util.*"); imports.add("java.io.IOException"); imports.add("java.lang.reflect.*"); - imports.add("com.taobao.arthas.protobuf.*"); - imports.add("com.taobao.arthas.protobuf.utils.*"); - imports.add("com.taobao.arthas.protobuf.annotation.*"); + imports.add("com.taobao.arthas.grpc.protobuf.*"); + imports.add("com.taobao.arthas.grpc.protobuf.utils.*"); + imports.add("com.taobao.arthas.grpc.protobuf.annotation.*"); imports.add("com.google.protobuf.*"); imports.add(clazz.getCanonicalName()); for (String pkg : imports) { diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufClass.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufClass.java similarity index 87% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufClass.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufClass.java index 20e50adc99..62482239c7 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufClass.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufClass.java @@ -1,4 +1,4 @@ -package com.taobao.arthas.protobuf.annotation;/** +package com.taobao.arthas.grpc.server.protobuf.annotation;/** * @author: 風楪 * @date: 2024/7/25 上午12:19 */ diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufCustomizedField.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufCustomizedField.java similarity index 81% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufCustomizedField.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufCustomizedField.java index ec3124299a..59eea05f4f 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufCustomizedField.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufCustomizedField.java @@ -1,9 +1,9 @@ -package com.taobao.arthas.protobuf.annotation;/** +package com.taobao.arthas.grpc.server.protobuf.annotation;/** * @author: 風楪 * @date: 2024/7/25 上午12:21 */ -import com.taobao.arthas.protobuf.ProtobufFieldTypeEnum; +import com.taobao.arthas.grpc.server.protobuf.ProtobufFieldTypeEnum; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufEnableZigZap.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufEnableZigZap.java similarity index 87% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufEnableZigZap.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufEnableZigZap.java index c6566dccbd..f92c9cbbe6 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufEnableZigZap.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufEnableZigZap.java @@ -1,4 +1,4 @@ -package com.taobao.arthas.protobuf.annotation;/** +package com.taobao.arthas.grpc.server.protobuf.annotation;/** * @author: 風楪 * @date: 2024/7/28 下午7:27 */ diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufIgnore.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufIgnore.java similarity index 87% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufIgnore.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufIgnore.java index 4ea603cf86..fa6b00a2f9 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufIgnore.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufIgnore.java @@ -1,4 +1,4 @@ -package com.taobao.arthas.protobuf.annotation;/** +package com.taobao.arthas.grpc.server.protobuf.annotation;/** * @author: 風楪 * @date: 2024/7/25 上午12:44 */ diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufPacked.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufPacked.java similarity index 89% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufPacked.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufPacked.java index 65a7ba270e..1944558a52 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/annotation/ProtobufPacked.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufPacked.java @@ -1,4 +1,4 @@ -package com.taobao.arthas.protobuf.annotation;/** +package com.taobao.arthas.grpc.server.protobuf.annotation;/** * @author: 風楪 * @date: 2024/7/30 上午2:01 */ diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/CodedOutputStreamCache.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/CodedOutputStreamCache.java similarity index 96% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/CodedOutputStreamCache.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/CodedOutputStreamCache.java index 756943f602..8c7709b01b 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/CodedOutputStreamCache.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/CodedOutputStreamCache.java @@ -1,4 +1,4 @@ -package com.taobao.arthas.protobuf.utils;/** +package com.taobao.arthas.grpc.server.protobuf.utils;/** * @author: 風楪 * @date: 2024/8/3 下午7:20 */ diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/EnumHandler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/EnumHandler.java similarity index 79% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/EnumHandler.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/EnumHandler.java index 90064ad05c..f2f1ab9db2 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/EnumHandler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/EnumHandler.java @@ -1,4 +1,4 @@ -package com.taobao.arthas.protobuf.utils;/** +package com.taobao.arthas.grpc.server.protobuf.utils;/** * @author: 風楪 * @date: 2024/8/6 上午1:19 */ diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/FieldUtil.java similarity index 98% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/FieldUtil.java index 309110b31c..93445b5fd2 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/FieldUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/FieldUtil.java @@ -1,23 +1,19 @@ -package com.taobao.arthas.protobuf.utils;/** +package com.taobao.arthas.grpc.server.protobuf.utils;/** * @author: 風楪 * @date: 2024/7/25 上午12:33 */ -import com.baidu.bjf.remoting.protobuf.EnumReadable; -import com.baidu.bjf.remoting.protobuf.FieldType; import com.baidu.bjf.remoting.protobuf.utils.ClassHelper; -import com.baidu.bjf.remoting.protobuf.utils.FieldInfo; -import com.baidu.bjf.remoting.protobuf.utils.ProtobufProxyUtils; import com.baidu.bjf.remoting.protobuf.utils.StringUtils; import com.google.protobuf.*; -import com.taobao.arthas.protobuf.ProtobufCodec; -import com.taobao.arthas.protobuf.ProtobufField; -import com.taobao.arthas.protobuf.ProtobufFieldTypeEnum; -import com.taobao.arthas.protobuf.ProtobufProxy; -import com.taobao.arthas.protobuf.annotation.ProtobufPacked; -import com.taobao.arthas.protobuf.annotation.ProtobufCustomizedField; -import com.taobao.arthas.protobuf.annotation.ProtobufIgnore; +import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; +import com.taobao.arthas.grpc.server.protobuf.ProtobufField; +import com.taobao.arthas.grpc.server.protobuf.ProtobufFieldTypeEnum; +import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; +import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufPacked; +import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufCustomizedField; +import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufIgnore; import java.io.IOException; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/MiniTemplator.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/MiniTemplator.java similarity index 99% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/MiniTemplator.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/MiniTemplator.java index 1701d1034d..4fe073fda0 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/MiniTemplator.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/MiniTemplator.java @@ -10,7 +10,7 @@ // Please contact the author if you need another license. // This module is provided "as is", without warranties of any kind. -package com.taobao.arthas.protobuf.utils;; +package com.taobao.arthas.grpc.server.protobuf.utils;; import java.io.File; import java.io.FileInputStream; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/ProtoBufClassCompiler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufClassCompiler.java similarity index 98% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/ProtoBufClassCompiler.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufClassCompiler.java index 5f60da3749..80e47576e2 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/protobuf/utils/ProtoBufClassCompiler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufClassCompiler.java @@ -1,18 +1,15 @@ -package com.taobao.arthas.protobuf.utils;/** +package com.taobao.arthas.grpc.server.protobuf.utils;/** * @author: 風楪 * @date: 2024/8/9 01:21 */ import com.baidu.bjf.remoting.protobuf.utils.ClassHelper; import com.baidu.bjf.remoting.protobuf.utils.compiler.ClassUtils; -import com.baidu.bjf.remoting.protobuf.utils.compiler.JdkCompiler; -import com.taobao.arthas.protobuf.ProtobufProxy; import javax.tools.*; import java.io.*; import java.net.URI; import java.net.URISyntaxException; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.AccessController; import java.security.PrivilegedAction; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java new file mode 100644 index 0000000000..992ce0b80f --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java @@ -0,0 +1,16 @@ +package com.taobao.arthas.grpc.server.service;/** + * @author: 風楪 + * @date: 2024/6/30 下午11:42 + */ + +import com.taobao.arthas.grpc.server.service.req.ArthasSampleRequest; +import com.taobao.arthas.grpc.server.service.res.ArthasSampleResponse; + +/** + * @author: FengYe + * @date: 2024/6/30 下午11:42 + * @description: ArthasSampleService + */ +public interface ArthasSampleService { + ArthasSampleResponse trace(ArthasSampleRequest command); +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java new file mode 100644 index 0000000000..0e4317fca6 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java @@ -0,0 +1,25 @@ +package com.taobao.arthas.grpc.server.service.impl;/** + * @author: 風楪 + * @date: 2024/6/30 下午11:43 + */ + +import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; +import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; +import com.taobao.arthas.grpc.server.service.ArthasSampleService; +import com.taobao.arthas.grpc.server.service.req.ArthasSampleRequest; +import com.taobao.arthas.grpc.server.service.res.ArthasSampleResponse; + +/** + * @author: FengYe + * @date: 2024/6/30 下午11:43 + * @description: ArthasSampleServiceImpl + */ +@GrpcService(path = "arthasSample.ArthasTempService") +public class ArthasSampleServiceImpl implements ArthasSampleService { + + @Override + @GrpcMethod("trace") + public ArthasSampleResponse trace(ArthasSampleRequest command) { + return new ArthasSampleResponse(); + } +} \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasSampleRequest.java similarity index 71% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasSampleRequest.java index 159b395dd7..46a0475c4d 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/req/ArthasSampleRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasSampleRequest.java @@ -1,19 +1,12 @@ -package com.taobao.arthas.service.req;/** +package com.taobao.arthas.grpc.server.service.req;/** * @author: 風楪 * @date: 2024/7/14 上午4:28 */ -import com.baidu.bjf.remoting.protobuf.annotation.Protobuf; -import com.google.protobuf.*; -import com.taobao.arthas.protobuf.annotation.ProtobufClass; -import com.taobao.arthas.protobuf.annotation.ProtobufCustomizedField; -import com.taobao.arthas.protobuf.annotation.ProtobufIgnore; +import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; import lombok.ToString; -import java.io.IOException; -import java.nio.ByteBuffer; import java.util.List; -import java.util.Map; /** * @author: FengYe @@ -21,7 +14,6 @@ * @description: ArthasSampleRequest */ -@com.baidu.bjf.remoting.protobuf.annotation.ProtobufClass @ProtobufClass @ToString public class ArthasSampleRequest { @@ -32,7 +24,6 @@ public class ArthasSampleRequest { private StatusEnum status; private List testList; - @com.baidu.bjf.remoting.protobuf.annotation.ProtobufClass @ProtobufClass public enum StatusEnum { START(1, "开始"), @@ -47,7 +38,6 @@ public enum StatusEnum { private String desc; } - @com.baidu.bjf.remoting.protobuf.annotation.ProtobufClass @ProtobufClass public static class TestClass { private String name; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/res/ArthasSampleResponse.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/res/ArthasSampleResponse.java similarity index 74% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/service/res/ArthasSampleResponse.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/res/ArthasSampleResponse.java index 06a357e010..09ab40f71f 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/res/ArthasSampleResponse.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/res/ArthasSampleResponse.java @@ -1,9 +1,9 @@ -package com.taobao.arthas.service.res;/** +package com.taobao.arthas.grpc.server.service.res;/** * @author: 風楪 * @date: 2024/8/11 22:11 */ -import com.taobao.arthas.protobuf.annotation.ProtobufClass; +import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; /** * @author: FengYe diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/temp/TempImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/temp/TempImpl.java similarity index 93% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/temp/TempImpl.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/temp/TempImpl.java index 62df4452ee..481b085368 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/temp/TempImpl.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/temp/TempImpl.java @@ -1,4 +1,4 @@ -package com.taobao.arthas.temp;/** +package com.taobao.arthas.grpc.server.temp;/** * @author: 風楪 * @date: 2024/8/13 01:57 */ diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/utils/ByteUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java similarity index 93% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/utils/ByteUtil.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java index b070c361d0..7f04b11128 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/utils/ByteUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java @@ -1,4 +1,4 @@ -package com.taobao.arthas.utils;/** +package com.taobao.arthas.grpc.server.utils;/** * @author: 風楪 * @date: 2024/9/5 00:51 */ diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ReflectUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ReflectUtil.java new file mode 100644 index 0000000000..5da93c9dfd --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ReflectUtil.java @@ -0,0 +1,38 @@ +package com.taobao.arthas.grpc.server.utils;/** + * @author: 風楪 + * @date: 2024/9/6 02:20 + */ + +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +/** + * @author: FengYe + * @date: 2024/9/6 02:20 + * @description: ReflectUtil + */ +public class ReflectUtil { + public static List> findClasses(String packageName) { + List> classes = new ArrayList<>(); + String path = packageName.replace('.', '/'); + try { + URL resource = Thread.currentThread().getContextClassLoader().getResource(path); + if (resource != null) { + File directory = new File(resource.toURI()); + if (directory.exists()) { + for (File file : directory.listFiles()) { + if (file.isFile() && file.getName().endsWith(".class")) { + String className = packageName + '.' + file.getName().replace(".class", ""); + classes.add(Class.forName(className)); + } + } + } + } + } catch (Exception e) { + + } + return classes; + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/ArthasSampleService.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/ArthasSampleService.java deleted file mode 100644 index 744258fc17..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/ArthasSampleService.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.taobao.arthas.service;/** - * @author: 風楪 - * @date: 2024/6/30 下午11:42 - */ - -/** - * @author: FengYe - * @date: 2024/6/30 下午11:42 - * @description: ArthasSampleService - */ -public interface ArthasSampleService { - String trace(String command); -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/service/impl/ArthasSampleServiceImpl.java deleted file mode 100644 index 194d8364fe..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/service/impl/ArthasSampleServiceImpl.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.taobao.arthas.service.impl;/** - * @author: 風楪 - * @date: 2024/6/30 下午11:43 - */ - -import com.taobao.arthas.service.ArthasSampleService; -import lombok.extern.slf4j.Slf4j; - -/** - * @author: FengYe - * @date: 2024/6/30 下午11:43 - * @description: ArthasSampleServiceImpl - */ -@Slf4j -public class ArthasSampleServiceImpl implements ArthasSampleService { - @Override - public String trace(String command) { - log.info("receive command: {}", command); - return "receive command: " + command; - } -} \ No newline at end of file From 526cdd943dd63e5d8825cec03b66c5ce619f6a76 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Tue, 10 Sep 2024 02:26:13 +0800 Subject: [PATCH 27/48] update: add grpc dispatcher --- .../arthas/grpc/server/ArthasGrpcServer.java | 6 ++- .../com/taobao/arthas/grpc/server/Main.java | 21 +++++++-- .../grpc/server/handler/GrpcDispatcher.java | 46 +++++++++++++------ .../grpc/server/handler/GrpcRequest.java | 28 +++++++++-- .../grpc/server/handler/Http2Handler.java | 14 +++--- .../handler/annotation/GrpcService.java | 2 +- .../service/impl/ArthasSampleServiceImpl.java | 2 +- 7 files changed, 87 insertions(+), 32 deletions(-) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java index c94c099f18..91b54455c0 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java @@ -3,6 +3,7 @@ * @date: 2024/7/3 上午12:30 */ +import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; import com.taobao.arthas.grpc.server.handler.Http2Handler; import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; import com.taobao.arthas.grpc.server.utils.ReflectUtil; @@ -60,6 +61,9 @@ public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); + GrpcDispatcher grpcDispatcher = new GrpcDispatcher(); + grpcDispatcher.loadGrpcService(); + try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) @@ -70,7 +74,7 @@ public static void main(String[] args) throws Exception { public void initChannel(SocketChannel ch) { // ch.pipeline().addLast(sslCtx.newHandler(ch.alloc())); ch.pipeline().addLast(Http2FrameCodecBuilder.forServer().build()); - ch.pipeline().addLast(new Http2Handler()); + ch.pipeline().addLast(new Http2Handler(grpcDispatcher)); } }); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/Main.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/Main.java index 8067b02df9..2c1388884c 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/Main.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/Main.java @@ -6,6 +6,10 @@ import java.io.File; import java.io.IOException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.List; @@ -20,11 +24,14 @@ public class Main { private static Server server; - public static void main(String[] args) throws Exception { -// Main service = new Main(); -// service.start(); -// service.blockUntilShutdown(); - findClasses("com.taobao.arthas.grpc.server.service.impl").forEach(System.out::println); + public static void main(String[] args) throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + Method plus1 = Main.class.getDeclaredMethod("plus", int.class, int.class); + + MethodHandle unreflect = lookup.unreflect(plus1); + + System.out.println(unreflect.invoke(new Main(), 1, 2)); } private static List> findClasses(String packageName) { @@ -49,6 +56,10 @@ private static List> findClasses(String packageName) { return classes; } + public int plus(int a,int b){ + return a+b; + } + public static void start() throws IOException { ServerBuilder builder = ServerBuilder.forPort(9090) .addService(new TempImpl()); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java index 562be95027..cb12620e69 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java @@ -3,6 +3,7 @@ * @date: 2024/9/6 01:12 */ +import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; import com.taobao.arthas.grpc.server.utils.ReflectUtil; @@ -10,6 +11,7 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Method; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -22,15 +24,28 @@ public class GrpcDispatcher { private static final String GRPC_SERVICE_PACKAGE_NAME = "com.taobao.arthas.grpc.server.service.impl"; - public static void loadGrpcService(){ + private Map grpcMethodMap = new HashMap<>(); + + public void loadGrpcService() { List> classes = ReflectUtil.findClasses(GRPC_SERVICE_PACKAGE_NAME); for (Class clazz : classes) { if (clazz.isAnnotationPresent(GrpcService.class)) { try { + // 处理 service + GrpcService grpcService = clazz.getAnnotation(GrpcService.class); Object instance = clazz.getDeclaredConstructor().newInstance(); -// Map ; - System.out.println("实例化类: " + clazz.getName()); - // 你可以在这里调用实例的方法或进行其他操作 + + // 处理 method + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Method[] declaredMethods = clazz.getDeclaredMethods(); + for (Method method : declaredMethods) { + if (method.isAnnotationPresent(GrpcMethod.class)) { + GrpcMethod grpcMethod = clazz.getAnnotation(GrpcMethod.class); + MethodHandle methodHandle = lookup.unreflect(method); + methodHandle.bindTo(instance); + grpcMethodMap.put(generateGrpcMethodKey(grpcService.value(),grpcMethod.value()), methodHandle); + } + } } catch (Exception e) { e.printStackTrace(); } @@ -38,15 +53,18 @@ public static void loadGrpcService(){ } } - public void execute(String serviceName,String methodName, Object[] args){ -// // 创建一个 Lookup 对象 -// MethodHandles.Lookup lookup = MethodHandles.lookup(); -// -// // 获取方法句柄 -// MethodType methodType = MethodType.methodType(void.class, String.class); -// MethodHandle methodHandle = lookup.findVirtual(Example.class, "sayHello", methodType); -// -// // 调用方法句柄 -// methodHandle.invoke(example, "World"); // 输出: Hello, World + private String generateGrpcMethodKey(String serviceName, String methodName) { + return serviceName + "." + methodName; + } + + public Object execute(String serviceName, String methodName, Object... args) throws Throwable { + MethodHandle methodHandle = grpcMethodMap.get(generateGrpcMethodKey(serviceName, methodName)); + return methodHandle.invoke(args); + } + + public GrpcResponse execute(GrpcRequest request) throws Throwable { + String service = request.getService(); + String method = request.getMethod(); + } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java index edba98ea0a..bb003b5b5b 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java @@ -25,12 +25,12 @@ public class GrpcRequest { private Integer streamId; /** - * 请求的 path + * 请求的 service */ - private String path; + private String service; /** - * 请求的方法 + * 请求的 method */ private String method; @@ -43,7 +43,7 @@ public class GrpcRequest { public GrpcRequest(Integer streamId, String path,String method) { this.streamId = streamId; - this.path = path; + this.service = path; this.method = method; this.data = ByteUtil.getByteBuf(); } @@ -94,4 +94,24 @@ private byte[] decompressGzip(byte[] compressedData) { return compressedData; } } + + public Integer getStreamId() { + return streamId; + } + + public String getService() { + return service; + } + + public String getMethod() { + return method; + } + + public ByteBuf getData() { + return data; + } + + public int getDataLength() { + return dataLength; + } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java index c9f6f94142..13f5d0d396 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java @@ -22,6 +22,8 @@ */ public class Http2Handler extends SimpleChannelInboundHandler { + private GrpcDispatcher grpcDispatcher; + /** * 暂存收到的所有请求的数据 */ @@ -29,6 +31,10 @@ public class Http2Handler extends SimpleChannelInboundHandler { private static final String HEADER_PATH = ":path"; + public Http2Handler(GrpcDispatcher grpcDispatcher) { + this.grpcDispatcher = grpcDispatcher; + } + @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { super.channelRead(ctx, msg); @@ -65,12 +71,8 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) if (dataFrame.isEndStream()) { - - try { - - } catch (Exception e) { - throw new RuntimeException(e); - } + //TODO +// grpcDispatcher.execute() byte[] bytes = grpcRequest.readData(); ProtobufCodec requestCodec = ProtobufProxy.create(ArthasSampleRequest.class); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java index c94327a773..6d0751d501 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java @@ -16,5 +16,5 @@ @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface GrpcService { - String path() default ""; + String value() default ""; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java index 0e4317fca6..fb72f68a0e 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java @@ -14,7 +14,7 @@ * @date: 2024/6/30 下午11:43 * @description: ArthasSampleServiceImpl */ -@GrpcService(path = "arthasSample.ArthasTempService") +@GrpcService("arthasSample.ArthasTempService") public class ArthasSampleServiceImpl implements ArthasSampleService { @Override From cb669af0445f20e02135127519066e0c605ef581 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Fri, 13 Sep 2024 01:36:04 +0800 Subject: [PATCH 28/48] update: add grpc dispatcher --- .../grpc/server/handler/GrpcDispatcher.java | 14 ++++++++ .../grpc/server/handler/GrpcRequest.java | 27 ++++++++++---- .../grpc/server/handler/GrpcResponse.java | 36 +++++++++++++++++-- .../arthas/grpc/server/utils/ByteUtil.java | 13 +++++-- 4 files changed, 77 insertions(+), 13 deletions(-) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java index cb12620e69..a357983eba 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java @@ -5,6 +5,9 @@ import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; +import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; +import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; +import com.taobao.arthas.grpc.server.utils.ByteUtil; import com.taobao.arthas.grpc.server.utils.ReflectUtil; import java.lang.invoke.MethodHandle; @@ -65,6 +68,17 @@ public Object execute(String serviceName, String methodName, Object... args) thr public GrpcResponse execute(GrpcRequest request) throws Throwable { String service = request.getService(); String method = request.getMethod(); + MethodType type = grpcMethodMap.get(generateGrpcMethodKey(request.getService(), request.getMethod())).type(); + // protobuf 规范只能有单入参 + request.setClazz(type.parameterArray()[0]); + ProtobufCodec protobufCodec = ProtobufProxy.create(request.getClazz()); + Object decode = protobufCodec.decode(ByteUtil.getBytes(request.getByteData())); + Object execute = this.execute(service, method, decode); + + GrpcResponse grpcResponse = new GrpcResponse(); + grpcResponse.setClazz(type.returnType()); + grpcResponse.setByteData(ByteUtil.getByteBuf(protobufCodec.encode(execute))); + return grpcResponse; } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java index bb003b5b5b..cd6bfa865e 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java @@ -35,9 +35,14 @@ public class GrpcRequest { private String method; /** - * 数据 + * 二进制数据 */ - private ByteBuf data; + private ByteBuf byteData; + + /** + * 请求类型 + */ + private Class clazz; private int dataLength; @@ -45,7 +50,7 @@ public GrpcRequest(Integer streamId, String path,String method) { this.streamId = streamId; this.service = path; this.method = method; - this.data = ByteUtil.getByteBuf(); + this.byteData = ByteUtil.getByteBuf(); } public void writeData(ByteBuf byteBuf) { @@ -63,13 +68,13 @@ public void writeData(ByteBuf byteBuf) { dataLength += length; System.out.println(length); System.out.println(operateByteBuf.readableBytes()); - data.writeBytes(operateByteBuf); + byteData.writeBytes(operateByteBuf); } public byte[] readData() { System.out.println(dataLength); byte[] res = new byte[dataLength]; - data.readBytes(res); + byteData.readBytes(res); return res; } @@ -107,11 +112,19 @@ public String getMethod() { return method; } - public ByteBuf getData() { - return data; + public ByteBuf getByteData() { + return byteData; } public int getDataLength() { return dataLength; } + + public Class getClazz() { + return clazz; + } + + public void setClazz(Class clazz) { + this.clazz = clazz; + } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java index 59a2214c8f..e2e1cba61a 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java @@ -15,13 +15,19 @@ * @date: 2024/9/5 02:05 * @description: GrpcResponse */ -public class GrpcResponse { +public class GrpcResponse { private Map headers; - private ByteBuf data; + /** + * 二进制数据 + */ + private ByteBuf byteData; - private T genericsData; + /** + * 响应类型 + */ + private Class clazz; { headers = new HashMap<>(); @@ -39,4 +45,28 @@ public Http2Headers getEndHeader() { public Http2Headers getEndStreamHeader() { return new DefaultHttp2Headers().set("grpc-status", "0"); } + + public Map getHeaders() { + return headers; + } + + public ByteBuf getByteData() { + return byteData; + } + + public Class getClazz() { + return clazz; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public void setByteData(ByteBuf byteData) { + this.byteData = byteData; + } + + public void setClazz(Class clazz) { + this.clazz = clazz; + } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java index 7f04b11128..70c3bd7c03 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java @@ -22,8 +22,15 @@ public static ByteBuf getByteBuf(byte[] bytes) { } public static byte[] getBytes(ByteBuf buf) { - byte[] bytes = new byte[buf.readableBytes()]; - buf.readBytes(bytes); - return bytes; + if (buf.hasArray()) { + // 如果 ByteBuf 是一个支持底层数组的实现,直接获取数组 + return buf.array(); + } else { + // 创建一个新的 byte 数组 + byte[] bytes = new byte[buf.readableBytes()]; + // 将 ByteBuf 的内容复制到 byte 数组中 + buf.getBytes(buf.readerIndex(), bytes); + return bytes; + } } } From bfee0f1c2238181f52c8b37040b7d7cae48b8fc7 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Sat, 14 Sep 2024 01:37:09 +0800 Subject: [PATCH 29/48] update: complete grpc dispatcher --- .../grpc/server/handler/GrpcDispatcher.java | 13 ++--- .../grpc/server/handler/GrpcRequest.java | 4 +- .../grpc/server/handler/GrpcResponse.java | 26 ++++----- .../grpc/server/handler/Http2Handler.java | 55 ++++++------------- .../grpc/server/protobuf/ProtobufProxy.java | 17 ++---- .../server/service/ArthasSampleService.java | 1 + .../service/impl/ArthasSampleServiceImpl.java | 12 +++- .../arthas/grpc/server/utils/ByteUtil.java | 4 +- .../src/main/proto/arthasSample.proto | 3 +- 9 files changed, 56 insertions(+), 79 deletions(-) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java index a357983eba..a6b1bcefec 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java @@ -43,10 +43,9 @@ public void loadGrpcService() { Method[] declaredMethods = clazz.getDeclaredMethods(); for (Method method : declaredMethods) { if (method.isAnnotationPresent(GrpcMethod.class)) { - GrpcMethod grpcMethod = clazz.getAnnotation(GrpcMethod.class); + GrpcMethod grpcMethod = method.getAnnotation(GrpcMethod.class); MethodHandle methodHandle = lookup.unreflect(method); - methodHandle.bindTo(instance); - grpcMethodMap.put(generateGrpcMethodKey(grpcService.value(),grpcMethod.value()), methodHandle); + grpcMethodMap.put(generateGrpcMethodKey(grpcService.value(),grpcMethod.value()), methodHandle.bindTo(instance)); } } } catch (Exception e) { @@ -60,9 +59,9 @@ private String generateGrpcMethodKey(String serviceName, String methodName) { return serviceName + "." + methodName; } - public Object execute(String serviceName, String methodName, Object... args) throws Throwable { + public Object execute(String serviceName, String methodName, Object arg) throws Throwable { MethodHandle methodHandle = grpcMethodMap.get(generateGrpcMethodKey(serviceName, methodName)); - return methodHandle.invoke(args); + return methodHandle.invoke(arg); } public GrpcResponse execute(GrpcRequest request) throws Throwable { @@ -71,14 +70,14 @@ public GrpcResponse execute(GrpcRequest request) throws Throwable { MethodType type = grpcMethodMap.get(generateGrpcMethodKey(request.getService(), request.getMethod())).type(); // protobuf 规范只能有单入参 request.setClazz(type.parameterArray()[0]); - ProtobufCodec protobufCodec = ProtobufProxy.create(request.getClazz()); + ProtobufCodec protobufCodec = ProtobufProxy.getCodecCacheSide(request.getClazz()); Object decode = protobufCodec.decode(ByteUtil.getBytes(request.getByteData())); Object execute = this.execute(service, method, decode); GrpcResponse grpcResponse = new GrpcResponse(); grpcResponse.setClazz(type.returnType()); - grpcResponse.setByteData(ByteUtil.getByteBuf(protobufCodec.encode(execute))); + grpcResponse.writeResponseData(execute); return grpcResponse; } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java index cd6bfa865e..ef4bbdcee9 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java @@ -50,7 +50,7 @@ public GrpcRequest(Integer streamId, String path,String method) { this.streamId = streamId; this.service = path; this.method = method; - this.byteData = ByteUtil.getByteBuf(); + this.byteData = ByteUtil.newByteBuf(); } public void writeData(ByteBuf byteBuf) { @@ -62,7 +62,7 @@ public void writeData(ByteBuf byteBuf) { if (decompressedData == null) { return; } - ByteBuf operateByteBuf = ByteUtil.getByteBuf(decompressedData); + ByteBuf operateByteBuf = ByteUtil.newByteBuf(decompressedData); boolean compressed = operateByteBuf.readBoolean(); int length = operateByteBuf.readInt(); dataLength += length; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java index e2e1cba61a..bed596dfa0 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java @@ -3,10 +3,14 @@ * @date: 2024/9/5 02:05 */ +import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; +import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; +import com.taobao.arthas.grpc.server.utils.ByteUtil; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http2.DefaultHttp2Headers; import io.netty.handler.codec.http2.Http2Headers; +import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -46,24 +50,18 @@ public Http2Headers getEndStreamHeader() { return new DefaultHttp2Headers().set("grpc-status", "0"); } - public Map getHeaders() { - return headers; - } - - public ByteBuf getByteData() { + public ByteBuf getResponseData(){ return byteData; } - public Class getClazz() { - return clazz; - } - - public void setHeaders(Map headers) { - this.headers = headers; - } + public void writeResponseData(Object response) throws IOException { + ProtobufCodec codec = ProtobufProxy.getCodecCacheSide(clazz); + byte[] encode = codec.encode(response); - public void setByteData(ByteBuf byteData) { - this.byteData = byteData; + this.byteData = ByteUtil.newByteBuf(); + this.byteData.writeBoolean(false); + this.byteData.writeInt(encode.length); + this.byteData.writeBytes(encode); } public void setClazz(Class clazz) { diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java index 13f5d0d396..51c976e6cd 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java @@ -70,45 +70,22 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) grpcRequest.writeData(dataFrame.content()); if (dataFrame.isEndStream()) { - - //TODO -// grpcDispatcher.execute() - - byte[] bytes = grpcRequest.readData(); - ProtobufCodec requestCodec = ProtobufProxy.create(ArthasSampleRequest.class); - ProtobufCodec responseCodec = ProtobufProxy.create(ArthasSampleResponse.class); - - ArthasSampleRequest decode = requestCodec.decode(bytes); - - System.out.println(decode); - - ArthasSampleResponse arthasSampleResponse = new ArthasSampleResponse(); - arthasSampleResponse.setMessage("Hello ArthasSample!"); - byte[] responseData = responseCodec.encode(arthasSampleResponse); - - - Http2Headers endHeader = new DefaultHttp2Headers() - .status("200"); -// .set("content-type", "application/grpc") -// .set("grpc-encoding", "identity") -// .set("grpc-accept-encoding", "identity,deflate,gzip"); - ctx.write(new DefaultHttp2HeadersFrame(endHeader).stream(dataFrame.stream())); - - ByteBuf buffer = ctx.alloc().buffer(); - buffer.writeBoolean(false); - buffer.writeInt(responseData.length); - buffer.writeBytes(responseData); - System.out.println(responseData.length); - DefaultHttp2DataFrame resDataFrame = new DefaultHttp2DataFrame(buffer).stream(dataFrame.stream()); - ctx.write(resDataFrame); - - - Http2Headers endStream = new DefaultHttp2Headers() - .set("grpc-status", "0"); - DefaultHttp2HeadersFrame endStreamFrame = new DefaultHttp2HeadersFrame(endStream, true).stream(dataFrame.stream()); - ctx.writeAndFlush(endStreamFrame); - } else { - + try { + GrpcResponse response = grpcDispatcher.execute(grpcRequest); + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(dataFrame.stream())); + ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(dataFrame.stream())); + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(dataFrame.stream())); + } catch (Throwable e) { + processError(ctx); + + } } } + + private void processError(ChannelHandlerContext ctx){ + // TODO +// ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(dataFrame.stream())); +// ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(dataFrame.stream())); +// ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(dataFrame.stream())); + } } \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java index 4a16baa218..181abb0ae7 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java @@ -32,11 +32,7 @@ public class ProtobufProxy { private static List protobufFields; - public ProtobufProxy(Class clazz) { - - } - - public ProtobufCodec getCodecCacheSide(Class clazz) { + public static ProtobufCodec getCodecCacheSide(Class clazz) { ProtobufCodec codec = codecCache.get(clazz.getName()); if (codec != null) { return codec; @@ -101,9 +97,9 @@ private static void processImportBlock() { imports.add("java.util.*"); imports.add("java.io.IOException"); imports.add("java.lang.reflect.*"); - imports.add("com.taobao.arthas.grpc.protobuf.*"); - imports.add("com.taobao.arthas.grpc.protobuf.utils.*"); - imports.add("com.taobao.arthas.grpc.protobuf.annotation.*"); + imports.add("com.taobao.arthas.grpc.server.protobuf.*"); + imports.add("com.taobao.arthas.grpc.server.protobuf.utils.*"); + imports.add("com.taobao.arthas.grpc.server.protobuf.annotation.*"); imports.add("com.google.protobuf.*"); imports.add(clazz.getCanonicalName()); for (String pkg : imports) { @@ -368,9 +364,4 @@ private static void loadProtobufField() { clazz.getAnnotation(ProtobufEnableZigZap.class) != null ); } - - public static void main(String[] args) { - ProtobufCodec protobufCodec = ProtobufProxy.create(ArthasSampleRequest.class); - } - } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java index 992ce0b80f..79c6499f58 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java @@ -13,4 +13,5 @@ */ public interface ArthasSampleService { ArthasSampleResponse trace(ArthasSampleRequest command); + ArthasSampleResponse watch(ArthasSampleRequest command); } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java index fb72f68a0e..c0fdce3d73 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java @@ -20,6 +20,16 @@ public class ArthasSampleServiceImpl implements ArthasSampleService { @Override @GrpcMethod("trace") public ArthasSampleResponse trace(ArthasSampleRequest command) { - return new ArthasSampleResponse(); + ArthasSampleResponse arthasSampleResponse = new ArthasSampleResponse(); + arthasSampleResponse.setMessage("trace"); + return arthasSampleResponse; + } + + @Override + @GrpcMethod("watch") + public ArthasSampleResponse watch(ArthasSampleRequest command) { + ArthasSampleResponse arthasSampleResponse = new ArthasSampleResponse(); + arthasSampleResponse.setMessage("watch"); + return arthasSampleResponse; } } \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java index 70c3bd7c03..29044d36e6 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java @@ -13,11 +13,11 @@ */ public class ByteUtil { - public static ByteBuf getByteBuf() { + public static ByteBuf newByteBuf() { return PooledByteBufAllocator.DEFAULT.buffer(); } - public static ByteBuf getByteBuf(byte[] bytes) { + public static ByteBuf newByteBuf(byte[] bytes) { return PooledByteBufAllocator.DEFAULT.buffer(bytes.length).writeBytes(bytes); } diff --git a/arthas-grpc-server/src/main/proto/arthasSample.proto b/arthas-grpc-server/src/main/proto/arthasSample.proto index c74da27e31..5e31b25c21 100644 --- a/arthas-grpc-server/src/main/proto/arthasSample.proto +++ b/arthas-grpc-server/src/main/proto/arthasSample.proto @@ -9,7 +9,8 @@ enum StatusEnum { } service ArthasTempService { - rpc SayHello(ArthasSampleRequest) returns (ArthasSampleResponse); + rpc trace(ArthasSampleRequest) returns (ArthasSampleResponse); + rpc watch(ArthasSampleRequest) returns (ArthasSampleResponse); } message ArthasSampleRequest { From 96251c569ac4120f5cd1bb39cacaba668d226963 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Mon, 16 Sep 2024 02:46:27 +0800 Subject: [PATCH 30/48] update: add stream handler --- .../taobao/arthas/grpc/server/GrpcTest.java | 32 ++++++++ .../com/taobao/arthas/grpc/server/Main.java | 73 ------------------- .../grpc/server/handler/GrpcDispatcher.java | 20 ++++- .../grpc/server/handler/GrpcRequest.java | 27 ++++--- .../grpc/server/handler/GrpcResponse.java | 2 +- .../grpc/server/handler/Http2Handler.java | 29 +++++--- .../server/handler/annotation/GrpcMethod.java | 1 + .../service/impl/ArthasSampleServiceImpl.java | 5 +- .../arthas/grpc/server/temp/TempImpl.java | 38 ++++++++-- .../src/main/proto/arthasSample.proto | 5 +- 10 files changed, 123 insertions(+), 109 deletions(-) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/GrpcTest.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/Main.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/GrpcTest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/GrpcTest.java new file mode 100644 index 0000000000..a32512798f --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/GrpcTest.java @@ -0,0 +1,32 @@ +package com.taobao.arthas.grpc.server; + +import io.grpc.Server; +import io.grpc.ServerBuilder; + +import java.io.IOException; + +import com.taobao.arthas.grpc.server.temp.TempImpl; + +/** + * @author: 風楪 + * @date: 2024/6/30 上午1:22 + */ +public class GrpcTest { + + private static Server server; + + public static void main(String[] args) throws Throwable { + start(); + blockUntilShutdown(); + } + public static void start() throws IOException { + ServerBuilder builder = ServerBuilder.forPort(9090) + .addService(new TempImpl()); + server = builder.build(); + server.start(); + } + + public static void blockUntilShutdown() throws InterruptedException { + server.awaitTermination(); + } +} \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/Main.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/Main.java deleted file mode 100644 index 2c1388884c..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/Main.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.taobao.arthas.grpc.server; - -import com.taobao.arthas.grpc.server.temp.TempImpl; -import io.grpc.Server; -import io.grpc.ServerBuilder; - -import java.io.File; -import java.io.IOException; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.lang.reflect.Method; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -/** - * @author: 風楪 - * @date: 2024/6/30 上午1:22 - */ -public class Main { - - private static final String TEMPLATE_FILE = "/class_template.tpl"; - - private static Server server; - - public static void main(String[] args) throws Throwable { - MethodHandles.Lookup lookup = MethodHandles.lookup(); - - Method plus1 = Main.class.getDeclaredMethod("plus", int.class, int.class); - - MethodHandle unreflect = lookup.unreflect(plus1); - - System.out.println(unreflect.invoke(new Main(), 1, 2)); - } - - private static List> findClasses(String packageName) { - List> classes = new ArrayList<>(); - String path = packageName.replace('.', '/'); - try { - URL resource = Thread.currentThread().getContextClassLoader().getResource(path); - if (resource != null) { - File directory = new File(resource.toURI()); - if (directory.exists()) { - for (File file : directory.listFiles()) { - if (file.isFile() && file.getName().endsWith(".class")) { - String className = packageName + '.' + file.getName().replace(".class", ""); - classes.add(Class.forName(className)); - } - } - } - } - } catch (Exception e) { - - } - return classes; - } - - public int plus(int a,int b){ - return a+b; - } - - public static void start() throws IOException { - ServerBuilder builder = ServerBuilder.forPort(9090) - .addService(new TempImpl()); - server = builder.build(); - server.start(); - } - - public void blockUntilShutdown() throws InterruptedException { - server.awaitTermination(); - } -} \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java index a6b1bcefec..a7d07d2a6e 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java @@ -17,6 +17,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; /** * @author: FengYe @@ -27,7 +28,9 @@ public class GrpcDispatcher { private static final String GRPC_SERVICE_PACKAGE_NAME = "com.taobao.arthas.grpc.server.service.impl"; - private Map grpcMethodMap = new HashMap<>(); + private Map grpcMethodInvokeMap = new HashMap<>(); + + private Map grpcMethodStreamMap = new HashMap<>(); public void loadGrpcService() { List> classes = ReflectUtil.findClasses(GRPC_SERVICE_PACKAGE_NAME); @@ -45,7 +48,9 @@ public void loadGrpcService() { if (method.isAnnotationPresent(GrpcMethod.class)) { GrpcMethod grpcMethod = method.getAnnotation(GrpcMethod.class); MethodHandle methodHandle = lookup.unreflect(method); - grpcMethodMap.put(generateGrpcMethodKey(grpcService.value(),grpcMethod.value()), methodHandle.bindTo(instance)); + String grpcMethodKey = generateGrpcMethodKey(grpcService.value(), grpcMethod.value()); + grpcMethodInvokeMap.put(grpcMethodKey, methodHandle.bindTo(instance)); + grpcMethodStreamMap.put(grpcMethodKey, grpcMethod.stream()); } } } catch (Exception e) { @@ -60,14 +65,14 @@ private String generateGrpcMethodKey(String serviceName, String methodName) { } public Object execute(String serviceName, String methodName, Object arg) throws Throwable { - MethodHandle methodHandle = grpcMethodMap.get(generateGrpcMethodKey(serviceName, methodName)); + MethodHandle methodHandle = grpcMethodInvokeMap.get(generateGrpcMethodKey(serviceName, methodName)); return methodHandle.invoke(arg); } public GrpcResponse execute(GrpcRequest request) throws Throwable { String service = request.getService(); String method = request.getMethod(); - MethodType type = grpcMethodMap.get(generateGrpcMethodKey(request.getService(), request.getMethod())).type(); + MethodType type = grpcMethodInvokeMap.get(generateGrpcMethodKey(request.getService(), request.getMethod())).type(); // protobuf 规范只能有单入参 request.setClazz(type.parameterArray()[0]); ProtobufCodec protobufCodec = ProtobufProxy.getCodecCacheSide(request.getClazz()); @@ -80,4 +85,11 @@ public GrpcResponse execute(GrpcRequest request) throws Throwable { grpcResponse.writeResponseData(execute); return grpcResponse; } + + public void checkGrpcStream(GrpcRequest request){ + request.setStream( + Optional.ofNullable(grpcMethodStreamMap.get(generateGrpcMethodKey(request.getService(), request.getMethod()))) + .orElse(false) + ); + } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java index ef4bbdcee9..9ad04a0c68 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java @@ -40,11 +40,14 @@ public class GrpcRequest { private ByteBuf byteData; /** - * 请求类型 + * 请求class */ private Class clazz; - private int dataLength; + /** + * 是否是 grpc 流式请求 + */ + private boolean stream; public GrpcRequest(Integer streamId, String path,String method) { this.streamId = streamId; @@ -63,17 +66,15 @@ public void writeData(ByteBuf byteBuf) { return; } ByteBuf operateByteBuf = ByteUtil.newByteBuf(decompressedData); + + // 必须要先把这5个字节读出来,后续才是真正的data boolean compressed = operateByteBuf.readBoolean(); int length = operateByteBuf.readInt(); - dataLength += length; - System.out.println(length); - System.out.println(operateByteBuf.readableBytes()); byteData.writeBytes(operateByteBuf); } public byte[] readData() { - System.out.println(dataLength); - byte[] res = new byte[dataLength]; + byte[] res = new byte[byteData.readableBytes()]; byteData.readBytes(res); return res; } @@ -116,10 +117,6 @@ public ByteBuf getByteData() { return byteData; } - public int getDataLength() { - return dataLength; - } - public Class getClazz() { return clazz; } @@ -127,4 +124,12 @@ public Class getClazz() { public void setClazz(Class clazz) { this.clazz = clazz; } + + public boolean isStream() { + return stream; + } + + public void setStream(boolean stream) { + this.stream = stream; + } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java index bed596dfa0..bbdaef78df 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java @@ -29,7 +29,7 @@ public class GrpcResponse { private ByteBuf byteData; /** - * 响应类型 + * 响应class */ private Class clazz; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java index 51c976e6cd..70c9ff52e1 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java @@ -61,7 +61,9 @@ private void handleGrpcRequest(Http2HeadersFrame headersFrame, ChannelHandlerCon String path = headersFrame.headers().get(HEADER_PATH).toString(); // 去掉前面的斜杠,然后按斜杠分割 String[] parts = path.substring(1).split("/"); - dataBuffer.put(id, new GrpcRequest(headersFrame.stream().id(), parts[0], parts[1])); + GrpcRequest grpcRequest = new GrpcRequest(headersFrame.stream().id(), parts[0], parts[1]); + grpcDispatcher.checkGrpcStream(grpcRequest); + dataBuffer.put(id, grpcRequest); System.out.println("Received headers: " + headersFrame.headers()); } @@ -69,17 +71,24 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) GrpcRequest grpcRequest = dataBuffer.get(dataFrame.stream().id()); grpcRequest.writeData(dataFrame.content()); - if (dataFrame.isEndStream()) { - try { - GrpcResponse response = grpcDispatcher.execute(grpcRequest); - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(dataFrame.stream())); - ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(dataFrame.stream())); - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(dataFrame.stream())); - } catch (Throwable e) { - processError(ctx); - + if (grpcRequest.isStream()) { + // 流式调用,即刻响应 + // todo + } else { + // 非流式调用,等到 endStream 再响应 + if (dataFrame.isEndStream()) { + try { + GrpcResponse response = grpcDispatcher.execute(grpcRequest); + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(dataFrame.stream())); + ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(dataFrame.stream())); + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(dataFrame.stream())); + } catch (Throwable e) { + processError(ctx); + } } } + + } private void processError(ChannelHandlerContext ctx){ diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java index ef35716913..d109fdce59 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java @@ -17,4 +17,5 @@ @Retention(RetentionPolicy.RUNTIME) public @interface GrpcMethod { String value() default ""; + boolean stream() default false; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java index c0fdce3d73..0d88de0501 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java @@ -26,10 +26,11 @@ public ArthasSampleResponse trace(ArthasSampleRequest command) { } @Override - @GrpcMethod("watch") + @GrpcMethod(value = "watch", stream = true) public ArthasSampleResponse watch(ArthasSampleRequest command) { + String name = command.getName(); ArthasSampleResponse arthasSampleResponse = new ArthasSampleResponse(); - arthasSampleResponse.setMessage("watch"); + arthasSampleResponse.setMessage(name); return arthasSampleResponse; } } \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/temp/TempImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/temp/TempImpl.java index 481b085368..2a6247888f 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/temp/TempImpl.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/temp/TempImpl.java @@ -1,22 +1,50 @@ package com.taobao.arthas.grpc.server.temp;/** * @author: 風楪 - * @date: 2024/8/13 01:57 + * @date: 2024/9/16 01:59 */ + import arthasSample.ArthasSample; import arthasSample.ArthasTempServiceGrpc; import io.grpc.stub.StreamObserver; /** * @author: FengYe - * @date: 2024/8/13 01:57 + * @date: 2024/9/16 01:59 * @description: TempImpl */ public class TempImpl extends ArthasTempServiceGrpc.ArthasTempServiceImplBase { + @Override - public void sayHello(ArthasSample.ArthasSampleRequest request, StreamObserver responseObserver) { - ArthasSample.ArthasSampleResponse build = ArthasSample.ArthasSampleResponse.newBuilder().setMessage("Hello ArthasSample!").build(); - responseObserver.onNext(build); + public void trace(ArthasSample.ArthasSampleRequest request, StreamObserver responseObserver) { + ArthasSample.ArthasSampleResponse.Builder builder = ArthasSample.ArthasSampleResponse.newBuilder(); + builder.setMessage("trace"); + responseObserver.onNext(builder.build()); responseObserver.onCompleted(); } + + @Override + public StreamObserver watch(StreamObserver responseObserver) { + return new StreamObserver() { + @Override + public void onNext(ArthasSample.ArthasSampleRequest value) { + + // 回应客户端 + ArthasSample.ArthasSampleResponse response = ArthasSample.ArthasSampleResponse.newBuilder() + .setMessage(value.getName()) + .build(); + responseObserver.onNext(response); + } + + @Override + public void onError(Throwable t) { + System.err.println("Error: " + t); + } + + @Override + public void onCompleted() { + responseObserver.onCompleted(); + } + }; + } } diff --git a/arthas-grpc-server/src/main/proto/arthasSample.proto b/arthas-grpc-server/src/main/proto/arthasSample.proto index 5e31b25c21..3e14f5eec9 100644 --- a/arthas-grpc-server/src/main/proto/arthasSample.proto +++ b/arthas-grpc-server/src/main/proto/arthasSample.proto @@ -10,12 +10,11 @@ enum StatusEnum { service ArthasTempService { rpc trace(ArthasSampleRequest) returns (ArthasSampleResponse); - rpc watch(ArthasSampleRequest) returns (ArthasSampleResponse); + rpc watch(stream ArthasSampleRequest) returns (stream ArthasSampleResponse); } message ArthasSampleRequest { - string name = 1; - double age = 2; + string name = 1; double age = 2; int64 price = 3; StatusEnum status = 4; repeated TestClass testList = 5; From b2c289f083aab1c865488f95d7b6d67140ca9bf2 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Tue, 17 Sep 2024 03:36:58 +0800 Subject: [PATCH 31/48] update: add stream handler --- arthas-grpc-server/pom.xml | 105 +++++------ .../arthas/grpc/server/ArthasGrpcServer.java | 49 ++--- .../taobao/arthas/grpc/server/GrpcTest.java | 32 ---- .../grpc/server/handler/GrpcDispatcher.java | 3 +- .../grpc/server/handler/GrpcRequest.java | 25 ++- .../grpc/server/handler/Http2Handler.java | 36 ++-- .../grpc/server/protobuf/ProtobufProxy.java | 83 +++++---- .../protobuf/utils/ProtoBufClassCompiler.java | 24 +-- .../{FieldUtil.java => ProtoBufUtil.java} | 170 ++++++++++++++++-- .../arthas/grpc/server/temp/TempImpl.java | 50 ------ 10 files changed, 314 insertions(+), 263 deletions(-) delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/GrpcTest.java rename arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/{FieldUtil.java => ProtoBufUtil.java} (87%) delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/temp/TempImpl.java diff --git a/arthas-grpc-server/pom.xml b/arthas-grpc-server/pom.xml index 7edcc6739d..7045441714 100644 --- a/arthas-grpc-server/pom.xml +++ b/arthas-grpc-server/pom.xml @@ -42,31 +42,12 @@ 4.1.111.Final - - - - - - - - - - - - - - - - - io.grpc - grpc-netty - - io.grpc - grpc-services + com.google.protobuf + protobuf-java + 4.27.2 - org.slf4j @@ -88,43 +69,53 @@ provided - - com.baidu - jprotobuf - 2.4.20 - + + + + + + + + + + + + + + + - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - 0.6.1 - - ${basedir}/src/main/proto - com.google.protobuf:protoc:3.11.0:exe:${os.detected.classifier} - grpc-java - io.grpc:protoc-gen-grpc-java:1.28.0:exe:${os.detected.classifier} - - - - - compile - compile-custom - - - - - - - - kr.motd.maven - os-maven-plugin - 1.4.1.Final - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java index 91b54455c0..07fc1a9718 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java @@ -5,8 +5,6 @@ import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; import com.taobao.arthas.grpc.server.handler.Http2Handler; -import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; -import com.taobao.arthas.grpc.server.utils.ReflectUtil; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; @@ -15,19 +13,10 @@ import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http2.Http2FrameCodecBuilder; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; - import java.io.File; import java.io.IOException; -import java.lang.reflect.Method; -import java.net.URL; import java.nio.file.Files; import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; /** * @author: FengYe @@ -36,27 +25,25 @@ */ public class ArthasGrpcServer { - public static void main(String[] args) throws Exception { - //自签名生成密钥 - SelfSignedCertificate ssc = new SelfSignedCertificate(); - SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .build(); - - - // 指定生成自签名证书和密钥的位置 - File certFile = new File(System.getProperty("user.dir"),"certificate.crt"); - File keyFile = new File(System.getProperty("user.dir"),"privateKey.key"); - - // 将生成的证书和私钥移动到指定位置 - moveFile(ssc.certificate(), certFile); - moveFile(ssc.privateKey(), keyFile); - - System.out.println(certFile.getAbsolutePath()); - System.out.println(keyFile.getAbsolutePath()); - - System.out.println("Certificate: " + ssc.certificate()); - System.out.println("Private Key: " + ssc.privateKey()); +// //自签名生成密钥 +// SelfSignedCertificate ssc = new SelfSignedCertificate(); +// SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) +// .build(); +// +// // 指定生成自签名证书和密钥的位置 +// File certFile = new File(System.getProperty("user.dir"),"certificate.crt"); +// File keyFile = new File(System.getProperty("user.dir"),"privateKey.key"); +// +// // 将生成的证书和私钥移动到指定位置 +// moveFile(ssc.certificate(), certFile); +// moveFile(ssc.privateKey(), keyFile); +// +// System.out.println(certFile.getAbsolutePath()); +// System.out.println(keyFile.getAbsolutePath()); +// +// System.out.println("Certificate: " + ssc.certificate()); +// System.out.println("Private Key: " + ssc.privateKey()); EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/GrpcTest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/GrpcTest.java deleted file mode 100644 index a32512798f..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/GrpcTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.taobao.arthas.grpc.server; - -import io.grpc.Server; -import io.grpc.ServerBuilder; - -import java.io.IOException; - -import com.taobao.arthas.grpc.server.temp.TempImpl; - -/** - * @author: 風楪 - * @date: 2024/6/30 上午1:22 - */ -public class GrpcTest { - - private static Server server; - - public static void main(String[] args) throws Throwable { - start(); - blockUntilShutdown(); - } - public static void start() throws IOException { - ServerBuilder builder = ServerBuilder.forPort(9090) - .addService(new TempImpl()); - server = builder.build(); - server.start(); - } - - public static void blockUntilShutdown() throws InterruptedException { - server.awaitTermination(); - } -} \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java index a7d07d2a6e..38b36fabac 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java @@ -76,7 +76,7 @@ public GrpcResponse execute(GrpcRequest request) throws Throwable { // protobuf 规范只能有单入参 request.setClazz(type.parameterArray()[0]); ProtobufCodec protobufCodec = ProtobufProxy.getCodecCacheSide(request.getClazz()); - Object decode = protobufCodec.decode(ByteUtil.getBytes(request.getByteData())); + Object decode = protobufCodec.decode(request.readData()); Object execute = this.execute(service, method, decode); @@ -91,5 +91,6 @@ public void checkGrpcStream(GrpcRequest request){ Optional.ofNullable(grpcMethodStreamMap.get(generateGrpcMethodKey(request.getService(), request.getMethod()))) .orElse(false) ); + request.setStreamFirstData(true); } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java index 9ad04a0c68..324bcff280 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java @@ -49,6 +49,11 @@ public class GrpcRequest { */ private boolean stream; + /** + * 是否是 grpc 流式请求的第一个data + */ + private boolean streamFirstData; + public GrpcRequest(Integer streamId, String path,String method) { this.streamId = streamId; this.service = path; @@ -73,10 +78,16 @@ public void writeData(ByteBuf byteBuf) { byteData.writeBytes(operateByteBuf); } + /** + * 只读取数据而不操作 byteData + * @return + */ public byte[] readData() { - byte[] res = new byte[byteData.readableBytes()]; - byteData.readBytes(res); - return res; + return ByteUtil.getBytes(byteData); + } + + public void clearData(){ + byteData.clear(); } private byte[] decompressGzip(byte[] compressedData) { @@ -132,4 +143,12 @@ public boolean isStream() { public void setStream(boolean stream) { this.stream = stream; } + + public boolean isStreamFirstData() { + return streamFirstData; + } + + public void setStreamFirstData(boolean streamFirstData) { + this.streamFirstData = streamFirstData; + } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java index 70c9ff52e1..d69bc10cff 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java @@ -3,11 +3,6 @@ * @date: 2024/7/7 下午9:58 */ -import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; -import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; -import com.taobao.arthas.grpc.server.service.req.ArthasSampleRequest; -import com.taobao.arthas.grpc.server.service.res.ArthasSampleResponse; -import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http2.*; @@ -73,7 +68,24 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) if (grpcRequest.isStream()) { // 流式调用,即刻响应 - // todo + try { + GrpcResponse response = grpcDispatcher.execute(grpcRequest); + grpcRequest.clearData(); + + // 针对第一个响应发送 header + if (grpcRequest.isStreamFirstData()) { + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(dataFrame.stream())); + grpcRequest.setStreamFirstData(false); + } + + ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(dataFrame.stream())); + + if (dataFrame.isEndStream()) { + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(dataFrame.stream())); + } + } catch (Throwable e) { + processError(ctx, e); + } } else { // 非流式调用,等到 endStream 再响应 if (dataFrame.isEndStream()) { @@ -83,18 +95,14 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(dataFrame.stream())); ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(dataFrame.stream())); } catch (Throwable e) { - processError(ctx); + processError(ctx, e); } } } - - } - private void processError(ChannelHandlerContext ctx){ - // TODO -// ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(dataFrame.stream())); -// ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(dataFrame.stream())); -// ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(dataFrame.stream())); + private void processError(ChannelHandlerContext ctx, Throwable e) { + //todo + ctx.writeAndFlush(e.getMessage()); } } \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java index 181abb0ae7..fb0101acb8 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java @@ -7,10 +7,9 @@ import com.google.protobuf.WireFormat; import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufEnableZigZap; import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; -import com.taobao.arthas.grpc.server.protobuf.utils.FieldUtil; +import com.taobao.arthas.grpc.server.protobuf.utils.ProtoBufUtil; import com.taobao.arthas.grpc.server.protobuf.utils.MiniTemplator; import com.taobao.arthas.grpc.server.protobuf.utils.ProtoBufClassCompiler; -import com.taobao.arthas.grpc.server.service.req.ArthasSampleRequest; import java.lang.reflect.InvocationTargetException; import java.util.*; @@ -66,7 +65,7 @@ public static ProtobufCodec create(Class clazz) { processImportBlock(); - miniTemplator.setVariable("className", FieldUtil.getClassName(clazz) + "$$ProxyClass"); + miniTemplator.setVariable("className", ProtoBufUtil.getClassName(clazz) + "$$ProxyClass"); miniTemplator.setVariable("codecClassName", ProtobufCodec.class.getName()); miniTemplator.setVariable("targetProxyClassName", clazz.getCanonicalName()); processEncodeBlock(); @@ -75,7 +74,7 @@ public static ProtobufCodec create(Class clazz) { String code = miniTemplator.generateOutput(); ProtoBufClassCompiler protoBufClassCompiler = new ProtoBufClassCompiler(ProtoBufClassCompiler.class.getClassLoader()); - String fullClassName = FieldUtil.getFullClassName(clazz) + "$$ProxyClass"; + String fullClassName = ProtoBufUtil.getFullClassName(clazz) + "$$ProxyClass"; Class newClass = protoBufClassCompiler.compile(fullClassName, code, clazz.getClassLoader()); try { @@ -110,16 +109,16 @@ private static void processImportBlock() { private static void processEncodeBlock() { for (ProtobufField protobufField : protobufFields) { - String dynamicFieldGetter = FieldUtil.getGetterDynamicString(protobufField, clazz); + String dynamicFieldGetter = ProtoBufUtil.getGetterDynamicString(protobufField, clazz); String dynamicFieldType = protobufField.isList() ? "Collection" : protobufField.getProtobufFieldType().getJavaType(); - String dynamicFieldName = FieldUtil.getDynamicFieldName(protobufField.getOrder()); + String dynamicFieldName = ProtoBufUtil.getDynamicFieldName(protobufField.getOrder()); miniTemplator.setVariable("dynamicFieldType", dynamicFieldType); miniTemplator.setVariable("dynamicFieldName", dynamicFieldName); miniTemplator.setVariable("dynamicFieldGetter", dynamicFieldGetter); - String sizeDynamicString = FieldUtil.getSizeDynamicString(protobufField); + String sizeDynamicString = ProtoBufUtil.getSizeDynamicString(protobufField); miniTemplator.setVariable("sizeDynamicString", sizeDynamicString); - miniTemplator.setVariable("encodeWriteFieldValue", FieldUtil.getWriteByteDynamicString(protobufField)); + miniTemplator.setVariable("encodeWriteFieldValue", ProtoBufUtil.getWriteByteDynamicString(protobufField)); miniTemplator.addBlock("encodeFields"); } } @@ -141,7 +140,7 @@ private static void processDecodeBlock() { express = "new HashMap()"; } if (isList || isMap) { - initListMapFields.append(FieldUtil.getInitListMapFieldDynamicString(protobufField, express)); + initListMapFields.append(ProtoBufUtil.getInitListMapFieldDynamicString(protobufField, express)); } if (protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.ENUM) { @@ -149,7 +148,7 @@ private static void processDecodeBlock() { if (!isList) { express = "FieldUtil.getEnumValue(" + clsName + ".class, " + clsName + ".values()[0].name())"; // add set get method - String setToField = FieldUtil.getSetFieldDynamicString(protobufField, clazz, express); + String setToField = ProtoBufUtil.getSetFieldDynamicString(protobufField, clazz, express); miniTemplator.setVariable("enumInitialize", setToField); miniTemplator.addBlock("enumFields"); } @@ -163,7 +162,7 @@ private static void processDecodeBlock() { for (ProtobufField protobufField : protobufFields) { boolean isList = protobufField.isList(); String t = protobufField.getProtobufFieldType().getType(); - t = FieldUtil.capitalize(t); + t = ProtoBufUtil.capitalize(t); boolean listTypeCheck = false; String express; @@ -172,7 +171,7 @@ private static void processDecodeBlock() { String decodeOrder = "-1"; if (protobufField.getProtobufFieldType() != ProtobufFieldTypeEnum.DEFAULT) { - decodeOrder = FieldUtil.makeTag(protobufField.getOrder(), + decodeOrder = ProtoBufUtil.makeTag(protobufField.getOrder(), protobufField.getProtobufFieldType().getInternalFieldType().getWireType()) + ""; } else { decodeOrder = "FieldUtil.makeTag(" + protobufField.getOrder() + ",WireFormat." @@ -209,12 +208,12 @@ private static void processDecodeBlock() { checkObjectType(protobufField, cls); code.append("codec = ProtobufProxy.create(").append(cls.getCanonicalName()).append(".class"); - code.append(")").append(FieldUtil.JAVA_LINE_BREAK); + code.append(")").append(ProtoBufUtil.JAVA_LINE_BREAK); objectDecodeExpress = code.toString(); code.setLength(0); - objectDecodeExpress += "int length = input.readRawVarint32()" + FieldUtil.JAVA_LINE_BREAK; - objectDecodeExpress += "final int oldLimit = input.pushLimit(length)" + FieldUtil.JAVA_LINE_BREAK; + objectDecodeExpress += "int length = input.readRawVarint32()" + ProtoBufUtil.JAVA_LINE_BREAK; + objectDecodeExpress += "final int oldLimit = input.pushLimit(length)" + ProtoBufUtil.JAVA_LINE_BREAK; listTypeCheck = true; express = "(" + cls.getCanonicalName() + ") codec.readFrom(input)"; @@ -228,16 +227,16 @@ private static void processDecodeBlock() { code.append("EnumHandler<").append(enumClassName).append("> keyhandler"); code.append("= new EnumHandler"); code.append("<").append(enumClassName).append(">() {"); - code.append(FieldUtil.LINE_BREAK); + code.append(ProtoBufUtil.LINE_BREAK); code.append("public ").append(enumClassName).append(" handle(int value) {"); - code.append(FieldUtil.LINE_BREAK); + code.append(ProtoBufUtil.LINE_BREAK); code.append("String enumName = FieldUtil.getEnumName(").append(enumClassName) .append(".values(), value)"); - code.append(FieldUtil.JAVA_LINE_BREAK); + code.append(ProtoBufUtil.JAVA_LINE_BREAK); code.append("return ").append(enumClassName).append(".valueOf(enumName)"); - code.append(FieldUtil.JAVA_LINE_BREAK); + code.append(ProtoBufUtil.JAVA_LINE_BREAK); code.append("}}"); - code.append(FieldUtil.JAVA_LINE_BREAK); + code.append(ProtoBufUtil.JAVA_LINE_BREAK); } if (protobufField.isEnumValueType()) { @@ -245,23 +244,23 @@ private static void processDecodeBlock() { code.append("EnumHandler<").append(enumClassName).append("> handler"); code.append("= new EnumHandler"); code.append("<").append(enumClassName).append(">() {"); - code.append(FieldUtil.LINE_BREAK); + code.append(ProtoBufUtil.LINE_BREAK); code.append("public ").append(enumClassName).append(" handle(int value) {"); - code.append(FieldUtil.LINE_BREAK); + code.append(ProtoBufUtil.LINE_BREAK); code.append("String enumName = FieldUtil.getEnumName(").append(enumClassName) .append(".values(), value)"); - code.append(FieldUtil.JAVA_LINE_BREAK); + code.append(ProtoBufUtil.JAVA_LINE_BREAK); code.append("return ").append(enumClassName).append(".valueOf(enumName)"); - code.append(FieldUtil.JAVA_LINE_BREAK); + code.append(ProtoBufUtil.JAVA_LINE_BREAK); code.append("}}"); - code.append(FieldUtil.JAVA_LINE_BREAK); + code.append(ProtoBufUtil.JAVA_LINE_BREAK); } objectDecodeExpress = code.toString(); code.setLength(0); express = "FieldUtil.putMapValue(input, " + getMapCommand + ","; - express += FieldUtil.getMapFieldGenericParameterString(protobufField); + express += ProtoBufUtil.getMapFieldGenericParameterString(protobufField); if (protobufField.isEnumKeyType()) { express += ", keyhandler"; } else { @@ -286,12 +285,12 @@ private static void processDecodeBlock() { // class code.append("codec = ProtobufProxy.create(").append(name).append(".class"); - code.append(")").append(FieldUtil.JAVA_LINE_BREAK); + code.append(")").append(ProtoBufUtil.JAVA_LINE_BREAK); objectDecodeExpress = code.toString(); code.setLength(0); - objectDecodeExpress += "int length = input.readRawVarint32()" + FieldUtil.JAVA_LINE_BREAK; - objectDecodeExpress += "final int oldLimit = input.pushLimit(length)" + FieldUtil.JAVA_LINE_BREAK; + objectDecodeExpress += "int length = input.readRawVarint32()" + ProtoBufUtil.JAVA_LINE_BREAK; + objectDecodeExpress += "final int oldLimit = input.pushLimit(length)" + ProtoBufUtil.JAVA_LINE_BREAK; listTypeCheck = true; express = "(" + name + ") codec.readFrom(input)"; @@ -301,11 +300,11 @@ private static void processDecodeBlock() { express += ".toByteArray()"; } - String decodeFieldSetValue = FieldUtil.getSetFieldDynamicString(protobufField, clazz, express) + FieldUtil.JAVA_LINE_BREAK; + String decodeFieldSetValue = ProtoBufUtil.getSetFieldDynamicString(protobufField, clazz, express) + ProtoBufUtil.JAVA_LINE_BREAK; if (listTypeCheck) { - objectDecodeExpressSuffix += "input.checkLastTagWas(0)" + FieldUtil.JAVA_LINE_BREAK; - objectDecodeExpressSuffix += "input.popLimit(oldLimit)" + FieldUtil.JAVA_LINE_BREAK; + objectDecodeExpressSuffix += "input.checkLastTagWas(0)" + ProtoBufUtil.JAVA_LINE_BREAK; + objectDecodeExpressSuffix += "input.popLimit(oldLimit)" + ProtoBufUtil.JAVA_LINE_BREAK; } String objectPackedDecodeExpress = ""; @@ -314,18 +313,18 @@ private static void processDecodeBlock() { ProtobufFieldTypeEnum protobufFieldType = protobufField.getProtobufFieldType(); if (protobufFieldType.isPrimitive() || protobufFieldType.isEnum()) { code.append("if (tag == ") - .append(FieldUtil.makeTag(protobufField.getOrder(), WireFormat.WIRETYPE_LENGTH_DELIMITED)); - code.append(") {").append(FieldUtil.LINE_BREAK); + .append(ProtoBufUtil.makeTag(protobufField.getOrder(), WireFormat.WIRETYPE_LENGTH_DELIMITED)); + code.append(") {").append(ProtoBufUtil.LINE_BREAK); - code.append("int length = input.readRawVarint32()").append(FieldUtil.JAVA_LINE_BREAK); - code.append("int limit = input.pushLimit(length)").append(FieldUtil.JAVA_LINE_BREAK); + code.append("int length = input.readRawVarint32()").append(ProtoBufUtil.JAVA_LINE_BREAK); + code.append("int limit = input.pushLimit(length)").append(ProtoBufUtil.JAVA_LINE_BREAK); - code.append(FieldUtil.getSetFieldDynamicString(protobufField, clazz, express)); + code.append(ProtoBufUtil.getSetFieldDynamicString(protobufField, clazz, express)); - code.append("input.popLimit(limit)").append(FieldUtil.JAVA_LINE_BREAK); + code.append("input.popLimit(limit)").append(ProtoBufUtil.JAVA_LINE_BREAK); - code.append("continue").append(FieldUtil.JAVA_LINE_BREAK); - code.append("}").append(FieldUtil.LINE_BREAK); + code.append("continue").append(ProtoBufUtil.JAVA_LINE_BREAK); + code.append("}").append(ProtoBufUtil.LINE_BREAK); objectPackedDecodeExpress = code.toString(); } @@ -355,12 +354,12 @@ private static String getMapCommand(ProtobufField protobufField) { valueGeneric = protobufField.getGenericValueType().getCanonicalName(); String getMapCommand = "(Map<" + keyGeneric; getMapCommand = getMapCommand + ", " + valueGeneric + ">)"; - getMapCommand = getMapCommand + FieldUtil.getGetterDynamicString(protobufField, clazz); + getMapCommand = getMapCommand + ProtoBufUtil.getGetterDynamicString(protobufField, clazz); return getMapCommand; } private static void loadProtobufField() { - protobufFields = FieldUtil.getProtobufFieldList(clazz, + protobufFields = ProtoBufUtil.getProtobufFieldList(clazz, clazz.getAnnotation(ProtobufEnableZigZap.class) != null ); } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufClassCompiler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufClassCompiler.java index 80e47576e2..7ca26e4a81 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufClassCompiler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufClassCompiler.java @@ -1,10 +1,4 @@ -package com.taobao.arthas.grpc.server.protobuf.utils;/** - * @author: 風楪 - * @date: 2024/8/9 01:21 - */ - -import com.baidu.bjf.remoting.protobuf.utils.ClassHelper; -import com.baidu.bjf.remoting.protobuf.utils.compiler.ClassUtils; +package com.taobao.arthas.grpc.server.protobuf.utils; import javax.tools.*; import java.io.*; @@ -70,7 +64,7 @@ public synchronized Class compile(String className, String code, ClassLoader throw t; } catch (Throwable t) { throw new IllegalStateException("Failed to compile class, cause: " + t.getMessage() + ", class: " - + className + ", code: \n" + code + "\n, stack: " + ClassUtils.toString(t)); + + className + ", code: \n" + code + "\n, stack: " + ProtoBufUtil.toString(t)); } } } @@ -82,7 +76,7 @@ public synchronized Class doCompile(String name, String sourceCode) throws Th JavaFileObjectImpl javaFileObject = new JavaFileObjectImpl(className, sourceCode); fileObjects.put(new URI(StandardLocation.SOURCE_PATH.getName() + "/" + packageName + "/" + className + ".java"), javaFileObject); javaFileManager.putFileForInput(StandardLocation.SOURCE_PATH, packageName, - className + ClassUtils.JAVA_EXTENSION, javaFileObject); + className + ProtoBufUtil.JAVA_EXTENSION, javaFileObject); DiagnosticCollector diagnosticCollector = new DiagnosticCollector(); Boolean result = compiler.getTask(null, javaFileManager, diagnosticCollector, options, null, @@ -124,7 +118,7 @@ public void putFileForInput(StandardLocation location, String packageName, Strin } private URI uri(Location location, String packageName, String relativeName) { - return ClassUtils.toURI(location.getName() + '/' + packageName + '/' + relativeName); + return ProtoBufUtil.toURI(location.getName() + '/' + packageName + '/' + relativeName); } @Override @@ -209,7 +203,7 @@ protected Class findClass(final String qualifiedClassName) throws ClassNotFou return defineClass(qualifiedClassName, bytes, 0, bytes.length); } try { - return ClassHelper.forNameWithCallerClassLoader(qualifiedClassName, getClass()); + return ProtoBufUtil.forNameWithCallerClassLoader(qualifiedClassName, getClass()); } catch (ClassNotFoundException nf) { return super.findClass(qualifiedClassName); } @@ -227,9 +221,9 @@ protected synchronized Class loadClass(final String name, final boolean resol @Override public InputStream getResourceAsStream(final String name) { - if (name.endsWith(ClassUtils.CLASS_EXTENSION)) { + if (name.endsWith(ProtoBufUtil.CLASS_EXTENSION)) { String qualifiedClassName = - name.substring(0, name.length() - ClassUtils.CLASS_EXTENSION.length()).replace('/', '.'); + name.substring(0, name.length() - ProtoBufUtil.CLASS_EXTENSION.length()).replace('/', '.'); JavaFileObjectImpl file = (JavaFileObjectImpl) classes.get(qualifiedClassName); if (file != null) { return new ByteArrayInputStream(file.getByteCode()); @@ -247,12 +241,12 @@ private static final class JavaFileObjectImpl extends SimpleJavaFileObject { private final CharSequence source; public JavaFileObjectImpl(final String baseName, final CharSequence source) throws URISyntaxException { - super(new URI(baseName + ClassUtils.JAVA_EXTENSION), Kind.SOURCE); + super(new URI(baseName + ProtoBufUtil.JAVA_EXTENSION), Kind.SOURCE); this.source = source; } JavaFileObjectImpl(final String name, final Kind kind) { - super(ClassUtils.toURI(name), kind); + super(ProtoBufUtil.toURI(name), kind); source = null; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/FieldUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java similarity index 87% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/FieldUtil.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java index 93445b5fd2..73e5cc2b85 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/FieldUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java @@ -1,11 +1,5 @@ -package com.taobao.arthas.grpc.server.protobuf.utils;/** - * @author: 風楪 - * @date: 2024/7/25 上午12:33 - */ +package com.taobao.arthas.grpc.server.protobuf.utils; - -import com.baidu.bjf.remoting.protobuf.utils.ClassHelper; -import com.baidu.bjf.remoting.protobuf.utils.StringUtils; import com.google.protobuf.*; import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; import com.taobao.arthas.grpc.server.protobuf.ProtobufField; @@ -15,13 +9,17 @@ import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufCustomizedField; import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufIgnore; - import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; import java.lang.Enum; +import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.BigInteger; +import java.net.URI; +import java.net.URISyntaxException; import java.util.*; /** @@ -29,13 +27,16 @@ * @date: 2024/7/25 上午12:33 * @description: FieldUtil */ -public class FieldUtil { - +public class ProtoBufUtil { public static final Map, ProtobufFieldTypeEnum> TYPE_MAPPER; public static final Map PRIMITIVE_TYPE_MAPPING; + private static final Map> PRIMIIIVE_TYPE_CLASS_MAPPING = new HashMap>(16); + + private static final Map, Class> PRIMITIVE_WRAPPER_TYPE_MAP = new HashMap, Class>(8); + public static final String DYNAMIC_TARGET = "target"; public static final String PACKAGE_SEPARATOR = "."; @@ -48,6 +49,14 @@ public class FieldUtil { public static final String WIREFORMAT_CLSNAME = com.google.protobuf.WireFormat.FieldType.class.getCanonicalName(); + public static final String ARRAY_SUFFIX = "[]"; + + private static final String INTERNAL_ARRAY_PREFIX = "[L"; + + public static final String JAVA_EXTENSION = ".java"; + + public static final String CLASS_EXTENSION = ".class"; + static { PRIMITIVE_TYPE_MAPPING = new HashMap(); @@ -60,6 +69,24 @@ public class FieldUtil { PRIMITIVE_TYPE_MAPPING.put(float.class.getSimpleName(), Float.class.getSimpleName()); PRIMITIVE_TYPE_MAPPING.put(char.class.getSimpleName(), Character.class.getSimpleName()); PRIMITIVE_TYPE_MAPPING.put(byte.class.getSimpleName(), Byte.class.getSimpleName()); + + PRIMITIVE_WRAPPER_TYPE_MAP.put(Boolean.class, boolean.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Byte.class, byte.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Character.class, char.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Double.class, double.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Float.class, float.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Integer.class, int.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Long.class, long.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Short.class, short.class); + + Set> primitiveTypeNames = new HashSet>(16); + primitiveTypeNames.addAll(PRIMITIVE_WRAPPER_TYPE_MAP.values()); + primitiveTypeNames.addAll(Arrays.asList(new Class[] { boolean[].class, byte[].class, char[].class, + double[].class, float[].class, int[].class, long[].class, short[].class })); + for (Iterator> it = primitiveTypeNames.iterator(); it.hasNext();) { + Class primitiveClass = (Class) it.next(); + PRIMIIIVE_TYPE_CLASS_MAPPING.put(primitiveClass.getName(), primitiveClass); + } } static { @@ -617,9 +644,9 @@ public static void writeObject(CodedOutputStream out, int order, ProtobufFieldTy } } else if (type == ProtobufFieldTypeEnum.STRING) { if (withTag) { - out.writeBytes(order, ByteString.copyFromUtf8(String.valueOf(o))); + out.writeBytes(order, ByteString.copyFromUtf8(java.lang.String.valueOf(o))); } else { - out.writeBytesNoTag(ByteString.copyFromUtf8(String.valueOf(o))); + out.writeBytesNoTag(ByteString.copyFromUtf8(java.lang.String.valueOf(o))); } } else if (type == ProtobufFieldTypeEnum.UINT32) { if (withTag) { @@ -880,7 +907,7 @@ public static int getEnumOrdinal(Enum en) { } public static > T getEnumValue(Class enumType, String name) { - if (StringUtils.isEmpty(name)) { + if (isEmpty(name)) { return null; } @@ -982,9 +1009,9 @@ public static int getObjectSize(int order, Object object, ProtobufFieldTypeEnum } if (type == ProtobufFieldTypeEnum.STRING) { - size = CodedOutputStream.computeStringSizeNoTag(String.valueOf(object)); + size = CodedOutputStream.computeStringSizeNoTag(java.lang.String.valueOf(object)); } else if (type == ProtobufFieldTypeEnum.BOOL) { - size = CodedOutputStream.computeBoolSizeNoTag(Boolean.valueOf(String.valueOf(object))); + size = CodedOutputStream.computeBoolSizeNoTag(Boolean.valueOf(java.lang.String.valueOf(object))); } else if (type == ProtobufFieldTypeEnum.BYTES) { byte[] bb = (byte[]) object; size = CodedOutputStream.computeBytesSizeNoTag(ByteString.copyFrom(bb)); @@ -1044,11 +1071,11 @@ public static String toObjectType(String primitiveType) { } public static String getFullClassName(Class cls) { - if (StringUtils.isEmpty(getPackage(cls))) { + if (isEmpty(getPackage(cls))) { return getClassName(cls); } - return getPackage(cls) + ClassHelper.PACKAGE_SEPARATOR + getClassName(cls); + return getPackage(cls) + PACKAGE_SEPARATOR + getClassName(cls); } public static String getPackage(Class cls) { @@ -1069,13 +1096,120 @@ public static String getPackage(Class cls) { public static String getClassName(Class cls) { if (cls.isMemberClass()) { String name = cls.getName(); - name = StringUtils.substringAfterLast(name, PACKAGE_SEPARATOR); + name = substringAfterLast(name, PACKAGE_SEPARATOR); return name; } return cls.getSimpleName(); } + public static boolean isEmpty(String s){ + return s == null || s.isEmpty(); + } + + public static String substringAfterLast(String str, String separator) { + if (isEmpty(str)) { + return str; + } else if (isEmpty(separator)) { + return ""; + } else { + int pos = str.lastIndexOf(separator); + return pos != -1 && pos != str.length() - separator.length() ? str.substring(pos + separator.length()) : ""; + } + } + + public static Class forNameWithCallerClassLoader(String name, Class caller) throws ClassNotFoundException { + return forName(name, caller.getClassLoader()); + } + + public static Class forName(String name, ClassLoader classLoader) throws ClassNotFoundException, LinkageError { + + Class clazz = resolvePrimitiveClassName(name); + if (clazz != null) { + return clazz; + } + + // "java.lang.String[]" style arrays + if (name.endsWith(ARRAY_SUFFIX)) { + String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length()); + Class elementClass = forName(elementClassName, classLoader); + return Array.newInstance(elementClass, 0).getClass(); + } + + // "[Ljava.lang.String;" style arrays + int internalArrayMarker = name.indexOf(INTERNAL_ARRAY_PREFIX); + if (internalArrayMarker != -1 && name.endsWith(";")) { + String elementClassName = null; + if (internalArrayMarker == 0) { + elementClassName = name.substring(INTERNAL_ARRAY_PREFIX.length(), name.length() - 1); + } else if (name.startsWith("[")) { + elementClassName = name.substring(1); + } + Class elementClass = forName(elementClassName, classLoader); + return Array.newInstance(elementClass, 0).getClass(); + } + + ClassLoader classLoaderToUse = classLoader; + if (classLoaderToUse == null) { + classLoaderToUse = getClassLoader(); + } + return classLoaderToUse.loadClass(name); + } + + public static Class resolvePrimitiveClassName(String name) { + Class result = null; + // Most class names will be quite long, considering that they + // SHOULD sit in a package, so a length check is worthwhile. + if (name != null && name.length() <= 8) { + // Could be a primitive - likely. + result = (Class) PRIMIIIVE_TYPE_CLASS_MAPPING.get(name); + } + return result; + } + + public static ClassLoader getClassLoader() { + return getClassLoader(ProtoBufUtil.class); + } + + public static ClassLoader getClassLoader(Class cls) { + ClassLoader cl = null; + try { + cl = Thread.currentThread().getContextClassLoader(); + } catch (Throwable ex) { + // Cannot access thread context ClassLoader - falling back to system + // class loader... + } + if (cl == null) { + // No thread context class loader -> use class loader of this class. + cl = cls.getClassLoader(); + } + return cl; + } + + public static URI toURI(String name) { + try { + return new URI(name); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + public static String toString(Throwable e) { + StringWriter w = new StringWriter(); + PrintWriter p = new PrintWriter(w); + p.print(e.getClass().getName() + ": "); + if (e.getMessage() != null) { + p.print(e.getMessage() + "\n"); + } + p.println(); + try { + e.printStackTrace(p); + return w.toString(); + } finally { + p.close(); + } + } + public static boolean isNull(Object o) { return o == null; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/temp/TempImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/temp/TempImpl.java deleted file mode 100644 index 2a6247888f..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/temp/TempImpl.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.taobao.arthas.grpc.server.temp;/** - * @author: 風楪 - * @date: 2024/9/16 01:59 - */ - - -import arthasSample.ArthasSample; -import arthasSample.ArthasTempServiceGrpc; -import io.grpc.stub.StreamObserver; - -/** - * @author: FengYe - * @date: 2024/9/16 01:59 - * @description: TempImpl - */ -public class TempImpl extends ArthasTempServiceGrpc.ArthasTempServiceImplBase { - - @Override - public void trace(ArthasSample.ArthasSampleRequest request, StreamObserver responseObserver) { - ArthasSample.ArthasSampleResponse.Builder builder = ArthasSample.ArthasSampleResponse.newBuilder(); - builder.setMessage("trace"); - responseObserver.onNext(builder.build()); - responseObserver.onCompleted(); - } - - @Override - public StreamObserver watch(StreamObserver responseObserver) { - return new StreamObserver() { - @Override - public void onNext(ArthasSample.ArthasSampleRequest value) { - - // 回应客户端 - ArthasSample.ArthasSampleResponse response = ArthasSample.ArthasSampleResponse.newBuilder() - .setMessage(value.getName()) - .build(); - responseObserver.onNext(response); - } - - @Override - public void onError(Throwable t) { - System.err.println("Error: " + t); - } - - @Override - public void onCompleted() { - responseObserver.onCompleted(); - } - }; - } -} From a3426b7220ba504bb4bcb4c15f07d5b0b0c28eb9 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Thu, 19 Sep 2024 00:05:52 +0800 Subject: [PATCH 32/48] update: Optimize the situation where multiple grpc bodies exist in the same http2frame --- .../grpc/server/handler/GrpcDispatcher.java | 32 +++++++---- .../grpc/server/handler/GrpcRequest.java | 57 +++++++++++++++---- .../server/handler/Http2FrameRequest.java | 19 +++++++ .../grpc/server/handler/Http2Handler.java | 25 +++++--- .../grpc/server/protobuf/ProtobufProxy.java | 12 ++-- .../server/protobuf/utils/ProtoBufUtil.java | 20 +++---- .../src/main/resources/class_template.tpl | 4 +- 7 files changed, 121 insertions(+), 48 deletions(-) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2FrameRequest.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java index 38b36fabac..36d5441cda 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java @@ -64,29 +64,39 @@ private String generateGrpcMethodKey(String serviceName, String methodName) { return serviceName + "." + methodName; } - public Object execute(String serviceName, String methodName, Object arg) throws Throwable { + public GrpcResponse execute(String serviceName, String methodName, Object arg) throws Throwable { MethodHandle methodHandle = grpcMethodInvokeMap.get(generateGrpcMethodKey(serviceName, methodName)); - return methodHandle.invoke(arg); + MethodType type = grpcMethodInvokeMap.get(generateGrpcMethodKey(serviceName, methodName)).type(); + Object execute = methodHandle.invoke(arg); + GrpcResponse grpcResponse = new GrpcResponse(); + grpcResponse.setClazz(type.returnType()); + grpcResponse.writeResponseData(execute); + return grpcResponse; } public GrpcResponse execute(GrpcRequest request) throws Throwable { String service = request.getService(); String method = request.getMethod(); - MethodType type = grpcMethodInvokeMap.get(generateGrpcMethodKey(request.getService(), request.getMethod())).type(); // protobuf 规范只能有单入参 - request.setClazz(type.parameterArray()[0]); + request.setClazz(getRequestClass(request.getService(), request.getMethod())); ProtobufCodec protobufCodec = ProtobufProxy.getCodecCacheSide(request.getClazz()); Object decode = protobufCodec.decode(request.readData()); + return this.execute(service, method, decode); + } - Object execute = this.execute(service, method, decode); - - GrpcResponse grpcResponse = new GrpcResponse(); - grpcResponse.setClazz(type.returnType()); - grpcResponse.writeResponseData(execute); - return grpcResponse; + /** + * 获取指定 service method 对应的入参类型 + * + * @param serviceName + * @param methodName + * @return + */ + public Class getRequestClass(String serviceName, String methodName) { + //protobuf 规范只能有单入参 + return grpcMethodInvokeMap.get(generateGrpcMethodKey(serviceName, methodName)).type().parameterArray()[0]; } - public void checkGrpcStream(GrpcRequest request){ + public void checkGrpcStream(GrpcRequest request) { request.setStream( Optional.ofNullable(grpcMethodStreamMap.get(generateGrpcMethodKey(request.getService(), request.getMethod()))) .orElse(false) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java index 324bcff280..7a0498bf13 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java @@ -15,7 +15,7 @@ /** * @author: FengYe * @date: 2024/9/4 23:07 - * @description: GrpcRequest + * @description: GrpcRequest grpc 请求体 */ public class GrpcRequest { @@ -35,10 +35,15 @@ public class GrpcRequest { private String method; /** - * 二进制数据 + * 二进制数据,可能包含多个 grpc body,每个 body 都带有 5 个 byte 的前缀,分别是 boolean compressed - int length */ private ByteBuf byteData; + /** + * 二进制数据的长度 + */ + private int length; + /** * 请求class */ @@ -54,7 +59,8 @@ public class GrpcRequest { */ private boolean streamFirstData; - public GrpcRequest(Integer streamId, String path,String method) { + + public GrpcRequest(Integer streamId, String path, String method) { this.streamId = streamId; this.service = path; this.method = method; @@ -70,26 +76,30 @@ public void writeData(ByteBuf byteBuf) { if (decompressedData == null) { return; } - ByteBuf operateByteBuf = ByteUtil.newByteBuf(decompressedData); - - // 必须要先把这5个字节读出来,后续才是真正的data - boolean compressed = operateByteBuf.readBoolean(); - int length = operateByteBuf.readInt(); - byteData.writeBytes(operateByteBuf); + byteData.writeBytes(ByteUtil.newByteBuf(decompressedData)); } /** - * 只读取数据而不操作 byteData + * 读取部分数据 + * * @return */ public byte[] readData() { - return ByteUtil.getBytes(byteData); + if (byteData.readableBytes() == 0) { + return null; + } + boolean compressed = byteData.readBoolean(); + int length = byteData.readInt(); + byte[] bytes = new byte[length]; + byteData.readBytes(bytes); + return bytes; } - public void clearData(){ + public void clearData() { byteData.clear(); } + // todo 后续优化gzip处理 private byte[] decompressGzip(byte[] compressedData) { boolean isGzip = (compressedData.length > 2 && (compressedData[0] & 0xff) == 0x1f && (compressedData[1] & 0xff) == 0x8b); if (isGzip) { @@ -112,6 +122,29 @@ private byte[] decompressGzip(byte[] compressedData) { } } + private ByteBuf decompressGzip(ByteBuf byteBuf) { + byte[] compressedData = ByteUtil.getBytes(byteBuf); + boolean isGzip = (compressedData.length > 2 && (compressedData[0] & 0xff) == 0x1f && (compressedData[1] & 0xff) == 0x8b); + if (isGzip) { + try { + InputStream byteStream = new ByteArrayInputStream(compressedData); + GZIPInputStream gzipStream = new GZIPInputStream(byteStream); + byte[] buffer = new byte[1024]; + int len; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + while ((len = gzipStream.read(buffer)) != -1) { + out.write(buffer, 0, len); + } + return ByteUtil.newByteBuf(out.toByteArray()); + } catch (IOException e) { + System.err.println("Failed to decompress GZIP data: " + e.getMessage()); + } + return null; + } else { + return byteBuf; + } + } + public Integer getStreamId() { return streamId; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2FrameRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2FrameRequest.java new file mode 100644 index 0000000000..49091da077 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2FrameRequest.java @@ -0,0 +1,19 @@ +package com.taobao.arthas.grpc.server.handler;/** + * @author: 風楪 + * @date: 2024/9/18 23:12 + */ + +import java.util.List; + +/** + * @author: FengYe + * @date: 2024/9/18 23:12 + * @description: 一个 http2 的 frame 中可能存在多个 grpc 的请求体 + */ +public class Http2FrameRequest { + + /** + * grpc 请求体 + */ + private List grpcRequests; +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java index d69bc10cff..dbd700f118 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java @@ -3,6 +3,8 @@ * @date: 2024/7/7 下午9:58 */ +import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; +import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http2.*; @@ -66,19 +68,28 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) GrpcRequest grpcRequest = dataBuffer.get(dataFrame.stream().id()); grpcRequest.writeData(dataFrame.content()); + System.out.println(dataFrame.stream().id()); if (grpcRequest.isStream()) { // 流式调用,即刻响应 try { - GrpcResponse response = grpcDispatcher.execute(grpcRequest); - grpcRequest.clearData(); + GrpcResponse response = new GrpcResponse(); + byte[] bytes = grpcRequest.readData(); + while (bytes != null) { + ProtobufCodec protobufCodec = ProtobufProxy.getCodecCacheSide(grpcDispatcher.getRequestClass(grpcRequest.getService(),grpcRequest.getMethod())); + Object decode = protobufCodec.decode(bytes); + response = grpcDispatcher.execute(grpcRequest.getService(), grpcRequest.getMethod(), decode); + + // 针对第一个响应发送 header + if (grpcRequest.isStreamFirstData()) { + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(dataFrame.stream())); + grpcRequest.setStreamFirstData(false); + } + ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(dataFrame.stream())); - // 针对第一个响应发送 header - if (grpcRequest.isStreamFirstData()) { - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(dataFrame.stream())); - grpcRequest.setStreamFirstData(false); + bytes = grpcRequest.readData(); } - ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(dataFrame.stream())); + grpcRequest.clearData(); if (dataFrame.isEndStream()) { ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(dataFrame.stream())); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java index fb0101acb8..389be4c24e 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java @@ -146,7 +146,7 @@ private static void processDecodeBlock() { if (protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.ENUM) { String clsName = protobufField.getJavaField().getType().getCanonicalName(); if (!isList) { - express = "FieldUtil.getEnumValue(" + clsName + ".class, " + clsName + ".values()[0].name())"; + express = "ProtoBufUtil.getEnumValue(" + clsName + ".class, " + clsName + ".values()[0].name())"; // add set get method String setToField = ProtoBufUtil.getSetFieldDynamicString(protobufField, clazz, express); miniTemplator.setVariable("enumInitialize", setToField); @@ -174,7 +174,7 @@ private static void processDecodeBlock() { decodeOrder = ProtoBufUtil.makeTag(protobufField.getOrder(), protobufField.getProtobufFieldType().getInternalFieldType().getWireType()) + ""; } else { - decodeOrder = "FieldUtil.makeTag(" + protobufField.getOrder() + ",WireFormat." + decodeOrder = "ProtoBufUtil.makeTag(" + protobufField.getOrder() + ",WireFormat." + protobufField.getProtobufFieldType().getWireFormat() + ")"; } miniTemplator.setVariable("decodeOrder", decodeOrder); @@ -188,7 +188,7 @@ private static void processDecodeBlock() { clsName = cls.getCanonicalName(); } } - express = "FieldUtil.getEnumValue(" + clsName + ".class, FieldUtil.getEnumName(" + clsName + express = "ProtoBufUtil.getEnumValue(" + clsName + ".class, ProtoBufUtil.getEnumName(" + clsName + ".values()," + "input.read" + t + "()))"; } else { // here is the trick way to process BigDecimal and BigInteger @@ -230,7 +230,7 @@ private static void processDecodeBlock() { code.append(ProtoBufUtil.LINE_BREAK); code.append("public ").append(enumClassName).append(" handle(int value) {"); code.append(ProtoBufUtil.LINE_BREAK); - code.append("String enumName = FieldUtil.getEnumName(").append(enumClassName) + code.append("String enumName = ProtoBufUtil.getEnumName(").append(enumClassName) .append(".values(), value)"); code.append(ProtoBufUtil.JAVA_LINE_BREAK); code.append("return ").append(enumClassName).append(".valueOf(enumName)"); @@ -247,7 +247,7 @@ private static void processDecodeBlock() { code.append(ProtoBufUtil.LINE_BREAK); code.append("public ").append(enumClassName).append(" handle(int value) {"); code.append(ProtoBufUtil.LINE_BREAK); - code.append("String enumName = FieldUtil.getEnumName(").append(enumClassName) + code.append("String enumName = ProtoBufUtil.getEnumName(").append(enumClassName) .append(".values(), value)"); code.append(ProtoBufUtil.JAVA_LINE_BREAK); code.append("return ").append(enumClassName).append(".valueOf(enumName)"); @@ -259,7 +259,7 @@ private static void processDecodeBlock() { objectDecodeExpress = code.toString(); code.setLength(0); - express = "FieldUtil.putMapValue(input, " + getMapCommand + ","; + express = "ProtoBufUtil.putMapValue(input, " + getMapCommand + ","; express += ProtoBufUtil.getMapFieldGenericParameterString(protobufField); if (protobufField.isEnumKeyType()) { express += ", keyhandler"; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java index 73e5cc2b85..221966832f 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java @@ -25,7 +25,7 @@ /** * @author: FengYe * @date: 2024/7/25 上午12:33 - * @description: FieldUtil + * @description: ProtoBufUtil */ public class ProtoBufUtil { @@ -385,7 +385,7 @@ public static String getGetterDynamicString(ProtobufField protobufField, Class, throws IOException { ${dynamicFieldType} ${dynamicFieldName} = null; - if (!FieldUtil.isNull(${dynamicFieldGetter})) { + if (!ProtoBufUtil.isNull(${dynamicFieldGetter})) { ${dynamicFieldName} = ${dynamicFieldGetter}; ${encodeWriteFieldValue} } @@ -34,7 +34,7 @@ public class ${className} implements ${codecClassName}<${targetProxyClassName}>, int size = 0; ${dynamicFieldType} ${dynamicFieldName} = null; - if (!FieldUtil.isNull(${dynamicFieldGetter})) { + if (!ProtoBufUtil.isNull(${dynamicFieldGetter})) { ${dynamicFieldName} = ${dynamicFieldGetter}; size += ${sizeDynamicString} } From 893f7f3dd5bb6295eeba016c246304355d9ff848 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Fri, 20 Sep 2024 00:26:32 +0800 Subject: [PATCH 33/48] update: add unit test --- arthas-grpc-server/pom.xml | 41 +++--- .../grpc/server/protobuf/ProtobufProxy.java | 8 +- .../server/protobuf/utils/ProtoBufUtil.java | 2 +- .../service/req/ArthasSampleRequest.java | 2 - .../src/test/java/grpc/GrpcTest.java | 13 ++ .../src/test/java/protobuf/ProtoBufTest.java | 51 +++++++ .../test/java/protobuf/ProtoBufTestReq.java | 129 ++++++++++++++++++ 7 files changed, 219 insertions(+), 27 deletions(-) create mode 100644 arthas-grpc-server/src/test/java/grpc/GrpcTest.java create mode 100644 arthas-grpc-server/src/test/java/protobuf/ProtoBufTest.java create mode 100644 arthas-grpc-server/src/test/java/protobuf/ProtoBufTestReq.java diff --git a/arthas-grpc-server/pom.xml b/arthas-grpc-server/pom.xml index 7045441714..025ed5b385 100644 --- a/arthas-grpc-server/pom.xml +++ b/arthas-grpc-server/pom.xml @@ -62,29 +62,30 @@ 1.5.0 - - org.projectlombok - lombok - 1.18.30 - provided - - - - - - - - - - - - - - - + + + io.grpc + grpc-netty + test + + + io.grpc + grpc-services + test + + + org.junit.vintage + junit-vintage-engine + test + + + org.junit.jupiter + junit-jupiter + test + diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java index 389be4c24e..bd6b690424 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java @@ -23,7 +23,7 @@ public class ProtobufProxy { private static final String TEMPLATE_FILE = "/class_template.tpl"; - private static final Map codecCache = new ConcurrentHashMap(); + private static final Map codecCache = new ConcurrentHashMap<>(); private static Class clazz; @@ -31,8 +31,8 @@ public class ProtobufProxy { private static List protobufFields; - public static ProtobufCodec getCodecCacheSide(Class clazz) { - ProtobufCodec codec = codecCache.get(clazz.getName()); + public static ProtobufCodec getCodecCacheSide(Class clazz) { + ProtobufCodec codec = codecCache.get(clazz.getName()); if (codec != null) { return codec; } @@ -46,7 +46,7 @@ public static ProtobufCodec getCodecCacheSide(Class clazz) { } } - public static ProtobufCodec create(Class clazz) { + public static ProtobufCodec create(Class clazz) { Objects.requireNonNull(clazz); if (clazz.getAnnotation(ProtobufClass.class) == null) { throw new IllegalArgumentException(clazz + "class is not annotated with @ProtobufClass"); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java index 221966832f..f0ee0162bd 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java @@ -453,7 +453,7 @@ public static String getWriteByteDynamicString(ProtobufField protobufField) { .append(JAVA_LINE_BREAK).append("}").append(LINE_BREAK); return sb.toString(); } else if (protobufField.isMap()) { - sb.append("Field.writeMap(").append(CODE_OUTPUT_STREAM_OBJ_NAME).append(","); + sb.append("ProtoBufUtil.writeMap(").append(CODE_OUTPUT_STREAM_OBJ_NAME).append(","); sb.append(order).append(",").append(dynamicFieldName); String joinedSentence = getMapFieldGenericParameterString(protobufField); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasSampleRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasSampleRequest.java index 46a0475c4d..d5eaabc29f 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasSampleRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasSampleRequest.java @@ -4,7 +4,6 @@ */ import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; -import lombok.ToString; import java.util.List; @@ -15,7 +14,6 @@ */ @ProtobufClass -@ToString public class ArthasSampleRequest { private String name; diff --git a/arthas-grpc-server/src/test/java/grpc/GrpcTest.java b/arthas-grpc-server/src/test/java/grpc/GrpcTest.java new file mode 100644 index 0000000000..d5c4b440e5 --- /dev/null +++ b/arthas-grpc-server/src/test/java/grpc/GrpcTest.java @@ -0,0 +1,13 @@ +package grpc; /** + * @author: 風楪 + * @date: 2024/9/19 22:20 + */ + +/** + * @author: FengYe + * @date: 2024/9/19 22:20 + * @description: grpc.GrpcTest + */ +public class GrpcTest { + +} diff --git a/arthas-grpc-server/src/test/java/protobuf/ProtoBufTest.java b/arthas-grpc-server/src/test/java/protobuf/ProtoBufTest.java new file mode 100644 index 0000000000..0c72902d93 --- /dev/null +++ b/arthas-grpc-server/src/test/java/protobuf/ProtoBufTest.java @@ -0,0 +1,51 @@ +package protobuf; /** + * @author: 風楪 + * @date: 2024/9/19 22:35 + */ + +import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; +import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; +import com.taobao.arthas.grpc.server.service.req.ArthasSampleRequest; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author: FengYe + * @date: 2024/9/19 22:35 + * @description: protobuf.ProtoBufTest + */ +public class ProtoBufTest { + @Test + public void testEncodeAndDecode() { + ProtoBufTestReq protoBufTestReq = new ProtoBufTestReq(); + protoBufTestReq.setName("test"); + protoBufTestReq.setAge(18); + protoBufTestReq.setPrice(100L); + protoBufTestReq.setStatus(ArthasSampleRequest.StatusEnum.START); + List list = new ArrayList<>(); + list.add(new ProtoBufTestReq.TestClass("test1")); + list.add(new ProtoBufTestReq.TestClass("test2")); + list.add(new ProtoBufTestReq.TestClass("test3")); + Map map = new HashMap<>(); + map.put("key1","value1"); + map.put("key2","value2"); + map.put("key3","value3"); + protoBufTestReq.setTestList(list); + protoBufTestReq.setTestMap(map); + + try { + ProtobufCodec protobufCodec = ProtobufProxy.getCodecCacheSide(ProtoBufTestReq.class); + byte[] encode = protobufCodec.encode(protoBufTestReq); + ProtoBufTestReq decode = protobufCodec.decode(encode); + Assert.assertEquals(protoBufTestReq.toString(), decode.toString()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/arthas-grpc-server/src/test/java/protobuf/ProtoBufTestReq.java b/arthas-grpc-server/src/test/java/protobuf/ProtoBufTestReq.java new file mode 100644 index 0000000000..53c48512c3 --- /dev/null +++ b/arthas-grpc-server/src/test/java/protobuf/ProtoBufTestReq.java @@ -0,0 +1,129 @@ +package protobuf;/** + * @author: 風楪 + * @date: 2024/9/19 22:38 + */ + +import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; +import com.taobao.arthas.grpc.server.service.req.ArthasSampleRequest; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * @author: FengYe + * @date: 2024/9/19 22:38 + * @description: ProtoBufTestReq + */ +@ProtobufClass +public class ProtoBufTestReq { + private String name; + private double age; + private long price; + private ArthasSampleRequest.StatusEnum status; + private List testList; + private Map testMap; + + @ProtobufClass + public enum StatusEnum { + START(1, "开始"), + STOP(2, "结束"); + + StatusEnum(int code, String desc) { + this.code = code; + this.desc = desc; + } + + private int code; + private String desc; + } + + @ProtobufClass + public static class TestClass { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + // 注意被 @ProtobufClass 注解的 class 必须添加无参构造函数 + public TestClass() { + } + + public TestClass(String name) { + this.name = name; + } + + @Override + public String toString() { + return "TestClass{" + + "name='" + name + '\'' + + '}'; + } + } + + public List getTestList() { + return testList; + } + + public void setTestList(List testList) { + this.testList = testList; + } + + public ArthasSampleRequest.StatusEnum getStatus() { + return status; + } + + public void setStatus(ArthasSampleRequest.StatusEnum status) { + this.status = status; + } + + public long getPrice() { + return price; + } + + public void setPrice(long price) { + this.price = price; + } + + public double getAge() { + return age; + } + + public void setAge(double age) { + this.age = age; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Map getTestMap() { + return testMap; + } + + public void setTestMap(Map testMap) { + this.testMap = testMap; + } + + @Override + public java.lang.String toString() { + return "ProtoBufTestReq{" + + "name='" + name + '\'' + + ", age=" + age + + ", price=" + price + + ", status=" + status + + ", testList=" + testList + + ", testMap=" + testMap + + '}'; + } +} + From f3050abc63c61aeafa291fc2781cebb5781dda4e Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Mon, 23 Sep 2024 02:47:33 +0800 Subject: [PATCH 34/48] update: formatter --- arthas-grpc-server/pom.xml | 64 +++++++------- .../arthas/grpc/server/ArthasGrpcServer.java | 6 +- .../grpc/server/handler/GrpcDispatcher.java | 6 +- .../grpc/server/handler/GrpcRequest.java | 5 +- .../grpc/server/handler/GrpcResponse.java | 7 +- .../server/handler/Http2FrameRequest.java | 5 +- .../grpc/server/handler/Http2Handler.java | 7 +- .../server/handler/annotation/GrpcMethod.java | 8 +- .../handler/annotation/GrpcService.java | 7 +- .../grpc/server/protobuf/ProtobufCodec.java | 5 +- .../grpc/server/protobuf/ProtobufField.java | 5 +- .../protobuf/ProtobufFieldTypeEnum.java | 5 +- .../grpc/server/protobuf/ProtobufProxy.java | 6 +- .../protobuf/annotation/ProtobufClass.java | 5 +- .../annotation/ProtobufCustomizedField.java | 8 +- .../annotation/ProtobufEnableZigZap.java | 7 +- .../protobuf/annotation/ProtobufIgnore.java | 7 +- .../protobuf/annotation/ProtobufPacked.java | 5 +- .../server/service/ArthasSampleService.java | 13 ++- .../service/impl/ArthasSampleServiceImpl.java | 29 +++---- .../service/req/ArthasSampleRequest.java | 83 ------------------- .../service/req/ArthasUnittestRequest.java | 21 +++++ ...ponse.java => ArthasUnittestResponse.java} | 7 +- .../arthas/grpc/server/utils/ByteUtil.java | 5 +- .../arthas/grpc/server/utils/ReflectUtil.java | 5 +- .../src/main/proto/arthasSample.proto | 8 +- .../src/main/proto/arthasUnittest.proto | 16 ++++ .../java/{ => unittest}/grpc/GrpcTest.java | 5 +- .../{ => unittest}/protobuf/ProtoBufTest.java | 9 +- .../protobuf/ProtoBufTestReq.java | 15 ++-- 30 files changed, 131 insertions(+), 253 deletions(-) delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasSampleRequest.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasUnittestRequest.java rename arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/res/{ArthasSampleResponse.java => ArthasUnittestResponse.java} (72%) create mode 100644 arthas-grpc-server/src/main/proto/arthasUnittest.proto rename arthas-grpc-server/src/test/java/{ => unittest}/grpc/GrpcTest.java (62%) rename arthas-grpc-server/src/test/java/{ => unittest}/protobuf/ProtoBufTest.java (87%) rename arthas-grpc-server/src/test/java/{ => unittest}/protobuf/ProtoBufTestReq.java (87%) diff --git a/arthas-grpc-server/pom.xml b/arthas-grpc-server/pom.xml index 025ed5b385..352b7e8c12 100644 --- a/arthas-grpc-server/pom.xml +++ b/arthas-grpc-server/pom.xml @@ -69,12 +69,12 @@ io.grpc grpc-netty - test + provided io.grpc grpc-services - test + provided org.junit.vintage @@ -89,34 +89,34 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + + ${basedir}/src/main/proto + com.google.protobuf:protoc:3.11.0:exe:${os.detected.classifier} + grpc-java + io.grpc:protoc-gen-grpc-java:1.28.0:exe:${os.detected.classifier} + + + + + compile + compile-custom + + + + + + + + kr.motd.maven + os-maven-plugin + 1.4.1.Final + + + \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java index 07fc1a9718..eef0fbacd7 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server;/** - * @author: 風楪 - * @date: 2024/7/3 上午12:30 - */ +package com.taobao.arthas.grpc.server; import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; import com.taobao.arthas.grpc.server.handler.Http2Handler; @@ -65,7 +62,6 @@ public void initChannel(SocketChannel ch) { } }); - // Bind and start to accept incoming connections. b.bind(9090).sync().channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java index 36d5441cda..10f40b14cb 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java @@ -1,13 +1,9 @@ -package com.taobao.arthas.grpc.server.handler;/** - * @author: 風楪 - * @date: 2024/9/6 01:12 - */ +package com.taobao.arthas.grpc.server.handler; import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; -import com.taobao.arthas.grpc.server.utils.ByteUtil; import com.taobao.arthas.grpc.server.utils.ReflectUtil; import java.lang.invoke.MethodHandle; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java index 7a0498bf13..feda7c3357 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.handler;/** - * @author: 風楪 - * @date: 2024/9/4 23:07 - */ +package com.taobao.arthas.grpc.server.handler; import com.taobao.arthas.grpc.server.utils.ByteUtil; import io.netty.buffer.ByteBuf; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java index bbdaef78df..1c6c2ccec3 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.handler;/** - * @author: 風楪 - * @date: 2024/9/5 02:05 - */ +package com.taobao.arthas.grpc.server.handler; import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; @@ -50,7 +47,7 @@ public Http2Headers getEndStreamHeader() { return new DefaultHttp2Headers().set("grpc-status", "0"); } - public ByteBuf getResponseData(){ + public ByteBuf getResponseData() { return byteData; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2FrameRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2FrameRequest.java index 49091da077..1e8058dcce 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2FrameRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2FrameRequest.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.handler;/** - * @author: 風楪 - * @date: 2024/9/18 23:12 - */ +package com.taobao.arthas.grpc.server.handler; import java.util.List; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java index dbd700f118..fdf634f4eb 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.handler;/** - * @author: 風楪 - * @date: 2024/7/7 下午9:58 - */ +package com.taobao.arthas.grpc.server.handler; import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; @@ -75,7 +72,7 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) GrpcResponse response = new GrpcResponse(); byte[] bytes = grpcRequest.readData(); while (bytes != null) { - ProtobufCodec protobufCodec = ProtobufProxy.getCodecCacheSide(grpcDispatcher.getRequestClass(grpcRequest.getService(),grpcRequest.getMethod())); + ProtobufCodec protobufCodec = ProtobufProxy.getCodecCacheSide(grpcDispatcher.getRequestClass(grpcRequest.getService(), grpcRequest.getMethod())); Object decode = protobufCodec.decode(bytes); response = grpcDispatcher.execute(grpcRequest.getService(), grpcRequest.getMethod(), decode); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java index d109fdce59..10f1c9b724 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.handler.annotation;/** - * @author: 風楪 - * @date: 2024/9/6 01:57 - */ +package com.taobao.arthas.grpc.server.handler.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -13,9 +10,10 @@ * @date: 2024/9/6 01:57 * @description: GrpcMethod */ -@Target({ ElementType.METHOD }) +@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface GrpcMethod { String value() default ""; + boolean stream() default false; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java index 6d0751d501..c54dc7d624 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.handler.annotation;/** - * @author: 風楪 - * @date: 2024/9/6 01:57 - */ +package com.taobao.arthas.grpc.server.handler.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -13,7 +10,7 @@ * @date: 2024/9/6 01:57 * @description: GrpcService */ -@Target({ ElementType.TYPE }) +@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface GrpcService { String value() default ""; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufCodec.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufCodec.java index 0bd6486258..63b783fa3e 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufCodec.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufCodec.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.protobuf;/** - * @author: 風楪 - * @date: 2024/7/17 下午9:44 - */ +package com.taobao.arthas.grpc.server.protobuf; import com.google.protobuf.CodedInputStream; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufField.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufField.java index 69b1fcb4d9..498265a0c5 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufField.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufField.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.protobuf;/** - * @author: 風楪 - * @date: 2024/7/25 上午12:14 - */ +package com.taobao.arthas.grpc.server.protobuf; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufFieldTypeEnum.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufFieldTypeEnum.java index 36f3a1d1e7..6fc34e19a5 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufFieldTypeEnum.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufFieldTypeEnum.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.protobuf;/** - * @author: 風楪 - * @date: 2024/7/17 下午10:02 - */ +package com.taobao.arthas.grpc.server.protobuf; import com.google.protobuf.WireFormat; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java index bd6b690424..6903f6e018 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java @@ -1,8 +1,4 @@ -package com.taobao.arthas.grpc.server.protobuf;/** - * @author: 風楪 - * @date: 2024/7/17 下午9:57 - */ - +package com.taobao.arthas.grpc.server.protobuf; import com.google.protobuf.WireFormat; import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufEnableZigZap; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufClass.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufClass.java index 62482239c7..5843dee4ad 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufClass.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufClass.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.protobuf.annotation;/** - * @author: 風楪 - * @date: 2024/7/25 上午12:19 - */ +package com.taobao.arthas.grpc.server.protobuf.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufCustomizedField.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufCustomizedField.java index 59eea05f4f..adfa7f05f3 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufCustomizedField.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufCustomizedField.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.protobuf.annotation;/** - * @author: 風楪 - * @date: 2024/7/25 上午12:21 - */ +package com.taobao.arthas.grpc.server.protobuf.annotation; import com.taobao.arthas.grpc.server.protobuf.ProtobufFieldTypeEnum; @@ -15,9 +12,10 @@ * @date: 2024/7/25 上午12:21 * @description: ProtobufField 用于自定义标识字段 */ -@Target({ ElementType.FIELD }) +@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface ProtobufCustomizedField { int order() default 0; + ProtobufFieldTypeEnum protoBufFieldType() default ProtobufFieldTypeEnum.DEFAULT; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufEnableZigZap.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufEnableZigZap.java index f92c9cbbe6..4a85807fd1 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufEnableZigZap.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufEnableZigZap.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.protobuf.annotation;/** - * @author: 風楪 - * @date: 2024/7/28 下午7:27 - */ +package com.taobao.arthas.grpc.server.protobuf.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -13,7 +10,7 @@ * @date: 2024/7/28 下午7:27 * @description: EnableZigZap 是否启用 zigzap 编码 */ -@Target({ ElementType.TYPE }) +@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface ProtobufEnableZigZap { } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufIgnore.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufIgnore.java index fa6b00a2f9..269a3edcb4 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufIgnore.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufIgnore.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.protobuf.annotation;/** - * @author: 風楪 - * @date: 2024/7/25 上午12:44 - */ +package com.taobao.arthas.grpc.server.protobuf.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -13,7 +10,7 @@ * @date: 2024/7/25 上午12:44 * @description: ProtobufIgnore */ -@Target({ ElementType.TYPE, ElementType.FIELD }) +@Target({ElementType.TYPE, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface ProtobufIgnore { } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufPacked.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufPacked.java index 1944558a52..7045ad6003 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufPacked.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufPacked.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.protobuf.annotation;/** - * @author: 風楪 - * @date: 2024/7/30 上午2:01 - */ +package com.taobao.arthas.grpc.server.protobuf.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java index 79c6499f58..732fe570fa 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java @@ -1,10 +1,7 @@ -package com.taobao.arthas.grpc.server.service;/** - * @author: 風楪 - * @date: 2024/6/30 下午11:42 - */ +package com.taobao.arthas.grpc.server.service; -import com.taobao.arthas.grpc.server.service.req.ArthasSampleRequest; -import com.taobao.arthas.grpc.server.service.res.ArthasSampleResponse; +import com.taobao.arthas.grpc.server.service.req.ArthasUnittestRequest; +import com.taobao.arthas.grpc.server.service.res.ArthasUnittestResponse; /** * @author: FengYe @@ -12,6 +9,6 @@ * @description: ArthasSampleService */ public interface ArthasSampleService { - ArthasSampleResponse trace(ArthasSampleRequest command); - ArthasSampleResponse watch(ArthasSampleRequest command); + ArthasUnittestResponse trace(ArthasUnittestRequest command); + ArthasUnittestResponse watch(ArthasUnittestRequest command); } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java index 0d88de0501..5e5cb415e3 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java @@ -1,36 +1,33 @@ -package com.taobao.arthas.grpc.server.service.impl;/** - * @author: 風楪 - * @date: 2024/6/30 下午11:43 - */ +package com.taobao.arthas.grpc.server.service.impl; import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; import com.taobao.arthas.grpc.server.service.ArthasSampleService; -import com.taobao.arthas.grpc.server.service.req.ArthasSampleRequest; -import com.taobao.arthas.grpc.server.service.res.ArthasSampleResponse; +import com.taobao.arthas.grpc.server.service.req.ArthasUnittestRequest; +import com.taobao.arthas.grpc.server.service.res.ArthasUnittestResponse; /** * @author: FengYe * @date: 2024/6/30 下午11:43 * @description: ArthasSampleServiceImpl */ -@GrpcService("arthasSample.ArthasTempService") +@GrpcService("arthas.sample.ArthasTempService") public class ArthasSampleServiceImpl implements ArthasSampleService { @Override @GrpcMethod("trace") - public ArthasSampleResponse trace(ArthasSampleRequest command) { - ArthasSampleResponse arthasSampleResponse = new ArthasSampleResponse(); - arthasSampleResponse.setMessage("trace"); - return arthasSampleResponse; + public ArthasUnittestResponse trace(ArthasUnittestRequest command) { + ArthasUnittestResponse arthasUnittestResponse = new ArthasUnittestResponse(); + arthasUnittestResponse.setMessage("trace"); + return arthasUnittestResponse; } @Override @GrpcMethod(value = "watch", stream = true) - public ArthasSampleResponse watch(ArthasSampleRequest command) { - String name = command.getName(); - ArthasSampleResponse arthasSampleResponse = new ArthasSampleResponse(); - arthasSampleResponse.setMessage(name); - return arthasSampleResponse; + public ArthasUnittestResponse watch(ArthasUnittestRequest command) { + String message = command.getMessage(); + ArthasUnittestResponse arthasUnittestResponse = new ArthasUnittestResponse(); + arthasUnittestResponse.setMessage(message); + return arthasUnittestResponse; } } \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasSampleRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasSampleRequest.java deleted file mode 100644 index d5eaabc29f..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasSampleRequest.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.taobao.arthas.grpc.server.service.req;/** - * @author: 風楪 - * @date: 2024/7/14 上午4:28 - */ - -import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; - -import java.util.List; - -/** - * @author: FengYe - * @date: 2024/7/14 上午4:28 - * @description: ArthasSampleRequest - */ - -@ProtobufClass -public class ArthasSampleRequest { - - private String name; - private double age; - private long price; - private StatusEnum status; - private List testList; - - @ProtobufClass - public enum StatusEnum { - START(1, "开始"), - STOP(2, "结束"); - - StatusEnum(int code, String desc) { - this.code = code; - this.desc = desc; - } - - private int code; - private String desc; - } - - @ProtobufClass - public static class TestClass { - private String name; - } - - public List getTestList() { - return testList; - } - - public void setTestList(List testList) { - this.testList = testList; - } - - public StatusEnum getStatus() { - return status; - } - - public void setStatus(StatusEnum status) { - this.status = status; - } - - public long getPrice() { - return price; - } - - public void setPrice(long price) { - this.price = price; - } - - public double getAge() { - return age; - } - - public void setAge(double age) { - this.age = age; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasUnittestRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasUnittestRequest.java new file mode 100644 index 0000000000..b2b1c44352 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasUnittestRequest.java @@ -0,0 +1,21 @@ +package com.taobao.arthas.grpc.server.service.req; + +import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; + +/** + * @author: FengYe + * @date: 2024/7/14 上午4:28 + * @description: ArthasSampleRequest + */ +@ProtobufClass +public class ArthasUnittestRequest { + private String message; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/res/ArthasSampleResponse.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/res/ArthasUnittestResponse.java similarity index 72% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/res/ArthasSampleResponse.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/res/ArthasUnittestResponse.java index 09ab40f71f..49c5a58387 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/res/ArthasSampleResponse.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/res/ArthasUnittestResponse.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.service.res;/** - * @author: 風楪 - * @date: 2024/8/11 22:11 - */ +package com.taobao.arthas.grpc.server.service.res; import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; @@ -11,7 +8,7 @@ * @description: ArthasSampleResponse */ @ProtobufClass -public class ArthasSampleResponse { +public class ArthasUnittestResponse { private String message; public String getMessage() { diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java index 29044d36e6..2bd239521d 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.utils;/** - * @author: 風楪 - * @date: 2024/9/5 00:51 - */ +package com.taobao.arthas.grpc.server.utils; import io.netty.buffer.ByteBuf; import io.netty.buffer.PooledByteBufAllocator; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ReflectUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ReflectUtil.java index 5da93c9dfd..c6e3e053ed 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ReflectUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ReflectUtil.java @@ -1,7 +1,4 @@ -package com.taobao.arthas.grpc.server.utils;/** - * @author: 風楪 - * @date: 2024/9/6 02:20 - */ +package com.taobao.arthas.grpc.server.utils; import java.io.File; import java.net.URL; diff --git a/arthas-grpc-server/src/main/proto/arthasSample.proto b/arthas-grpc-server/src/main/proto/arthasSample.proto index 3e14f5eec9..33df5d8a80 100644 --- a/arthas-grpc-server/src/main/proto/arthasSample.proto +++ b/arthas-grpc-server/src/main/proto/arthasSample.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package arthasSample; +package arthas.sample; enum StatusEnum { @@ -8,9 +8,9 @@ enum StatusEnum { STOP = 1; } -service ArthasTempService { - rpc trace(ArthasSampleRequest) returns (ArthasSampleResponse); - rpc watch(stream ArthasSampleRequest) returns (stream ArthasSampleResponse); +service ArthasSampleService { + rpc testUnary(ArthasSampleRequest) returns (ArthasSampleResponse); + rpc testBiStream(stream ArthasSampleRequest) returns (stream ArthasSampleResponse); } message ArthasSampleRequest { diff --git a/arthas-grpc-server/src/main/proto/arthasUnittest.proto b/arthas-grpc-server/src/main/proto/arthasUnittest.proto new file mode 100644 index 0000000000..731c0f1e63 --- /dev/null +++ b/arthas-grpc-server/src/main/proto/arthasUnittest.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package arthas.unittest; + +service ArthasUnittestService { + rpc trace(ArthasUnittestRequest) returns (ArthasUnittestResponse); + rpc watch(stream ArthasUnittestRequest) returns (stream ArthasUnittestResponse); +} + +message ArthasUnittestRequest { + string message = 1; +} + +message ArthasUnittestResponse{ + string message = 1; +} diff --git a/arthas-grpc-server/src/test/java/grpc/GrpcTest.java b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java similarity index 62% rename from arthas-grpc-server/src/test/java/grpc/GrpcTest.java rename to arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java index d5c4b440e5..504282a14c 100644 --- a/arthas-grpc-server/src/test/java/grpc/GrpcTest.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java @@ -1,7 +1,4 @@ -package grpc; /** - * @author: 風楪 - * @date: 2024/9/19 22:20 - */ +package unittest.grpc; /** * @author: FengYe diff --git a/arthas-grpc-server/src/test/java/protobuf/ProtoBufTest.java b/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTest.java similarity index 87% rename from arthas-grpc-server/src/test/java/protobuf/ProtoBufTest.java rename to arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTest.java index 0c72902d93..c7de3bf762 100644 --- a/arthas-grpc-server/src/test/java/protobuf/ProtoBufTest.java +++ b/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTest.java @@ -1,11 +1,8 @@ -package protobuf; /** - * @author: 風楪 - * @date: 2024/9/19 22:35 - */ +package unittest.protobuf; import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; -import com.taobao.arthas.grpc.server.service.req.ArthasSampleRequest; +import com.taobao.arthas.grpc.server.service.req.ArthasUnittestRequest; import org.junit.Assert; import org.junit.Test; @@ -27,7 +24,7 @@ public void testEncodeAndDecode() { protoBufTestReq.setName("test"); protoBufTestReq.setAge(18); protoBufTestReq.setPrice(100L); - protoBufTestReq.setStatus(ArthasSampleRequest.StatusEnum.START); + protoBufTestReq.setStatus(ProtoBufTestReq.StatusEnum.START); List list = new ArrayList<>(); list.add(new ProtoBufTestReq.TestClass("test1")); list.add(new ProtoBufTestReq.TestClass("test2")); diff --git a/arthas-grpc-server/src/test/java/protobuf/ProtoBufTestReq.java b/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTestReq.java similarity index 87% rename from arthas-grpc-server/src/test/java/protobuf/ProtoBufTestReq.java rename to arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTestReq.java index 53c48512c3..2af16c91fe 100644 --- a/arthas-grpc-server/src/test/java/protobuf/ProtoBufTestReq.java +++ b/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTestReq.java @@ -1,14 +1,11 @@ -package protobuf;/** - * @author: 風楪 - * @date: 2024/9/19 22:38 - */ +package unittest.protobuf; +import com.taobao.arthas.grpc.server.protobuf.ProtobufField; import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; -import com.taobao.arthas.grpc.server.service.req.ArthasSampleRequest; +import com.taobao.arthas.grpc.server.service.req.ArthasUnittestRequest; import java.util.List; import java.util.Map; -import java.util.Objects; /** * @author: FengYe @@ -20,7 +17,7 @@ public class ProtoBufTestReq { private String name; private double age; private long price; - private ArthasSampleRequest.StatusEnum status; + private ProtoBufTestReq.StatusEnum status; private List testList; private Map testMap; @@ -74,11 +71,11 @@ public void setTestList(List testList) { this.testList = testList; } - public ArthasSampleRequest.StatusEnum getStatus() { + public ProtoBufTestReq.StatusEnum getStatus() { return status; } - public void setStatus(ArthasSampleRequest.StatusEnum status) { + public void setStatus(ProtoBufTestReq.StatusEnum status) { this.status = status; } From 7aa63aa641f9309610f83cc148fea2f0dcf8acdf Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Tue, 24 Sep 2024 01:40:06 +0800 Subject: [PATCH 35/48] update: add unit test and error process --- arthas-grpc-server/pom.xml | 10 +- .../arthas/grpc/server/handler/ErrorRes.java | 21 ++++ .../grpc/server/handler/GrpcDispatcher.java | 2 +- .../grpc/server/handler/GrpcResponse.java | 10 +- .../grpc/server/handler/Http2Handler.java | 20 ++-- .../service/impl/ArthasSampleServiceImpl.java | 7 +- .../src/main/proto/arthasSample.proto | 29 ----- .../src/test/java/unittest/grpc/GrpcTest.java | 102 +++++++++++++++++- 8 files changed, 153 insertions(+), 48 deletions(-) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/ErrorRes.java delete mode 100644 arthas-grpc-server/src/main/proto/arthasSample.proto diff --git a/arthas-grpc-server/pom.xml b/arthas-grpc-server/pom.xml index 352b7e8c12..6f3e8575d5 100644 --- a/arthas-grpc-server/pom.xml +++ b/arthas-grpc-server/pom.xml @@ -39,13 +39,13 @@ io.netty netty-codec-http2 - 4.1.111.Final + 4.1.72.Final com.google.protobuf protobuf-java - 4.27.2 + 3.19.2 @@ -70,6 +70,12 @@ io.grpc grpc-netty provided + + + io.netty + netty-codec-http2 + + io.grpc diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/ErrorRes.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/ErrorRes.java new file mode 100644 index 0000000000..c7cbe8d7ea --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/ErrorRes.java @@ -0,0 +1,21 @@ +package com.taobao.arthas.grpc.server.handler; + +import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; + +/** + * @author: FengYe + * @date: 2024/9/23 23:58 + * @description: ErrorRes + */ +@ProtobufClass +public class ErrorRes { + private String errorMsg; + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java index 10f40b14cb..8f3daed1be 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java @@ -89,7 +89,7 @@ public GrpcResponse execute(GrpcRequest request) throws Throwable { */ public Class getRequestClass(String serviceName, String methodName) { //protobuf 规范只能有单入参 - return grpcMethodInvokeMap.get(generateGrpcMethodKey(serviceName, methodName)).type().parameterArray()[0]; + return Optional.ofNullable(grpcMethodInvokeMap.get(generateGrpcMethodKey(serviceName, methodName))).orElseThrow(() -> new RuntimeException("The specified grpc method does not exist")).type().parameterArray()[0]; } public void checkGrpcStream(GrpcRequest request) { diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java index 1c6c2ccec3..bbf678fcd7 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java @@ -51,10 +51,14 @@ public ByteBuf getResponseData() { return byteData; } - public void writeResponseData(Object response) throws IOException { + public void writeResponseData(Object response) { ProtobufCodec codec = ProtobufProxy.getCodecCacheSide(clazz); - byte[] encode = codec.encode(response); - + byte[] encode = null; + try { + encode = codec.encode(response); + } catch (IOException e) { + throw new RuntimeException("ProtobufCodec encode error"); + } this.byteData = ByteUtil.newByteBuf(); this.byteData.writeBoolean(false); this.byteData.writeInt(encode.length); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java index fdf634f4eb..ff4c01a8b6 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java @@ -2,6 +2,8 @@ import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; +import com.taobao.arthas.grpc.server.utils.ByteUtil; +import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http2.*; @@ -43,7 +45,6 @@ protected void channelRead0(ChannelHandlerContext ctx, Http2Frame frame) throws } } - @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); @@ -65,7 +66,6 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) GrpcRequest grpcRequest = dataBuffer.get(dataFrame.stream().id()); grpcRequest.writeData(dataFrame.content()); - System.out.println(dataFrame.stream().id()); if (grpcRequest.isStream()) { // 流式调用,即刻响应 try { @@ -92,7 +92,7 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(dataFrame.stream())); } } catch (Throwable e) { - processError(ctx, e); + processError(ctx, e, dataFrame.stream()); } } else { // 非流式调用,等到 endStream 再响应 @@ -103,14 +103,20 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(dataFrame.stream())); ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(dataFrame.stream())); } catch (Throwable e) { - processError(ctx, e); + processError(ctx, e, dataFrame.stream()); } } } } - private void processError(ChannelHandlerContext ctx, Throwable e) { - //todo - ctx.writeAndFlush(e.getMessage()); + private void processError(ChannelHandlerContext ctx, Throwable e, Http2FrameStream stream) { + GrpcResponse response = new GrpcResponse(); + ErrorRes errorRes = new ErrorRes(); + errorRes.setErrorMsg(e.getMessage()); + response.setClazz(ErrorRes.class); + response.writeResponseData(errorRes); + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(stream)); + ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(stream)); + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(stream)); } } \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java index 5e5cb415e3..b01787c36c 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java @@ -11,23 +11,22 @@ * @date: 2024/6/30 下午11:43 * @description: ArthasSampleServiceImpl */ -@GrpcService("arthas.sample.ArthasTempService") +@GrpcService("arthas.unittest.ArthasUnittestService") public class ArthasSampleServiceImpl implements ArthasSampleService { @Override @GrpcMethod("trace") public ArthasUnittestResponse trace(ArthasUnittestRequest command) { ArthasUnittestResponse arthasUnittestResponse = new ArthasUnittestResponse(); - arthasUnittestResponse.setMessage("trace"); + arthasUnittestResponse.setMessage(command.getMessage()); return arthasUnittestResponse; } @Override @GrpcMethod(value = "watch", stream = true) public ArthasUnittestResponse watch(ArthasUnittestRequest command) { - String message = command.getMessage(); ArthasUnittestResponse arthasUnittestResponse = new ArthasUnittestResponse(); - arthasUnittestResponse.setMessage(message); + arthasUnittestResponse.setMessage(command.getMessage()); return arthasUnittestResponse; } } \ No newline at end of file diff --git a/arthas-grpc-server/src/main/proto/arthasSample.proto b/arthas-grpc-server/src/main/proto/arthasSample.proto deleted file mode 100644 index 33df5d8a80..0000000000 --- a/arthas-grpc-server/src/main/proto/arthasSample.proto +++ /dev/null @@ -1,29 +0,0 @@ -syntax = "proto3"; - -package arthas.sample; - - -enum StatusEnum { - START = 0; - STOP = 1; -} - -service ArthasSampleService { - rpc testUnary(ArthasSampleRequest) returns (ArthasSampleResponse); - rpc testBiStream(stream ArthasSampleRequest) returns (stream ArthasSampleResponse); -} - -message ArthasSampleRequest { - string name = 1; double age = 2; - int64 price = 3; - StatusEnum status = 4; - repeated TestClass testList = 5; -} - -message TestClass{ - string name = 1; -} - -message ArthasSampleResponse { - string message = 1; -} diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java index 504282a14c..d6d57b12e0 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java @@ -1,10 +1,108 @@ package unittest.grpc; +import arthas.unittest.ArthasUnittest; +import arthas.unittest.ArthasUnittestServiceGrpc; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import io.grpc.StatusRuntimeException; +import io.grpc.stub.StreamObserver; +import org.junit.Test; +import org.junit.jupiter.api.Disabled; + +import java.util.concurrent.CountDownLatch; + /** * @author: FengYe - * @date: 2024/9/19 22:20 - * @description: grpc.GrpcTest + * @date: 2024/9/24 00:17 + * @description: GrpcUnaryTest */ public class GrpcTest { + private static final String target = "localhost:9090"; + private ArthasUnittestServiceGrpc.ArthasUnittestServiceBlockingStub blockingStub = null; + private ArthasUnittestServiceGrpc.ArthasUnittestServiceStub stub = null; + + @Disabled("跳过启动测试") + @Test + public void testUnary() { + ManagedChannel channel = ManagedChannelBuilder.forTarget(target) + .usePlaintext() + .build(); + + blockingStub = ArthasUnittestServiceGrpc.newBlockingStub(channel); + + try { + trace("trace"); + } finally { + channel.shutdownNow(); + } + } + + @Disabled("跳过启动测试") + @Test + public void testStream() { + ManagedChannel channel = ManagedChannelBuilder.forTarget(target) + .usePlaintext() + .build(); + + stub = ArthasUnittestServiceGrpc.newStub(channel); + + try { + watch("watch1", "watch2", "watch3"); + } finally { + channel.shutdownNow(); + } + } + + private void trace(String name) { + ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.newBuilder().setMessage(name).build(); + try { + ArthasUnittest.ArthasUnittestResponse res = blockingStub.trace(request); + System.out.println(res.getMessage()); + } catch (StatusRuntimeException e) { + e.printStackTrace(); + System.out.println("RPC failed: " + e.getStatus()); + } + } + + private void watch(String... names) { + // 使用 CountDownLatch 来等待所有响应 + CountDownLatch finishLatch = new CountDownLatch(1); + + StreamObserver watch = stub.watch(new StreamObserver() { + @Override + public void onNext(ArthasUnittest.ArthasUnittestResponse value) { + System.out.println("watch: " + value.getMessage()); + } + + @Override + public void onError(Throwable t) { + + } + + @Override + public void onCompleted() { + System.out.println("Finished sending watch."); + } + }); + + + try { + for (String name : names) { + ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.newBuilder().setMessage(name).build(); + Thread.sleep(1000L); + watch.onNext(request); + } + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + watch.onCompleted(); + } + // 等待服务器的响应 + try { + finishLatch.await(); // 等待完成 + } catch (InterruptedException e) { + System.out.println("Client interrupted: " + e.getMessage()); + } + } } From 2061dea048713d86fa016e1f4e88ebd878f43723 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Thu, 10 Oct 2024 21:28:21 +0800 Subject: [PATCH 36/48] update: add maven profiles --- arthas-grpc-server/pom.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arthas-grpc-server/pom.xml b/arthas-grpc-server/pom.xml index 6f3e8575d5..b74977c7b6 100644 --- a/arthas-grpc-server/pom.xml +++ b/arthas-grpc-server/pom.xml @@ -125,4 +125,18 @@ + + + + mac + + + mac + + + + osx-x86_64 + + + \ No newline at end of file From e594712d53b1b1a1921501754038c30e42d6ca2e Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Sun, 13 Oct 2024 03:17:22 +0800 Subject: [PATCH 37/48] update: add more unit test and log --- arthas-grpc-server/pom.xml | 25 +++++++- .../grpc/server/ArthasGrpcBootstrap.java | 13 +++++ .../arthas/grpc/server/ArthasGrpcServer.java | 58 ++++++++----------- .../grpc/server/handler/GrpcDispatcher.java | 6 +- .../grpc/server/handler/GrpcRequest.java | 35 ++--------- .../grpc/server/handler/Http2Handler.java | 5 ++ .../server/protobuf/utils/ProtoBufUtil.java | 22 ++++--- .../src/test/java/unittest/grpc/GrpcTest.java | 42 +++++++++----- .../java/unittest/grpc/Http2HandlerTest.java | 9 +++ .../src/test/java/unittest/grpc/TempTest.java | 36 ++++++++++++ .../grpc/service/ArthasSampleService.java | 14 +++++ .../service/impl/ArthasSampleServiceImpl.java | 32 ++++++++++ .../service/req/ArthasUnittestRequest.java | 21 +++++++ .../service/res/ArthasUnittestResponse.java | 21 +++++++ .../src/test/proto/arthasUnittest.proto | 16 +++++ 15 files changed, 262 insertions(+), 93 deletions(-) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcBootstrap.java create mode 100644 arthas-grpc-server/src/test/java/unittest/grpc/Http2HandlerTest.java create mode 100644 arthas-grpc-server/src/test/java/unittest/grpc/TempTest.java create mode 100644 arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java create mode 100644 arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java create mode 100644 arthas-grpc-server/src/test/java/unittest/grpc/service/req/ArthasUnittestRequest.java create mode 100644 arthas-grpc-server/src/test/java/unittest/grpc/service/res/ArthasUnittestResponse.java create mode 100644 arthas-grpc-server/src/test/proto/arthasUnittest.proto diff --git a/arthas-grpc-server/pom.xml b/arthas-grpc-server/pom.xml index b74977c7b6..c073413454 100644 --- a/arthas-grpc-server/pom.xml +++ b/arthas-grpc-server/pom.xml @@ -63,8 +63,6 @@ - - io.grpc @@ -92,6 +90,29 @@ junit-jupiter test + + javax.annotation + javax.annotation-api + 1.3.2 + provided + true + + + com.alibaba.arthas + arthas-repackage-logger + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + org.slf4j + slf4j-api + diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcBootstrap.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcBootstrap.java new file mode 100644 index 0000000000..c953ffc954 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcBootstrap.java @@ -0,0 +1,13 @@ +package com.taobao.arthas.grpc.server; + +/** + * @author: FengYe + * @date: 2024/10/13 02:40 + * @description: ArthasGrpcServerBootstrap + */ +public class ArthasGrpcBootstrap { + public static void main(String[] args) { + ArthasGrpcServer arthasGrpcServer = new ArthasGrpcServer(9090, null); + arthasGrpcServer.start(); + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java index eef0fbacd7..3425657256 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java @@ -1,8 +1,13 @@ package com.taobao.arthas.grpc.server; +import com.alibaba.arthas.deps.ch.qos.logback.classic.Level; +import com.alibaba.arthas.deps.ch.qos.logback.classic.LoggerContext; +import com.alibaba.arthas.deps.org.slf4j.Logger; +import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; import com.taobao.arthas.grpc.server.handler.Http2Handler; import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; @@ -10,10 +15,8 @@ import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http2.Http2FrameCodecBuilder; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; + +import java.lang.invoke.MethodHandles; /** * @author: FengYe @@ -22,31 +25,23 @@ */ public class ArthasGrpcServer { - public static void main(String[] args) throws Exception { -// //自签名生成密钥 -// SelfSignedCertificate ssc = new SelfSignedCertificate(); -// SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) -// .build(); -// -// // 指定生成自签名证书和密钥的位置 -// File certFile = new File(System.getProperty("user.dir"),"certificate.crt"); -// File keyFile = new File(System.getProperty("user.dir"),"privateKey.key"); -// -// // 将生成的证书和私钥移动到指定位置 -// moveFile(ssc.certificate(), certFile); -// moveFile(ssc.privateKey(), keyFile); -// -// System.out.println(certFile.getAbsolutePath()); -// System.out.println(keyFile.getAbsolutePath()); -// -// System.out.println("Certificate: " + ssc.certificate()); -// System.out.println("Private Key: " + ssc.privateKey()); + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass().getName()); + + private int port = 9090; + + private String grpcServicePackageName; + + public ArthasGrpcServer(int port, String grpcServicePackageName) { + this.port = port; + this.grpcServicePackageName = grpcServicePackageName; + } + public void start() { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); GrpcDispatcher grpcDispatcher = new GrpcDispatcher(); - grpcDispatcher.loadGrpcService(); + grpcDispatcher.loadGrpcService(grpcServicePackageName); try { ServerBootstrap b = new ServerBootstrap(); @@ -56,23 +51,18 @@ public static void main(String[] args) throws Exception { .childHandler(new ChannelInitializer() { @Override public void initChannel(SocketChannel ch) { -// ch.pipeline().addLast(sslCtx.newHandler(ch.alloc())); ch.pipeline().addLast(Http2FrameCodecBuilder.forServer().build()); ch.pipeline().addLast(new Http2Handler(grpcDispatcher)); } }); - - b.bind(9090).sync().channel().closeFuture().sync(); + Channel channel = b.bind(port).sync().channel(); + logger.info("ArthasGrpcServer start successfully on port: {}", port); + channel.closeFuture().sync(); + } catch (InterruptedException e) { + logger.error("ArthasGrpcServer start error", e); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } - - private static void moveFile(File source, File target) throws IOException { - if (!target.getParentFile().exists()) { - target.getParentFile().mkdirs(); - } - Files.move(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING); - } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java index 8f3daed1be..abfb30559e 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java @@ -22,14 +22,14 @@ */ public class GrpcDispatcher { - private static final String GRPC_SERVICE_PACKAGE_NAME = "com.taobao.arthas.grpc.server.service.impl"; + private static final String DEFAULT_GRPC_SERVICE_PACKAGE_NAME = "com.taobao.arthas.grpc.server.service.impl"; private Map grpcMethodInvokeMap = new HashMap<>(); private Map grpcMethodStreamMap = new HashMap<>(); - public void loadGrpcService() { - List> classes = ReflectUtil.findClasses(GRPC_SERVICE_PACKAGE_NAME); + public void loadGrpcService(String grpcServicePackageName) { + List> classes = ReflectUtil.findClasses(Optional.ofNullable(grpcServicePackageName).orElse(DEFAULT_GRPC_SERVICE_PACKAGE_NAME)); for (Class clazz : classes) { if (clazz.isAnnotationPresent(GrpcService.class)) { try { diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java index feda7c3357..1f3b2a852a 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java @@ -96,52 +96,29 @@ public void clearData() { byteData.clear(); } - // todo 后续优化gzip处理 private byte[] decompressGzip(byte[] compressedData) { boolean isGzip = (compressedData.length > 2 && (compressedData[0] & 0xff) == 0x1f && (compressedData[1] & 0xff) == 0x8b); if (isGzip) { - try { - InputStream byteStream = new ByteArrayInputStream(compressedData); - GZIPInputStream gzipStream = new GZIPInputStream(byteStream); + try (InputStream byteStream = new ByteArrayInputStream(compressedData); + GZIPInputStream gzipStream = new GZIPInputStream(byteStream); + ByteArrayOutputStream out = new ByteArrayOutputStream()) { + byte[] buffer = new byte[1024]; int len; - ByteArrayOutputStream out = new ByteArrayOutputStream(); while ((len = gzipStream.read(buffer)) != -1) { out.write(buffer, 0, len); } return out.toByteArray(); } catch (IOException e) { System.err.println("Failed to decompress GZIP data: " + e.getMessage()); + // Optionally rethrow the exception or return an Optional + return null; // or throw new RuntimeException(e); } - return null; } else { return compressedData; } } - private ByteBuf decompressGzip(ByteBuf byteBuf) { - byte[] compressedData = ByteUtil.getBytes(byteBuf); - boolean isGzip = (compressedData.length > 2 && (compressedData[0] & 0xff) == 0x1f && (compressedData[1] & 0xff) == 0x8b); - if (isGzip) { - try { - InputStream byteStream = new ByteArrayInputStream(compressedData); - GZIPInputStream gzipStream = new GZIPInputStream(byteStream); - byte[] buffer = new byte[1024]; - int len; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - while ((len = gzipStream.read(buffer)) != -1) { - out.write(buffer, 0, len); - } - return ByteUtil.newByteBuf(out.toByteArray()); - } catch (IOException e) { - System.err.println("Failed to decompress GZIP data: " + e.getMessage()); - } - return null; - } else { - return byteBuf; - } - } - public Integer getStreamId() { return streamId; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java index ff4c01a8b6..22eee788be 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java @@ -1,5 +1,7 @@ package com.taobao.arthas.grpc.server.handler; +import com.alibaba.arthas.deps.org.slf4j.Logger; +import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; import com.taobao.arthas.grpc.server.utils.ByteUtil; @@ -9,6 +11,7 @@ import io.netty.handler.codec.http2.*; import java.io.*; +import java.lang.invoke.MethodHandles; import java.util.concurrent.ConcurrentHashMap; /** @@ -18,6 +21,8 @@ */ public class Http2Handler extends SimpleChannelInboundHandler { + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass().getName()); + private GrpcDispatcher grpcDispatcher; /** diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java index f0ee0162bd..d4c23882e0 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java @@ -1,5 +1,7 @@ package com.taobao.arthas.grpc.server.protobuf.utils; +import com.alibaba.arthas.deps.org.slf4j.Logger; +import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.google.protobuf.*; import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; import com.taobao.arthas.grpc.server.protobuf.ProtobufField; @@ -13,6 +15,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.lang.Enum; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -29,6 +32,8 @@ */ public class ProtoBufUtil { + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass().getName()); + public static final Map, ProtobufFieldTypeEnum> TYPE_MAPPER; public static final Map PRIMITIVE_TYPE_MAPPING; @@ -81,9 +86,9 @@ public class ProtoBufUtil { Set> primitiveTypeNames = new HashSet>(16); primitiveTypeNames.addAll(PRIMITIVE_WRAPPER_TYPE_MAP.values()); - primitiveTypeNames.addAll(Arrays.asList(new Class[] { boolean[].class, byte[].class, char[].class, - double[].class, float[].class, int[].class, long[].class, short[].class })); - for (Iterator> it = primitiveTypeNames.iterator(); it.hasNext();) { + primitiveTypeNames.addAll(Arrays.asList(new Class[]{boolean[].class, byte[].class, char[].class, + double[].class, float[].class, int[].class, long[].class, short[].class})); + for (Iterator> it = primitiveTypeNames.iterator(); it.hasNext(); ) { Class primitiveClass = (Class) it.next(); PRIMIIIVE_TYPE_CLASS_MAPPING.put(primitiveClass.getName(), primitiveClass); } @@ -289,7 +294,7 @@ public static Object getField(Object t, String name) { try { return field.get(t); } catch (Exception e) { - //todo log + logger.error("ProtoBufUtil getFiled error, t:{}, name:{}", t, name, e); } return null; } @@ -303,7 +308,7 @@ public static void setField(Object t, String name, Object value) { try { field.set(t, value); } catch (Exception e) { - //todo log + logger.error("ProtoBufUtil setFiled error, t:{}, name:{}, value:{}", t, name, value, e); } } @@ -376,7 +381,7 @@ public static String getGetterDynamicString(ProtobufField protobufField, Class[0]); return DYNAMIC_TARGET + PACKAGE_SEPARATOR + getter + "()"; } catch (Exception e) { - //todo log + logger.error("ProtoBufUtil getGetterDynamicString error, protobufField:{}, dynamicTargetClass:{}", protobufField, dynamicTargetClass, e); } String type = field.getType().getCanonicalName(); @@ -427,7 +432,6 @@ public static String getSizeDynamicString(ProtobufField field) { } javaType = capitalize(javaType); dynamicFieldName = dynamicFieldName + protobufFieldType.getToPrimitiveType(); - //todo check 感觉上面这个有点问题,测试的时候看下 return "com.google.protobuf.CodedOutputStream.compute" + javaType + "Size(" + order + "," + dynamicFieldName + ");" + LINE_BREAK; } @@ -859,7 +863,7 @@ public static String getSetFieldDynamicString(ProtobufField protobufField, Class return DYNAMIC_TARGET + PACKAGE_SEPARATOR + setter + "(" + express + ")\n"; } catch (Exception e) { - //todo log + logger.error("ProtoBufUtil getSetFieldDynamicString error, protobufField:{}, dynamicTargetClass:{}, express:{}", protobufField, dynamicTargetClass, express, e); } if (isList) { @@ -1103,7 +1107,7 @@ public static String getClassName(Class cls) { return cls.getSimpleName(); } - public static boolean isEmpty(String s){ + public static boolean isEmpty(String s) { return s == null || s.isEmpty(); } diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java index d6d57b12e0..fcb46576c6 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java @@ -2,11 +2,15 @@ import arthas.unittest.ArthasUnittest; import arthas.unittest.ArthasUnittestServiceGrpc; +import com.taobao.arthas.grpc.server.ArthasGrpcServer; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.StatusRuntimeException; import io.grpc.stub.StreamObserver; +import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import java.util.concurrent.CountDownLatch; @@ -17,14 +21,25 @@ * @description: GrpcUnaryTest */ public class GrpcTest { - private static final String target = "localhost:9090"; + private static final String HOST = "localhost"; + private static final int PORT = 9090; + private static final String HOST_PORT = HOST + ":" + PORT; + private static final String UNIT_TEST_GRPC_SERVICE_PACKAGE_NAME = "unittest.grpc.service.impl"; private ArthasUnittestServiceGrpc.ArthasUnittestServiceBlockingStub blockingStub = null; private ArthasUnittestServiceGrpc.ArthasUnittestServiceStub stub = null; - @Disabled("跳过启动测试") + @Before + public void startServer() { + Thread grpcWebProxyStart = new Thread(() -> { + ArthasGrpcServer arthasGrpcServer = new ArthasGrpcServer(PORT, UNIT_TEST_GRPC_SERVICE_PACKAGE_NAME); + arthasGrpcServer.start(); + }); + grpcWebProxyStart.start(); + } + @Test public void testUnary() { - ManagedChannel channel = ManagedChannelBuilder.forTarget(target) + ManagedChannel channel = ManagedChannelBuilder.forTarget(HOST_PORT) .usePlaintext() .build(); @@ -37,10 +52,9 @@ public void testUnary() { } } - @Disabled("跳过启动测试") @Test public void testStream() { - ManagedChannel channel = ManagedChannelBuilder.forTarget(target) + ManagedChannel channel = ManagedChannelBuilder.forTarget(HOST_PORT) .usePlaintext() .build(); @@ -55,16 +69,11 @@ public void testStream() { private void trace(String name) { ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.newBuilder().setMessage(name).build(); - try { - ArthasUnittest.ArthasUnittestResponse res = blockingStub.trace(request); - System.out.println(res.getMessage()); - } catch (StatusRuntimeException e) { - e.printStackTrace(); - System.out.println("RPC failed: " + e.getStatus()); - } + ArthasUnittest.ArthasUnittestResponse res = blockingStub.trace(request); + System.out.println(res.getMessage()); } - private void watch(String... names) { + private void watch(String... names){ // 使用 CountDownLatch 来等待所有响应 CountDownLatch finishLatch = new CountDownLatch(1); @@ -92,17 +101,18 @@ public void onCompleted() { Thread.sleep(1000L); watch.onNext(request); } - } catch (Exception e) { - throw new RuntimeException(e); + } catch (InterruptedException e) { + e.printStackTrace(); } finally { watch.onCompleted(); + finishLatch.countDown(); } // 等待服务器的响应 try { finishLatch.await(); // 等待完成 } catch (InterruptedException e) { - System.out.println("Client interrupted: " + e.getMessage()); + e.printStackTrace(); } } } diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/Http2HandlerTest.java b/arthas-grpc-server/src/test/java/unittest/grpc/Http2HandlerTest.java new file mode 100644 index 0000000000..97edfa5220 --- /dev/null +++ b/arthas-grpc-server/src/test/java/unittest/grpc/Http2HandlerTest.java @@ -0,0 +1,9 @@ +package unittest.grpc; + +/** + * @author: FengYe + * @date: 2024/10/12 00:56 + * @description: Http2HandlerTest + */ +public class Http2HandlerTest { +} diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/TempTest.java b/arthas-grpc-server/src/test/java/unittest/grpc/TempTest.java new file mode 100644 index 0000000000..754b7d92ef --- /dev/null +++ b/arthas-grpc-server/src/test/java/unittest/grpc/TempTest.java @@ -0,0 +1,36 @@ +package unittest.grpc; + +import org.junit.Before; +import org.junit.Test; + +/** + * @author: FengYe + * @date: 2024/10/13 03:03 + * @description: TempTest + */ +public class TempTest { + + private int num; + + @Before + public void before() throws InterruptedException { + System.out.println("before start,"+num++); + System.out.println(Thread.currentThread().getId()); + Thread.sleep(10000L); + System.out.println("before end"); + } + + @Test + public void test1() throws InterruptedException { + System.out.println("test1 start"); + Thread.sleep(1000L); + System.out.println("test1 end"); + } + + @Test + public void test2() throws InterruptedException { + System.out.println("test2 start"); + Thread.sleep(1000L); + System.out.println("test2 end"); + } +} diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java new file mode 100644 index 0000000000..4830fc9c51 --- /dev/null +++ b/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java @@ -0,0 +1,14 @@ +package unittest.grpc.service; + +import unittest.grpc.service.req.ArthasUnittestRequest; +import unittest.grpc.service.res.ArthasUnittestResponse; + +/** + * @author: FengYe + * @date: 2024/6/30 下午11:42 + * @description: ArthasSampleService + */ +public interface ArthasSampleService { + ArthasUnittestResponse trace(ArthasUnittestRequest command); + ArthasUnittestResponse watch(ArthasUnittestRequest command); +} diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java new file mode 100644 index 0000000000..4ed9cc6e73 --- /dev/null +++ b/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java @@ -0,0 +1,32 @@ +package unittest.grpc.service.impl; + +import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; +import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; +import unittest.grpc.service.ArthasSampleService; +import unittest.grpc.service.req.ArthasUnittestRequest; +import unittest.grpc.service.res.ArthasUnittestResponse; + +/** + * @author: FengYe + * @date: 2024/6/30 下午11:43 + * @description: ArthasSampleServiceImpl + */ +@GrpcService("arthas.unittest.ArthasUnittestService") +public class ArthasSampleServiceImpl implements ArthasSampleService { + + @Override + @GrpcMethod("trace") + public ArthasUnittestResponse trace(ArthasUnittestRequest command) { + ArthasUnittestResponse arthasUnittestResponse = new ArthasUnittestResponse(); + arthasUnittestResponse.setMessage(command.getMessage()); + return arthasUnittestResponse; + } + + @Override + @GrpcMethod(value = "watch", stream = true) + public ArthasUnittestResponse watch(ArthasUnittestRequest command) { + ArthasUnittestResponse arthasUnittestResponse = new ArthasUnittestResponse(); + arthasUnittestResponse.setMessage(command.getMessage()); + return arthasUnittestResponse; + } +} \ No newline at end of file diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/req/ArthasUnittestRequest.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/req/ArthasUnittestRequest.java new file mode 100644 index 0000000000..018e15490e --- /dev/null +++ b/arthas-grpc-server/src/test/java/unittest/grpc/service/req/ArthasUnittestRequest.java @@ -0,0 +1,21 @@ +package unittest.grpc.service.req; + +import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; + +/** + * @author: FengYe + * @date: 2024/7/14 上午4:28 + * @description: ArthasSampleRequest + */ +@ProtobufClass +public class ArthasUnittestRequest { + private String message; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/res/ArthasUnittestResponse.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/res/ArthasUnittestResponse.java new file mode 100644 index 0000000000..f340c3844d --- /dev/null +++ b/arthas-grpc-server/src/test/java/unittest/grpc/service/res/ArthasUnittestResponse.java @@ -0,0 +1,21 @@ +package unittest.grpc.service.res; + +import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; + +/** + * @author: FengYe + * @date: 2024/8/11 22:11 + * @description: ArthasSampleResponse + */ +@ProtobufClass +public class ArthasUnittestResponse { + private String message; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/arthas-grpc-server/src/test/proto/arthasUnittest.proto b/arthas-grpc-server/src/test/proto/arthasUnittest.proto new file mode 100644 index 0000000000..731c0f1e63 --- /dev/null +++ b/arthas-grpc-server/src/test/proto/arthasUnittest.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package arthas.unittest; + +service ArthasUnittestService { + rpc trace(ArthasUnittestRequest) returns (ArthasUnittestResponse); + rpc watch(stream ArthasUnittestRequest) returns (stream ArthasUnittestResponse); +} + +message ArthasUnittestRequest { + string message = 1; +} + +message ArthasUnittestResponse{ + string message = 1; +} From 45b6027b926089728ce32c6beebb73c7659c2749 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Tue, 15 Oct 2024 03:01:34 +0800 Subject: [PATCH 38/48] update: Migration from jprotobuf serialization to protobuf:protoc; migrating the business logic in http2Handler to a custom executorGroup; added http2 headers in grpcRequest; support for handling resetStream --- .../arthas/grpc/server/ArthasGrpcServer.java | 4 +- .../arthas/grpc/server/handler/ErrorRes.java | 1 + .../grpc/server/handler/GrpcDispatcher.java | 59 ++++++----- .../grpc/server/handler/GrpcRequest.java | 14 +++ .../grpc/server/handler/GrpcResponse.java | 42 ++++++-- .../grpc/server/handler/Http2Handler.java | 99 +++++++++++-------- .../grpc/server/protobuf/ProtobufCodec.java | 1 + .../grpc/server/protobuf/ProtobufField.java | 1 + .../protobuf/ProtobufFieldTypeEnum.java | 1 + .../grpc/server/protobuf/ProtobufProxy.java | 1 + .../protobuf/annotation/ProtobufClass.java | 1 + .../annotation/ProtobufCustomizedField.java | 1 + .../annotation/ProtobufEnableZigZap.java | 1 + .../protobuf/annotation/ProtobufIgnore.java | 1 + .../protobuf/annotation/ProtobufPacked.java | 1 + .../server/service/ArthasSampleService.java | 7 +- .../service/impl/ArthasSampleServiceImpl.java | 26 ++--- .../service/req/ArthasUnittestRequest.java | 21 ---- .../service/res/ArthasUnittestResponse.java | 21 ---- .../src/main/proto/arthasGrpc.proto | 7 ++ .../src/main/proto/arthasUnittest.proto | 2 +- .../src/test/java/unittest/grpc/GrpcTest.java | 4 +- .../grpc/service/ArthasSampleService.java | 7 +- .../service/impl/ArthasSampleServiceImpl.java | 26 ++--- .../service/req/ArthasUnittestRequest.java | 21 ---- .../service/res/ArthasUnittestResponse.java | 21 ---- .../src/test/proto/arthasUnittest.proto | 2 +- 27 files changed, 202 insertions(+), 191 deletions(-) delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasUnittestRequest.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/res/ArthasUnittestResponse.java create mode 100644 arthas-grpc-server/src/main/proto/arthasGrpc.proto delete mode 100644 arthas-grpc-server/src/test/java/unittest/grpc/service/req/ArthasUnittestRequest.java delete mode 100644 arthas-grpc-server/src/test/java/unittest/grpc/service/res/ArthasUnittestResponse.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java index 3425657256..f46f4dda5b 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java @@ -15,6 +15,8 @@ import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http2.Http2FrameCodecBuilder; +import io.netty.util.concurrent.DefaultEventExecutorGroup; +import io.netty.util.concurrent.EventExecutorGroup; import java.lang.invoke.MethodHandles; @@ -38,7 +40,7 @@ public ArthasGrpcServer(int port, String grpcServicePackageName) { public void start() { EventLoopGroup bossGroup = new NioEventLoopGroup(1); - EventLoopGroup workerGroup = new NioEventLoopGroup(); + EventLoopGroup workerGroup = new NioEventLoopGroup(10); GrpcDispatcher grpcDispatcher = new GrpcDispatcher(); grpcDispatcher.loadGrpcService(grpcServicePackageName); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/ErrorRes.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/ErrorRes.java index c7cbe8d7ea..5045283271 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/ErrorRes.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/ErrorRes.java @@ -8,6 +8,7 @@ * @description: ErrorRes */ @ProtobufClass +@Deprecated public class ErrorRes { private String errorMsg; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java index abfb30559e..2a0185f568 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java @@ -1,9 +1,9 @@ package com.taobao.arthas.grpc.server.handler; + import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; -import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; -import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; +import com.taobao.arthas.grpc.server.utils.ByteUtil; import com.taobao.arthas.grpc.server.utils.ReflectUtil; import java.lang.invoke.MethodHandle; @@ -22,11 +22,17 @@ */ public class GrpcDispatcher { - private static final String DEFAULT_GRPC_SERVICE_PACKAGE_NAME = "com.taobao.arthas.grpc.server.service.impl"; + public static final String DEFAULT_GRPC_SERVICE_PACKAGE_NAME = "com.taobao.arthas.grpc.server.service.impl"; + + public static Map grpcMethodInvokeMap = new HashMap<>(); + + public static Map requestParseFromMap = new HashMap<>(); + public static Map requestToByteArrayMap = new HashMap<>(); - private Map grpcMethodInvokeMap = new HashMap<>(); + public static Map responseParseFromMap = new HashMap<>(); + public static Map responseToByteArrayMap = new HashMap<>(); - private Map grpcMethodStreamMap = new HashMap<>(); + public static Map grpcMethodStreamMap = new HashMap<>(); public void loadGrpcService(String grpcServicePackageName) { List> classes = ReflectUtil.findClasses(Optional.ofNullable(grpcServicePackageName).orElse(DEFAULT_GRPC_SERVICE_PACKAGE_NAME)); @@ -43,10 +49,20 @@ public void loadGrpcService(String grpcServicePackageName) { for (Method method : declaredMethods) { if (method.isAnnotationPresent(GrpcMethod.class)) { GrpcMethod grpcMethod = method.getAnnotation(GrpcMethod.class); - MethodHandle methodHandle = lookup.unreflect(method); + MethodHandle grpcInvoke = lookup.unreflect(method); + Class requestClass = grpcInvoke.type().parameterType(1); + Class responseClass = grpcInvoke.type().returnType(); + MethodHandle requestParseFrom = lookup.findStatic(requestClass, "parseFrom", MethodType.methodType(requestClass, byte[].class)); + MethodHandle responseParseFrom = lookup.findStatic(responseClass, "parseFrom", MethodType.methodType(responseClass, byte[].class)); + MethodHandle requestToByteArray = lookup.findVirtual(requestClass, "toByteArray", MethodType.methodType(byte[].class)); + MethodHandle responseToByteArray = lookup.findVirtual(responseClass, "toByteArray", MethodType.methodType(byte[].class)); String grpcMethodKey = generateGrpcMethodKey(grpcService.value(), grpcMethod.value()); - grpcMethodInvokeMap.put(grpcMethodKey, methodHandle.bindTo(instance)); + grpcMethodInvokeMap.put(grpcMethodKey, grpcInvoke.bindTo(instance)); grpcMethodStreamMap.put(grpcMethodKey, grpcMethod.stream()); + requestParseFromMap.put(grpcMethodKey, requestParseFrom); + responseParseFromMap.put(grpcMethodKey, responseParseFrom); + requestToByteArrayMap.put(grpcMethodKey, requestToByteArray); + responseToByteArrayMap.put(grpcMethodKey, responseToByteArray); } } } catch (Exception e) { @@ -56,16 +72,15 @@ public void loadGrpcService(String grpcServicePackageName) { } } - private String generateGrpcMethodKey(String serviceName, String methodName) { - return serviceName + "." + methodName; - } - - public GrpcResponse execute(String serviceName, String methodName, Object arg) throws Throwable { - MethodHandle methodHandle = grpcMethodInvokeMap.get(generateGrpcMethodKey(serviceName, methodName)); - MethodType type = grpcMethodInvokeMap.get(generateGrpcMethodKey(serviceName, methodName)).type(); - Object execute = methodHandle.invoke(arg); + public GrpcResponse execute(String service, String method, byte[] arg) throws Throwable { + MethodHandle methodHandle = grpcMethodInvokeMap.get(generateGrpcMethodKey(service, method)); + MethodType type = grpcMethodInvokeMap.get(generateGrpcMethodKey(service, method)).type(); + Object req = requestParseFromMap.get(generateGrpcMethodKey(service, method)).invoke(arg); + Object execute = methodHandle.invoke(req); GrpcResponse grpcResponse = new GrpcResponse(); grpcResponse.setClazz(type.returnType()); + grpcResponse.setService(service); + grpcResponse.setMethod(method); grpcResponse.writeResponseData(execute); return grpcResponse; } @@ -73,11 +88,7 @@ public GrpcResponse execute(String serviceName, String methodName, Object arg) t public GrpcResponse execute(GrpcRequest request) throws Throwable { String service = request.getService(); String method = request.getMethod(); - // protobuf 规范只能有单入参 - request.setClazz(getRequestClass(request.getService(), request.getMethod())); - ProtobufCodec protobufCodec = ProtobufProxy.getCodecCacheSide(request.getClazz()); - Object decode = protobufCodec.decode(request.readData()); - return this.execute(service, method, decode); + return this.execute(service, method, request.readData()); } /** @@ -87,12 +98,16 @@ public GrpcResponse execute(GrpcRequest request) throws Throwable { * @param methodName * @return */ - public Class getRequestClass(String serviceName, String methodName) { + public static Class getRequestClass(String serviceName, String methodName) { //protobuf 规范只能有单入参 return Optional.ofNullable(grpcMethodInvokeMap.get(generateGrpcMethodKey(serviceName, methodName))).orElseThrow(() -> new RuntimeException("The specified grpc method does not exist")).type().parameterArray()[0]; } - public void checkGrpcStream(GrpcRequest request) { + public static String generateGrpcMethodKey(String serviceName, String methodName) { + return serviceName + "." + methodName; + } + + public static void checkGrpcStream(GrpcRequest request) { request.setStream( Optional.ofNullable(grpcMethodStreamMap.get(generateGrpcMethodKey(request.getService(), request.getMethod()))) .orElse(false) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java index 1f3b2a852a..5deb390f88 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java @@ -2,6 +2,7 @@ import com.taobao.arthas.grpc.server.utils.ByteUtil; import io.netty.buffer.ByteBuf; +import io.netty.handler.codec.http2.Http2Headers; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -56,6 +57,11 @@ public class GrpcRequest { */ private boolean streamFirstData; + /** + * http2 headers + */ + private Http2Headers headers; + public GrpcRequest(Integer streamId, String path, String method) { this.streamId = streamId; @@ -158,4 +164,12 @@ public boolean isStreamFirstData() { public void setStreamFirstData(boolean streamFirstData) { this.streamFirstData = streamFirstData; } + + public Http2Headers getHeaders() { + return headers; + } + + public void setHeaders(Http2Headers headers) { + this.headers = headers; + } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java index bbf678fcd7..165807e80f 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java @@ -1,13 +1,12 @@ package com.taobao.arthas.grpc.server.handler; -import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; -import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; + +import arthas.grpc.common.ArthasGrpc; import com.taobao.arthas.grpc.server.utils.ByteUtil; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http2.DefaultHttp2Headers; import io.netty.handler.codec.http2.Http2Headers; -import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -20,6 +19,16 @@ public class GrpcResponse { private Map headers; + /** + * 请求的 service + */ + private String service; + + /** + * 请求的 method + */ + private String method; + /** * 二进制数据 */ @@ -52,12 +61,15 @@ public ByteBuf getResponseData() { } public void writeResponseData(Object response) { - ProtobufCodec codec = ProtobufProxy.getCodecCacheSide(clazz); byte[] encode = null; try { - encode = codec.encode(response); - } catch (IOException e) { - throw new RuntimeException("ProtobufCodec encode error"); + if (ArthasGrpc.ErrorRes.class.equals(clazz)) { + encode = ((ArthasGrpc.ErrorRes) response).toByteArray(); + } else { + encode = (byte[]) GrpcDispatcher.responseToByteArrayMap.get(GrpcDispatcher.generateGrpcMethodKey(service, method)).invoke(response); + } + } catch (Throwable e) { + throw new RuntimeException(e); } this.byteData = ByteUtil.newByteBuf(); this.byteData.writeBoolean(false); @@ -68,4 +80,20 @@ public void writeResponseData(Object response) { public void setClazz(Class clazz) { this.clazz = clazz; } + + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java index 22eee788be..eb6d9b5218 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java @@ -1,14 +1,15 @@ package com.taobao.arthas.grpc.server.handler; + +import arthas.grpc.common.ArthasGrpc; import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; -import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; -import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; -import com.taobao.arthas.grpc.server.utils.ByteUtil; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.nio.NioEventLoopGroup; import io.netty.handler.codec.http2.*; +import io.netty.util.concurrent.EventExecutorGroup; import java.io.*; import java.lang.invoke.MethodHandles; @@ -25,6 +26,8 @@ public class Http2Handler extends SimpleChannelInboundHandler { private GrpcDispatcher grpcDispatcher; + private final EventExecutorGroup executorGroup = new NioEventLoopGroup(); + /** * 暂存收到的所有请求的数据 */ @@ -47,6 +50,8 @@ protected void channelRead0(ChannelHandlerContext ctx, Http2Frame frame) throws handleGrpcRequest((Http2HeadersFrame) frame, ctx); } else if (frame instanceof Http2DataFrame) { handleGrpcData((Http2DataFrame) frame, ctx); + } else if (frame instanceof Http2ResetFrame) { + handleResetStream((Http2ResetFrame) frame, ctx); } } @@ -62,63 +67,71 @@ private void handleGrpcRequest(Http2HeadersFrame headersFrame, ChannelHandlerCon // 去掉前面的斜杠,然后按斜杠分割 String[] parts = path.substring(1).split("/"); GrpcRequest grpcRequest = new GrpcRequest(headersFrame.stream().id(), parts[0], parts[1]); - grpcDispatcher.checkGrpcStream(grpcRequest); + grpcRequest.setHeaders(headersFrame.headers()); + GrpcDispatcher.checkGrpcStream(grpcRequest); dataBuffer.put(id, grpcRequest); System.out.println("Received headers: " + headersFrame.headers()); } private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) throws IOException { GrpcRequest grpcRequest = dataBuffer.get(dataFrame.stream().id()); - grpcRequest.writeData(dataFrame.content()); - - if (grpcRequest.isStream()) { - // 流式调用,即刻响应 - try { - GrpcResponse response = new GrpcResponse(); - byte[] bytes = grpcRequest.readData(); - while (bytes != null) { - ProtobufCodec protobufCodec = ProtobufProxy.getCodecCacheSide(grpcDispatcher.getRequestClass(grpcRequest.getService(), grpcRequest.getMethod())); - Object decode = protobufCodec.decode(bytes); - response = grpcDispatcher.execute(grpcRequest.getService(), grpcRequest.getMethod(), decode); - - // 针对第一个响应发送 header - if (grpcRequest.isStreamFirstData()) { - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(dataFrame.stream())); - grpcRequest.setStreamFirstData(false); - } - ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(dataFrame.stream())); + ByteBuf content = dataFrame.content(); + grpcRequest.writeData(content); - bytes = grpcRequest.readData(); - } + executorGroup.execute(() -> { + if (grpcRequest.isStream()) { + // 流式调用,即刻响应 + try { + GrpcResponse response = new GrpcResponse(); + byte[] bytes = grpcRequest.readData(); + while (bytes != null) { + response = grpcDispatcher.execute(grpcRequest.getService(), grpcRequest.getMethod(), bytes); + + // 针对第一个响应发送 header + if (grpcRequest.isStreamFirstData()) { + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(dataFrame.stream())); + grpcRequest.setStreamFirstData(false); + } + ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(dataFrame.stream())); + + bytes = grpcRequest.readData(); + } - grpcRequest.clearData(); + grpcRequest.clearData(); - if (dataFrame.isEndStream()) { - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(dataFrame.stream())); - } - } catch (Throwable e) { - processError(ctx, e, dataFrame.stream()); - } - } else { - // 非流式调用,等到 endStream 再响应 - if (dataFrame.isEndStream()) { - try { - GrpcResponse response = grpcDispatcher.execute(grpcRequest); - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(dataFrame.stream())); - ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(dataFrame.stream())); - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(dataFrame.stream())); + if (dataFrame.isEndStream()) { + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(dataFrame.stream())); + } } catch (Throwable e) { processError(ctx, e, dataFrame.stream()); } + } else { + // 非流式调用,等到 endStream 再响应 + if (dataFrame.isEndStream()) { + try { + GrpcResponse response = grpcDispatcher.execute(grpcRequest); + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(dataFrame.stream())); + ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(dataFrame.stream())); + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(dataFrame.stream())); + } catch (Throwable e) { + processError(ctx, e, dataFrame.stream()); + } + } } - } + }); + } + + private void handleResetStream(Http2ResetFrame resetFrame, ChannelHandlerContext ctx) { + int id = resetFrame.stream().id(); + System.out.println("handleResetStream"); + dataBuffer.remove(id); } private void processError(ChannelHandlerContext ctx, Throwable e, Http2FrameStream stream) { GrpcResponse response = new GrpcResponse(); - ErrorRes errorRes = new ErrorRes(); - errorRes.setErrorMsg(e.getMessage()); - response.setClazz(ErrorRes.class); + ArthasGrpc.ErrorRes.Builder builder = ArthasGrpc.ErrorRes.newBuilder(); + ArthasGrpc.ErrorRes errorRes = builder.setErrorMsg(e.getMessage()).build(); + response.setClazz(ArthasGrpc.ErrorRes.class); response.writeResponseData(errorRes); ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(stream)); ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(stream)); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufCodec.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufCodec.java index 63b783fa3e..d0bcf99d61 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufCodec.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufCodec.java @@ -9,6 +9,7 @@ * @date: 2024/7/17 下午9:44 * @description: Codec */ +@Deprecated public interface ProtobufCodec { byte[] encode(T t) throws IOException; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufField.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufField.java index 498265a0c5..96b36dfa2f 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufField.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufField.java @@ -13,6 +13,7 @@ * @date: 2024/7/25 上午12:14 * @description: ProtobufField */ +@Deprecated public class ProtobufField { /** diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufFieldTypeEnum.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufFieldTypeEnum.java index 6fc34e19a5..f45375b609 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufFieldTypeEnum.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufFieldTypeEnum.java @@ -7,6 +7,7 @@ * @date: 2024/7/17 下午10:02 * @description: ProtoBufFieldType */ +@Deprecated public enum ProtobufFieldTypeEnum { /** * types defined in .proto file. diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java index 6903f6e018..ba2d8a9571 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java @@ -16,6 +16,7 @@ * @date: 2024/7/17 下午9:57 * @description: ProtoBufProxy */ +@Deprecated public class ProtobufProxy { private static final String TEMPLATE_FILE = "/class_template.tpl"; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufClass.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufClass.java index 5843dee4ad..6779ae63fc 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufClass.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufClass.java @@ -12,5 +12,6 @@ */ @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) +@Deprecated public @interface ProtobufClass { } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufCustomizedField.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufCustomizedField.java index adfa7f05f3..0d9fceefa5 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufCustomizedField.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufCustomizedField.java @@ -14,6 +14,7 @@ */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) +@Deprecated public @interface ProtobufCustomizedField { int order() default 0; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufEnableZigZap.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufEnableZigZap.java index 4a85807fd1..7912c2fed4 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufEnableZigZap.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufEnableZigZap.java @@ -12,5 +12,6 @@ */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) +@Deprecated public @interface ProtobufEnableZigZap { } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufIgnore.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufIgnore.java index 269a3edcb4..9dabb0394d 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufIgnore.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufIgnore.java @@ -12,5 +12,6 @@ */ @Target({ElementType.TYPE, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) +@Deprecated public @interface ProtobufIgnore { } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufPacked.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufPacked.java index 7045ad6003..5a3c8113da 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufPacked.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufPacked.java @@ -13,5 +13,6 @@ */ @Target({ ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) +@Deprecated public @interface ProtobufPacked { } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java index 732fe570fa..28e5a48717 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java @@ -1,7 +1,6 @@ package com.taobao.arthas.grpc.server.service; -import com.taobao.arthas.grpc.server.service.req.ArthasUnittestRequest; -import com.taobao.arthas.grpc.server.service.res.ArthasUnittestResponse; +import arthas.grpc.unittest.ArthasUnittest; /** * @author: FengYe @@ -9,6 +8,6 @@ * @description: ArthasSampleService */ public interface ArthasSampleService { - ArthasUnittestResponse trace(ArthasUnittestRequest command); - ArthasUnittestResponse watch(ArthasUnittestRequest command); + ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittestRequest command); + ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittestRequest command); } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java index b01787c36c..c2973f5afd 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java @@ -1,32 +1,36 @@ package com.taobao.arthas.grpc.server.service.impl; +import arthas.grpc.unittest.ArthasUnittest; import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; import com.taobao.arthas.grpc.server.service.ArthasSampleService; -import com.taobao.arthas.grpc.server.service.req.ArthasUnittestRequest; -import com.taobao.arthas.grpc.server.service.res.ArthasUnittestResponse; /** * @author: FengYe * @date: 2024/6/30 下午11:43 * @description: ArthasSampleServiceImpl */ -@GrpcService("arthas.unittest.ArthasUnittestService") +@GrpcService("arthas.grpc.unittest.ArthasUnittestService") public class ArthasSampleServiceImpl implements ArthasSampleService { @Override @GrpcMethod("trace") - public ArthasUnittestResponse trace(ArthasUnittestRequest command) { - ArthasUnittestResponse arthasUnittestResponse = new ArthasUnittestResponse(); - arthasUnittestResponse.setMessage(command.getMessage()); - return arthasUnittestResponse; + public ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittestRequest command) { + try { + Thread.sleep(50000L); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); + builder.setMessage(command.getMessage()); + return builder.build(); } @Override @GrpcMethod(value = "watch", stream = true) - public ArthasUnittestResponse watch(ArthasUnittestRequest command) { - ArthasUnittestResponse arthasUnittestResponse = new ArthasUnittestResponse(); - arthasUnittestResponse.setMessage(command.getMessage()); - return arthasUnittestResponse; + public ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittestRequest command) { + ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); + builder.setMessage(command.getMessage()); + return builder.build(); } } \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasUnittestRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasUnittestRequest.java deleted file mode 100644 index b2b1c44352..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/req/ArthasUnittestRequest.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.taobao.arthas.grpc.server.service.req; - -import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; - -/** - * @author: FengYe - * @date: 2024/7/14 上午4:28 - * @description: ArthasSampleRequest - */ -@ProtobufClass -public class ArthasUnittestRequest { - private String message; - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/res/ArthasUnittestResponse.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/res/ArthasUnittestResponse.java deleted file mode 100644 index 49c5a58387..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/res/ArthasUnittestResponse.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.taobao.arthas.grpc.server.service.res; - -import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; - -/** - * @author: FengYe - * @date: 2024/8/11 22:11 - * @description: ArthasSampleResponse - */ -@ProtobufClass -public class ArthasUnittestResponse { - private String message; - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } -} diff --git a/arthas-grpc-server/src/main/proto/arthasGrpc.proto b/arthas-grpc-server/src/main/proto/arthasGrpc.proto new file mode 100644 index 0000000000..103bb9b4c4 --- /dev/null +++ b/arthas-grpc-server/src/main/proto/arthasGrpc.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package arthas.grpc.common; + +message ErrorRes { + string errorMsg = 1; +} \ No newline at end of file diff --git a/arthas-grpc-server/src/main/proto/arthasUnittest.proto b/arthas-grpc-server/src/main/proto/arthasUnittest.proto index 731c0f1e63..b615b056aa 100644 --- a/arthas-grpc-server/src/main/proto/arthasUnittest.proto +++ b/arthas-grpc-server/src/main/proto/arthasUnittest.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package arthas.unittest; +package arthas.grpc.unittest; service ArthasUnittestService { rpc trace(ArthasUnittestRequest) returns (ArthasUnittestResponse); diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java index fcb46576c6..5c3f1d4195 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java @@ -1,7 +1,7 @@ package unittest.grpc; -import arthas.unittest.ArthasUnittest; -import arthas.unittest.ArthasUnittestServiceGrpc; +import arthas.grpc.unittest.ArthasUnittest; +import arthas.grpc.unittest.ArthasUnittestServiceGrpc; import com.taobao.arthas.grpc.server.ArthasGrpcServer; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java index 4830fc9c51..d9f17088b9 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java @@ -1,7 +1,6 @@ package unittest.grpc.service; -import unittest.grpc.service.req.ArthasUnittestRequest; -import unittest.grpc.service.res.ArthasUnittestResponse; +import arthas.grpc.unittest.ArthasUnittest; /** * @author: FengYe @@ -9,6 +8,6 @@ * @description: ArthasSampleService */ public interface ArthasSampleService { - ArthasUnittestResponse trace(ArthasUnittestRequest command); - ArthasUnittestResponse watch(ArthasUnittestRequest command); + ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittestRequest command); + ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittestRequest command); } diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java index 4ed9cc6e73..e7cc9d4df0 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java @@ -1,32 +1,36 @@ package unittest.grpc.service.impl; +import arthas.grpc.unittest.ArthasUnittest; import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; import unittest.grpc.service.ArthasSampleService; -import unittest.grpc.service.req.ArthasUnittestRequest; -import unittest.grpc.service.res.ArthasUnittestResponse; /** * @author: FengYe * @date: 2024/6/30 下午11:43 * @description: ArthasSampleServiceImpl */ -@GrpcService("arthas.unittest.ArthasUnittestService") +@GrpcService("arthas.grpc.unittest.ArthasUnittestService") public class ArthasSampleServiceImpl implements ArthasSampleService { @Override @GrpcMethod("trace") - public ArthasUnittestResponse trace(ArthasUnittestRequest command) { - ArthasUnittestResponse arthasUnittestResponse = new ArthasUnittestResponse(); - arthasUnittestResponse.setMessage(command.getMessage()); - return arthasUnittestResponse; + public ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittestRequest command) { + try { + Thread.sleep(5000L); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); + builder.setMessage(command.getMessage()); + return builder.build(); } @Override @GrpcMethod(value = "watch", stream = true) - public ArthasUnittestResponse watch(ArthasUnittestRequest command) { - ArthasUnittestResponse arthasUnittestResponse = new ArthasUnittestResponse(); - arthasUnittestResponse.setMessage(command.getMessage()); - return arthasUnittestResponse; + public ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittestRequest command) { + ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); + builder.setMessage(command.getMessage()); + return builder.build(); } } \ No newline at end of file diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/req/ArthasUnittestRequest.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/req/ArthasUnittestRequest.java deleted file mode 100644 index 018e15490e..0000000000 --- a/arthas-grpc-server/src/test/java/unittest/grpc/service/req/ArthasUnittestRequest.java +++ /dev/null @@ -1,21 +0,0 @@ -package unittest.grpc.service.req; - -import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; - -/** - * @author: FengYe - * @date: 2024/7/14 上午4:28 - * @description: ArthasSampleRequest - */ -@ProtobufClass -public class ArthasUnittestRequest { - private String message; - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } -} diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/res/ArthasUnittestResponse.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/res/ArthasUnittestResponse.java deleted file mode 100644 index f340c3844d..0000000000 --- a/arthas-grpc-server/src/test/java/unittest/grpc/service/res/ArthasUnittestResponse.java +++ /dev/null @@ -1,21 +0,0 @@ -package unittest.grpc.service.res; - -import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; - -/** - * @author: FengYe - * @date: 2024/8/11 22:11 - * @description: ArthasSampleResponse - */ -@ProtobufClass -public class ArthasUnittestResponse { - private String message; - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } -} diff --git a/arthas-grpc-server/src/test/proto/arthasUnittest.proto b/arthas-grpc-server/src/test/proto/arthasUnittest.proto index 731c0f1e63..b615b056aa 100644 --- a/arthas-grpc-server/src/test/proto/arthasUnittest.proto +++ b/arthas-grpc-server/src/test/proto/arthasUnittest.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package arthas.unittest; +package arthas.grpc.unittest; service ArthasUnittestService { rpc trace(ArthasUnittestRequest) returns (ArthasUnittestResponse); From b2a95f468f29348615ed742d9332e894b288085b Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Tue, 15 Oct 2024 22:58:43 +0800 Subject: [PATCH 39/48] update: optimize unit tests --- .../src/test/java/unittest/protobuf/ProtoBufTest.java | 1 - .../src/test/java/unittest/protobuf/ProtoBufTestReq.java | 2 -- 2 files changed, 3 deletions(-) diff --git a/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTest.java b/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTest.java index c7de3bf762..915457a210 100644 --- a/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTest.java +++ b/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTest.java @@ -2,7 +2,6 @@ import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; -import com.taobao.arthas.grpc.server.service.req.ArthasUnittestRequest; import org.junit.Assert; import org.junit.Test; diff --git a/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTestReq.java b/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTestReq.java index 2af16c91fe..6c27901da0 100644 --- a/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTestReq.java +++ b/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTestReq.java @@ -1,8 +1,6 @@ package unittest.protobuf; -import com.taobao.arthas.grpc.server.protobuf.ProtobufField; import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; -import com.taobao.arthas.grpc.server.service.req.ArthasUnittestRequest; import java.util.List; import java.util.Map; From 850a9b86f03dccb1dc02a0a903d0e294d6da7d75 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Tue, 15 Oct 2024 23:00:49 +0800 Subject: [PATCH 40/48] update: optimize unit tests --- .../java/unittest/grpc/Http2HandlerTest.java | 9 ----- .../src/test/java/unittest/grpc/TempTest.java | 36 ------------------- 2 files changed, 45 deletions(-) delete mode 100644 arthas-grpc-server/src/test/java/unittest/grpc/Http2HandlerTest.java delete mode 100644 arthas-grpc-server/src/test/java/unittest/grpc/TempTest.java diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/Http2HandlerTest.java b/arthas-grpc-server/src/test/java/unittest/grpc/Http2HandlerTest.java deleted file mode 100644 index 97edfa5220..0000000000 --- a/arthas-grpc-server/src/test/java/unittest/grpc/Http2HandlerTest.java +++ /dev/null @@ -1,9 +0,0 @@ -package unittest.grpc; - -/** - * @author: FengYe - * @date: 2024/10/12 00:56 - * @description: Http2HandlerTest - */ -public class Http2HandlerTest { -} diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/TempTest.java b/arthas-grpc-server/src/test/java/unittest/grpc/TempTest.java deleted file mode 100644 index 754b7d92ef..0000000000 --- a/arthas-grpc-server/src/test/java/unittest/grpc/TempTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package unittest.grpc; - -import org.junit.Before; -import org.junit.Test; - -/** - * @author: FengYe - * @date: 2024/10/13 03:03 - * @description: TempTest - */ -public class TempTest { - - private int num; - - @Before - public void before() throws InterruptedException { - System.out.println("before start,"+num++); - System.out.println(Thread.currentThread().getId()); - Thread.sleep(10000L); - System.out.println("before end"); - } - - @Test - public void test1() throws InterruptedException { - System.out.println("test1 start"); - Thread.sleep(1000L); - System.out.println("test1 end"); - } - - @Test - public void test2() throws InterruptedException { - System.out.println("test2 start"); - Thread.sleep(1000L); - System.out.println("test2 end"); - } -} From ea54ebef297c8c139d6674b086a9231d0b0c5b17 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Mon, 21 Oct 2024 01:58:08 +0800 Subject: [PATCH 41/48] update: remove custom protobuf serialization implementation, and add more unit tests. --- .../arthas/grpc/server/handler/ErrorRes.java | 22 - .../grpc/server/handler/GrpcDispatcher.java | 1 - .../grpc/server/handler/Http2Handler.java | 4 +- .../grpc/server/protobuf/ProtobufCodec.java | 21 - .../grpc/server/protobuf/ProtobufField.java | 286 ---- .../protobuf/ProtobufFieldTypeEnum.java | 177 --- .../grpc/server/protobuf/ProtobufProxy.java | 363 ----- .../protobuf/annotation/ProtobufClass.java | 17 - .../annotation/ProtobufCustomizedField.java | 22 - .../annotation/ProtobufEnableZigZap.java | 17 - .../protobuf/annotation/ProtobufIgnore.java | 17 - .../protobuf/annotation/ProtobufPacked.java | 18 - .../utils/CodedOutputStreamCache.java | 52 - .../server/protobuf/utils/EnumHandler.java | 13 - .../server/protobuf/utils/MiniTemplator.java | 1305 ----------------- .../protobuf/utils/ProtoBufClassCompiler.java | 283 ---- .../server/protobuf/utils/ProtoBufUtil.java | 1248 ---------------- .../src/main/proto/arthasUnittest.proto | 10 +- .../src/test/java/unittest/grpc/GrpcTest.java | 46 +- .../grpc/service/ArthasSampleService.java | 2 + .../service/impl/ArthasSampleServiceImpl.java | 24 + .../java/unittest/protobuf/ProtoBufTest.java | 47 - .../unittest/protobuf/ProtoBufTestReq.java | 124 -- 23 files changed, 82 insertions(+), 4037 deletions(-) delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/ErrorRes.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufCodec.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufField.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufFieldTypeEnum.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufClass.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufCustomizedField.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufEnableZigZap.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufIgnore.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufPacked.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/CodedOutputStreamCache.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/EnumHandler.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/MiniTemplator.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufClassCompiler.java delete mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java delete mode 100644 arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTest.java delete mode 100644 arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTestReq.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/ErrorRes.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/ErrorRes.java deleted file mode 100644 index 5045283271..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/ErrorRes.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.taobao.arthas.grpc.server.handler; - -import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; - -/** - * @author: FengYe - * @date: 2024/9/23 23:58 - * @description: ErrorRes - */ -@ProtobufClass -@Deprecated -public class ErrorRes { - private String errorMsg; - - public String getErrorMsg() { - return errorMsg; - } - - public void setErrorMsg(String errorMsg) { - this.errorMsg = errorMsg; - } -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java index 2a0185f568..087830750f 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java @@ -3,7 +3,6 @@ import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; -import com.taobao.arthas.grpc.server.utils.ByteUtil; import com.taobao.arthas.grpc.server.utils.ReflectUtil; import java.lang.invoke.MethodHandle; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java index eb6d9b5218..6572c8c68a 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java @@ -14,6 +14,7 @@ import java.io.*; import java.lang.invoke.MethodHandles; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; /** * @author: FengYe @@ -74,7 +75,8 @@ private void handleGrpcRequest(Http2HeadersFrame headersFrame, ChannelHandlerCon } private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) throws IOException { - GrpcRequest grpcRequest = dataBuffer.get(dataFrame.stream().id()); + int streamId = dataFrame.stream().id(); + GrpcRequest grpcRequest = dataBuffer.get(streamId); ByteBuf content = dataFrame.content(); grpcRequest.writeData(content); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufCodec.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufCodec.java deleted file mode 100644 index d0bcf99d61..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufCodec.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.taobao.arthas.grpc.server.protobuf; - -import com.google.protobuf.CodedInputStream; - -import java.io.IOException; - -/** - * @author: FengYe - * @date: 2024/7/17 下午9:44 - * @description: Codec - */ -@Deprecated -public interface ProtobufCodec { - byte[] encode(T t) throws IOException; - - T decode(byte[] bytes) throws IOException; - - int size(T t) throws IOException; - - T readFrom(CodedInputStream intput) throws IOException; -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufField.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufField.java deleted file mode 100644 index 96b36dfa2f..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufField.java +++ /dev/null @@ -1,286 +0,0 @@ -package com.taobao.arthas.grpc.server.protobuf; - -import java.lang.reflect.Field; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.WildcardType; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * @author: FengYe - * @date: 2024/7/25 上午12:14 - * @description: ProtobufField - */ -@Deprecated -public class ProtobufField { - - /** - * 序号 - */ - private int order; - - /** - * protobuf 字段类型 - */ - private ProtobufFieldTypeEnum protobufFieldType; - - /** - * java 字段类型 - */ - private Field javaField; - - /** - * 是否为 List - */ - private boolean isList; - - /** - * 是否为 Map - */ - private boolean isMap; - - /** - * List & Map key 的泛型类型 - */ - private Class genericKeyType; - - /** - * Map value 的泛型类型 - */ - private Class genericValueType; - - /** - * 是否是通配符类型 - */ - private boolean wildcardType; - - private boolean packed; - - /** - * 处理 List 和 Map 类型字段 - * - * @param field - */ - public void parseListOrMap(Field field) { - Class cls = field.getType(); - boolean needCheckGenericType = false; - if (List.class.isAssignableFrom(cls) || Set.class.isAssignableFrom(cls)) { - isList = true; - needCheckGenericType = true; - } - if (Map.class.isAssignableFrom(cls)) { - isMap = true; - needCheckGenericType = true; - } - - if (!needCheckGenericType) { - return; - } - - Type type = field.getGenericType(); - if (type instanceof ParameterizedType) { - ParameterizedType ptype = (ParameterizedType) type; - - Type[] actualTypeArguments = ptype.getActualTypeArguments(); - - if (actualTypeArguments != null) { - - int length = actualTypeArguments.length; - if (isList) { - if (length != 1) { - throw new RuntimeException( - "List must use generic definiation like List, please check field name '" - + field.getName() + " at class " + field.getDeclaringClass().getName()); - } - } else if (isMap) { - if (length != 2) { - throw new RuntimeException( - "Map must use generic definiation like Map, please check field name '" - + field.getName() + " at class " + field.getDeclaringClass().getName()); - } - } - - Type targetType = actualTypeArguments[0]; - if (targetType instanceof Class) { - genericKeyType = (Class) targetType; - } else if (targetType instanceof ParameterizedType) { - boolean mapKey = false; - if (isMap) { - mapKey = true; - } - throw new RuntimeException(noSubParameterizedType(field, mapKey)); - } else if (WildcardType.class.isAssignableFrom(targetType.getClass())) { - wildcardType = true; - WildcardType wildcardType = (WildcardType) targetType; - - Type[] upperBounds = wildcardType.getUpperBounds(); - if (upperBounds != null && upperBounds.length == 1) { - if (upperBounds[0] instanceof Class) { - genericKeyType = (Class) upperBounds[0]; - } - } - } - - if (actualTypeArguments.length > 1) { - targetType = actualTypeArguments[1]; - if (targetType instanceof Class) { - genericValueType = (Class) targetType; - } else if (targetType instanceof ParameterizedType) { - boolean mapKey = false; - if (isMap) { - mapKey = true; - } - throw new RuntimeException(noSubParameterizedType(field, mapKey)); - } else if (WildcardType.class.isAssignableFrom(targetType.getClass())) { - wildcardType = true; - WildcardType wildcardType = (WildcardType) targetType; - - Type[] upperBounds = wildcardType.getUpperBounds(); - if (upperBounds != null && upperBounds.length == 1) { - if (upperBounds[0] instanceof Class) { - genericValueType = (Class) upperBounds[0]; - } - } - } - } - - } - } - } - - /** - * No sub parameterized type. - * - * @param field the field - * @param listOrMap the list or map - * @return the string - */ - private String noSubParameterizedType(Field field, boolean listOrMap) { - String key = "List"; - if (listOrMap) { - key = "Map"; - } - return key + " can not has sub parameterized type please check field name '" + field.getName() + " at class " - + field.getDeclaringClass().getName(); - - } - - public boolean isEnumValueType() { - if (genericValueType != null) { - return Enum.class.isAssignableFrom(genericValueType); - } - return false; - } - - public boolean isEnumKeyType() { - if (genericKeyType != null) { - return Enum.class.isAssignableFrom(genericKeyType); - } - return false; - } - - public static boolean isListType(Field field) { - return List.class.isAssignableFrom(field.getType()); - } - - public static boolean isSetType(Field field) { - return Set.class.isAssignableFrom(field.getType()); - } - - public static boolean isPrimitiveType(Class c) { - if (c.isPrimitive()) { - return true; - } - if (c.getName().equals(String.class.getName())) { - return true; - } - return false; - } - - public int getOrder() { - return order; - } - - public void setOrder(int order) { - this.order = order; - } - - public ProtobufFieldTypeEnum getProtobufFieldType() { - return protobufFieldType; - } - - public void setProtobufFieldType(ProtobufFieldTypeEnum protobufFieldType) { - this.protobufFieldType = protobufFieldType; - } - - public boolean isList() { - return isList; - } - - public void setList(boolean list) { - isList = list; - } - - public boolean isMap() { - return isMap; - } - - public void setMap(boolean map) { - isMap = map; - } - - public Class getGenericKeyType() { - return genericKeyType; - } - - public void setGenericKeyType(Class genericKeyType) { - this.genericKeyType = genericKeyType; - } - - public Class getGenericValueType() { - return genericValueType; - } - - public void setGenericValueType(Class genericValueType) { - this.genericValueType = genericValueType; - } - - public Field getJavaField() { - return javaField; - } - - public void setJavaField(Field javaField) { - this.javaField = javaField; - } - - public boolean isWildcardType() { - return wildcardType; - } - - public void setWildcardType(boolean wildcardType) { - this.wildcardType = wildcardType; - } - - public boolean isPacked() { - return packed; - } - - public void setPacked(boolean packed) { - this.packed = packed; - } - - @Override - public String toString() { - return "ProtobufField{" + - "order=" + order + - ", protobufFieldType=" + protobufFieldType + - ", isList=" + isList + - ", isMap=" + isMap + - ", genericKeyType=" + genericKeyType + - ", genericValueType=" + genericValueType + - ", wildcardType=" + wildcardType + - '}'; - } -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufFieldTypeEnum.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufFieldTypeEnum.java deleted file mode 100644 index f45375b609..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufFieldTypeEnum.java +++ /dev/null @@ -1,177 +0,0 @@ -package com.taobao.arthas.grpc.server.protobuf; - -import com.google.protobuf.WireFormat; - -/** - * @author: FengYe protobuf字段类型映射 - * @date: 2024/7/17 下午10:02 - * @description: ProtoBufFieldType - */ -@Deprecated -public enum ProtobufFieldTypeEnum { - /** - * types defined in .proto file. - */ - DOUBLE ("Double", "double" , "WIRETYPE_FIXED64", ".doubleValue()", WireFormat.FieldType.DOUBLE ,"0d"), - FLOAT ("Float", "float", "WIRETYPE_FIXED32" , ".floatValue()", WireFormat.FieldType.FLOAT ,"0f"), - INT64 ("Long", "int64" , "WIRETYPE_VARINT" , ".longValue()", WireFormat.FieldType.INT64 ,"0L" ), - UINT64 ("Long", "uInt64" , "WIRETYPE_VARINT" , ".longValue()", WireFormat.FieldType.UINT64 ,"0L" ), - INT32 ("Integer", "int32" , "WIRETYPE_VARINT" , ".intValue()" , WireFormat.FieldType.INT32 ,"0" ), - FIXED64 ("Long", "fixed64" , "WIRETYPE_FIXED64" , ".longValue()" , WireFormat.FieldType.FIXED64 ,"0L" ), - FIXED32 ("Integer", "fixed32" , "WIRETYPE_FIXED32" , ".intValue()", WireFormat.FieldType.FIXED32 ,"0" ), - BOOL ("Boolean", "bool" , "WIRETYPE_VARINT" , ".booleanValue()", WireFormat.FieldType.BOOL ,"false" ), - STRING ("String", "string" , "WIRETYPE_LENGTH_DELIMITED", "", WireFormat.FieldType.STRING ,"\"\""), - BYTES ("byte[]", "bytes", "WIRETYPE_LENGTH_DELIMITED", "", WireFormat.FieldType.BYTES, "new byte[0]"), - UINT32 ("Integer", "uInt32" , "WIRETYPE_VARINT" , ".intValue()" , WireFormat.FieldType.UINT32 ,"0" ), - SFIXED32("Integer", "sFixed32" , "WIRETYPE_FIXED32" , ".intValue()" , WireFormat.FieldType.SFIXED32 ,"0" ), - SFIXED64("Long", "sFixed64" , "WIRETYPE_FIXED64" , ".longValue()" , WireFormat.FieldType.SFIXED64 ,"0L" ), - SINT32 ("Integer", "sInt32" , "WIRETYPE_VARINT" , ".intValue()" , WireFormat.FieldType.SINT32 ,"0" ), - SINT64 ("Long", "sInt64" , "WIRETYPE_VARINT" , ".longValue()" , WireFormat.FieldType.SINT64 ,"0L" ), - OBJECT ("Object", "object" , "WIRETYPE_LENGTH_DELIMITED" , "" , WireFormat.FieldType.MESSAGE ,null ), - ENUM ("Enum", "enum" , "WIRETYPE_VARINT" , ".ordinal()" , WireFormat.FieldType.ENUM , null ), - MAP ("Map", "map" , "WIRETYPE_VARINT" , "" , WireFormat.FieldType.MESSAGE , null ), - DATE ("Date", "int64" , "WIRETYPE_VARINT" , ".getTime()" , WireFormat.FieldType.INT64 , null ), - BIGDECIMAL("java.math.BigDecimal", "string", "WIRETYPE_LENGTH_DELIMITED", ".toString()", - WireFormat.FieldType.STRING, null), - BIGINTEGER("java.math.BigInteger", "string", "WIRETYPE_LENGTH_DELIMITED", ".toString()", - WireFormat.FieldType.STRING, null), - DEFAULT("", "" , "" , "" , WireFormat.FieldType.MESSAGE , null ); - - /** - * java original type - */ - private final String javaType; - /** - * protobuf type - */ - private final String type; - /** - * protobuf wire format type - */ - private final String wireFormat; - - /** - * to primitive type - */ - private final String toPrimitiveType; - - /** - * internal field type - */ - private WireFormat.FieldType internalFieldType; - - /** - * default value - */ - private String defaultValue; - - /** - * get the defaultValue - * @return the defaultValue - */ - public String getDefaultValue() { - return defaultValue; - } - - /** - * set defaultValue value to defaultValue - * @param defaultValue the defaultValue to set - */ - public void setDefaultValue(String defaultValue) { - this.defaultValue = defaultValue; - } - - /** - * get the internalFieldType - * @return the internalFieldType - */ - public WireFormat.FieldType getInternalFieldType() { - return internalFieldType; - } - - /** - * set internalFieldType value to internalFieldType - * @param internalFieldType the internalFieldType to set - */ - public void setInternalFieldType(WireFormat.FieldType internalFieldType) { - this.internalFieldType = internalFieldType; - } - - /** - * get primitive type in string - * @return primitive type in string - */ - public String getToPrimitiveType() { - return toPrimitiveType; - } - - /** - * get protobuf wire format type - * @return protobuf wire format type - */ - public String getWireFormat() { - return wireFormat; - } - - /** - * get protobuf type - * @return protobuf type - */ - public String getType() { - return type; - } - - /** - * get java original type - * @return java original type - */ - public String getJavaType() { - if (this == ProtobufFieldTypeEnum.ENUM) { - return Enum.class.getName(); - } - return javaType; - } - - /** - * Constructor method - * - * @param javaType java original type - * @param type protobuf type - * @param wireFormat protobuf wire format type - */ - ProtobufFieldTypeEnum(String javaType, String type, String wireFormat, - String toPrimitiveType, WireFormat.FieldType internalFieldType, String defaultValue) { - this.javaType = javaType; - this.type = type; - this.wireFormat = wireFormat; - this.toPrimitiveType = toPrimitiveType; - this.internalFieldType = internalFieldType; - this.defaultValue = defaultValue; - } - - /** - * Checks if is primitive. - * - * @return true, if is primitive - */ - public boolean isPrimitive() { - if (this == INT32 || this == DOUBLE || this == FIXED32 - || this == FIXED64 || this == FLOAT - || this == INT64 || this == SFIXED32 - || this == SFIXED64 || this == SINT32 - || this == SINT64 || this == BOOL || this == ProtobufFieldTypeEnum.UINT32 || this == ProtobufFieldTypeEnum.UINT64) { - return true; - } - - return false; - } - - /** - * Checks if is enum. - * - * @return true, if is enum - */ - public boolean isEnum() { - return this == ENUM; - } -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java deleted file mode 100644 index ba2d8a9571..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/ProtobufProxy.java +++ /dev/null @@ -1,363 +0,0 @@ -package com.taobao.arthas.grpc.server.protobuf; - -import com.google.protobuf.WireFormat; -import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufEnableZigZap; -import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; -import com.taobao.arthas.grpc.server.protobuf.utils.ProtoBufUtil; -import com.taobao.arthas.grpc.server.protobuf.utils.MiniTemplator; -import com.taobao.arthas.grpc.server.protobuf.utils.ProtoBufClassCompiler; - -import java.lang.reflect.InvocationTargetException; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author: FengYe - * @date: 2024/7/17 下午9:57 - * @description: ProtoBufProxy - */ -@Deprecated -public class ProtobufProxy { - private static final String TEMPLATE_FILE = "/class_template.tpl"; - - private static final Map codecCache = new ConcurrentHashMap<>(); - - private static Class clazz; - - private static MiniTemplator miniTemplator; - - private static List protobufFields; - - public static ProtobufCodec getCodecCacheSide(Class clazz) { - ProtobufCodec codec = codecCache.get(clazz.getName()); - if (codec != null) { - return codec; - } - try { - codec = create(clazz); - codecCache.put(clazz.getName(), codec); - return codec; - } catch (Exception exception) { - exception.printStackTrace(); - return null; - } - } - - public static ProtobufCodec create(Class clazz) { - Objects.requireNonNull(clazz); - if (clazz.getAnnotation(ProtobufClass.class) == null) { - throw new IllegalArgumentException(clazz + "class is not annotated with @ProtobufClass"); - } - ProtobufProxy.clazz = clazz; - loadProtobufField(); - - String path = Objects.requireNonNull(clazz.getResource(TEMPLATE_FILE)).getPath(); - try { - miniTemplator = new MiniTemplator(path); - } catch (Exception e) { - throw new RuntimeException("miniTemplator init failed. " + path, e); - } - - miniTemplator.setVariable("package", "package " + clazz.getPackage().getName() + ";"); - - processImportBlock(); - - miniTemplator.setVariable("className", ProtoBufUtil.getClassName(clazz) + "$$ProxyClass"); - miniTemplator.setVariable("codecClassName", ProtobufCodec.class.getName()); - miniTemplator.setVariable("targetProxyClassName", clazz.getCanonicalName()); - processEncodeBlock(); - processDecodeBlock(); - - String code = miniTemplator.generateOutput(); - - ProtoBufClassCompiler protoBufClassCompiler = new ProtoBufClassCompiler(ProtoBufClassCompiler.class.getClassLoader()); - String fullClassName = ProtoBufUtil.getFullClassName(clazz) + "$$ProxyClass"; - Class newClass = protoBufClassCompiler.compile(fullClassName, code, clazz.getClassLoader()); - - try { - ProtobufCodec newInstance = (ProtobufCodec) newClass.getDeclaredConstructor(new Class[0]).newInstance(new Object[0]); - return newInstance; - } catch (InstantiationException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - private static void processImportBlock() { - Set imports = new HashSet<>(); - imports.add("java.util.*"); - imports.add("java.io.IOException"); - imports.add("java.lang.reflect.*"); - imports.add("com.taobao.arthas.grpc.server.protobuf.*"); - imports.add("com.taobao.arthas.grpc.server.protobuf.utils.*"); - imports.add("com.taobao.arthas.grpc.server.protobuf.annotation.*"); - imports.add("com.google.protobuf.*"); - imports.add(clazz.getCanonicalName()); - for (String pkg : imports) { - miniTemplator.setVariable("importBlock", pkg); - miniTemplator.addBlock("imports"); - } - } - - private static void processEncodeBlock() { - for (ProtobufField protobufField : protobufFields) { - String dynamicFieldGetter = ProtoBufUtil.getGetterDynamicString(protobufField, clazz); - String dynamicFieldType = protobufField.isList() ? "Collection" : protobufField.getProtobufFieldType().getJavaType(); - String dynamicFieldName = ProtoBufUtil.getDynamicFieldName(protobufField.getOrder()); - - miniTemplator.setVariable("dynamicFieldType", dynamicFieldType); - miniTemplator.setVariable("dynamicFieldName", dynamicFieldName); - miniTemplator.setVariable("dynamicFieldGetter", dynamicFieldGetter); - String sizeDynamicString = ProtoBufUtil.getSizeDynamicString(protobufField); - miniTemplator.setVariable("sizeDynamicString", sizeDynamicString); - miniTemplator.setVariable("encodeWriteFieldValue", ProtoBufUtil.getWriteByteDynamicString(protobufField)); - miniTemplator.addBlock("encodeFields"); - } - } - - private static void processDecodeBlock() { - // 初始化 list、map、enum - StringBuilder initListMapFields = new StringBuilder(); - for (ProtobufField protobufField : protobufFields) { - boolean isList = protobufField.isList(); - boolean isMap = protobufField.isMap(); - String express = ""; - if (isList) { - if (ProtobufField.isListType(protobufField.getJavaField())) { - express = "new ArrayList()"; - } else if (ProtobufField.isSetType(protobufField.getJavaField())) { - express = "new HashSet()"; - } - } else if (isMap) { - express = "new HashMap()"; - } - if (isList || isMap) { - initListMapFields.append(ProtoBufUtil.getInitListMapFieldDynamicString(protobufField, express)); - } - - if (protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.ENUM) { - String clsName = protobufField.getJavaField().getType().getCanonicalName(); - if (!isList) { - express = "ProtoBufUtil.getEnumValue(" + clsName + ".class, " + clsName + ".values()[0].name())"; - // add set get method - String setToField = ProtoBufUtil.getSetFieldDynamicString(protobufField, clazz, express); - miniTemplator.setVariable("enumInitialize", setToField); - miniTemplator.addBlock("enumFields"); - } - } - } - miniTemplator.setVariable("initListMapFields", initListMapFields.toString()); - - //处理字段赋值 - StringBuilder code = new StringBuilder(); - // 处理field解析 - for (ProtobufField protobufField : protobufFields) { - boolean isList = protobufField.isList(); - String t = protobufField.getProtobufFieldType().getType(); - t = ProtoBufUtil.capitalize(t); - - boolean listTypeCheck = false; - String express; - String objectDecodeExpress = ""; - String objectDecodeExpressSuffix = ""; - - String decodeOrder = "-1"; - if (protobufField.getProtobufFieldType() != ProtobufFieldTypeEnum.DEFAULT) { - decodeOrder = ProtoBufUtil.makeTag(protobufField.getOrder(), - protobufField.getProtobufFieldType().getInternalFieldType().getWireType()) + ""; - } else { - decodeOrder = "ProtoBufUtil.makeTag(" + protobufField.getOrder() + ",WireFormat." - + protobufField.getProtobufFieldType().getWireFormat() + ")"; - } - miniTemplator.setVariable("decodeOrder", decodeOrder); - - // enumeration type - if (protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.ENUM) { - String clsName = protobufField.getJavaField().getType().getCanonicalName(); - if (isList) { - if (protobufField.getGenericKeyType() != null) { - Class cls = protobufField.getGenericKeyType(); - clsName = cls.getCanonicalName(); - } - } - express = "ProtoBufUtil.getEnumValue(" + clsName + ".class, ProtoBufUtil.getEnumName(" + clsName - + ".values()," + "input.read" + t + "()))"; - } else { - // here is the trick way to process BigDecimal and BigInteger - if (protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.BIGDECIMAL || protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.BIGINTEGER) { - express = "new " + protobufField.getProtobufFieldType().getJavaType() + "(input.read" + t + "())"; - } else { - express = "input.read" + t + "()"; - } - - } - - // if List type and element is object message type - if (isList && protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.OBJECT) { - if (protobufField.getGenericKeyType() != null) { - Class cls = protobufField.getGenericKeyType(); - - checkObjectType(protobufField, cls); - - code.append("codec = ProtobufProxy.create(").append(cls.getCanonicalName()).append(".class"); - code.append(")").append(ProtoBufUtil.JAVA_LINE_BREAK); - objectDecodeExpress = code.toString(); - code.setLength(0); - - objectDecodeExpress += "int length = input.readRawVarint32()" + ProtoBufUtil.JAVA_LINE_BREAK; - objectDecodeExpress += "final int oldLimit = input.pushLimit(length)" + ProtoBufUtil.JAVA_LINE_BREAK; - listTypeCheck = true; - express = "(" + cls.getCanonicalName() + ") codec.readFrom(input)"; - - } - } else if (protobufField.isMap()) { - - String getMapCommand = getMapCommand(protobufField); - - if (protobufField.isEnumKeyType()) { - String enumClassName = protobufField.getGenericKeyType().getCanonicalName(); - code.append("EnumHandler<").append(enumClassName).append("> keyhandler"); - code.append("= new EnumHandler"); - code.append("<").append(enumClassName).append(">() {"); - code.append(ProtoBufUtil.LINE_BREAK); - code.append("public ").append(enumClassName).append(" handle(int value) {"); - code.append(ProtoBufUtil.LINE_BREAK); - code.append("String enumName = ProtoBufUtil.getEnumName(").append(enumClassName) - .append(".values(), value)"); - code.append(ProtoBufUtil.JAVA_LINE_BREAK); - code.append("return ").append(enumClassName).append(".valueOf(enumName)"); - code.append(ProtoBufUtil.JAVA_LINE_BREAK); - code.append("}}"); - code.append(ProtoBufUtil.JAVA_LINE_BREAK); - } - - if (protobufField.isEnumValueType()) { - String enumClassName = protobufField.getGenericValueType().getCanonicalName(); - code.append("EnumHandler<").append(enumClassName).append("> handler"); - code.append("= new EnumHandler"); - code.append("<").append(enumClassName).append(">() {"); - code.append(ProtoBufUtil.LINE_BREAK); - code.append("public ").append(enumClassName).append(" handle(int value) {"); - code.append(ProtoBufUtil.LINE_BREAK); - code.append("String enumName = ProtoBufUtil.getEnumName(").append(enumClassName) - .append(".values(), value)"); - code.append(ProtoBufUtil.JAVA_LINE_BREAK); - code.append("return ").append(enumClassName).append(".valueOf(enumName)"); - code.append(ProtoBufUtil.JAVA_LINE_BREAK); - code.append("}}"); - code.append(ProtoBufUtil.JAVA_LINE_BREAK); - } - - objectDecodeExpress = code.toString(); - code.setLength(0); - - express = "ProtoBufUtil.putMapValue(input, " + getMapCommand + ","; - express += ProtoBufUtil.getMapFieldGenericParameterString(protobufField); - if (protobufField.isEnumKeyType()) { - express += ", keyhandler"; - } else { - express += ", null"; - } - if (protobufField.isEnumValueType()) { - express += ", handler"; - } else { - express += ", null"; - } - express += ")"; - - } else if (protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.OBJECT) { // if object - // message - // type - Class cls = protobufField.getJavaField().getType(); - checkObjectType(protobufField, cls); - String name = cls.getCanonicalName(); // need - // to - // parse - // nested - // class - code.append("codec = ProtobufProxy.create(").append(name).append(".class"); - - code.append(")").append(ProtoBufUtil.JAVA_LINE_BREAK); - objectDecodeExpress = code.toString(); - code.setLength(0); - - objectDecodeExpress += "int length = input.readRawVarint32()" + ProtoBufUtil.JAVA_LINE_BREAK; - objectDecodeExpress += "final int oldLimit = input.pushLimit(length)" + ProtoBufUtil.JAVA_LINE_BREAK; - - listTypeCheck = true; - express = "(" + name + ") codec.readFrom(input)"; - } - - if (protobufField.getProtobufFieldType() == ProtobufFieldTypeEnum.BYTES) { - express += ".toByteArray()"; - } - - String decodeFieldSetValue = ProtoBufUtil.getSetFieldDynamicString(protobufField, clazz, express) + ProtoBufUtil.JAVA_LINE_BREAK; - - if (listTypeCheck) { - objectDecodeExpressSuffix += "input.checkLastTagWas(0)" + ProtoBufUtil.JAVA_LINE_BREAK; - objectDecodeExpressSuffix += "input.popLimit(oldLimit)" + ProtoBufUtil.JAVA_LINE_BREAK; - } - - String objectPackedDecodeExpress = ""; - // read packed type - if (isList) { - ProtobufFieldTypeEnum protobufFieldType = protobufField.getProtobufFieldType(); - if (protobufFieldType.isPrimitive() || protobufFieldType.isEnum()) { - code.append("if (tag == ") - .append(ProtoBufUtil.makeTag(protobufField.getOrder(), WireFormat.WIRETYPE_LENGTH_DELIMITED)); - code.append(") {").append(ProtoBufUtil.LINE_BREAK); - - code.append("int length = input.readRawVarint32()").append(ProtoBufUtil.JAVA_LINE_BREAK); - code.append("int limit = input.pushLimit(length)").append(ProtoBufUtil.JAVA_LINE_BREAK); - - code.append(ProtoBufUtil.getSetFieldDynamicString(protobufField, clazz, express)); - - code.append("input.popLimit(limit)").append(ProtoBufUtil.JAVA_LINE_BREAK); - - code.append("continue").append(ProtoBufUtil.JAVA_LINE_BREAK); - code.append("}").append(ProtoBufUtil.LINE_BREAK); - - objectPackedDecodeExpress = code.toString(); - } - } - miniTemplator.setVariable("objectPackedDecodeExpress", objectPackedDecodeExpress); - miniTemplator.setVariable("objectDecodeExpress", objectDecodeExpress); - miniTemplator.setVariable("objectDecodeExpressSuffix", objectDecodeExpressSuffix); - miniTemplator.setVariable("decodeFieldSetValue", decodeFieldSetValue); - miniTemplator.addBlock("decodeFields"); - } - - } - - private static void checkObjectType(ProtobufField protobufField, Class cls) { - if (ProtobufField.isPrimitiveType(cls)) { - throw new RuntimeException("invalid generic type for List as Object type, current type is '" + cls.getName() - + "' on field name '" + protobufField.getJavaField().getDeclaringClass().getName() + "#" - + protobufField.getJavaField().getName()); - } - } - - private static String getMapCommand(ProtobufField protobufField) { - String keyGeneric; - keyGeneric = protobufField.getGenericKeyType().getCanonicalName(); - - String valueGeneric; - valueGeneric = protobufField.getGenericValueType().getCanonicalName(); - String getMapCommand = "(Map<" + keyGeneric; - getMapCommand = getMapCommand + ", " + valueGeneric + ">)"; - getMapCommand = getMapCommand + ProtoBufUtil.getGetterDynamicString(protobufField, clazz); - return getMapCommand; - } - - private static void loadProtobufField() { - protobufFields = ProtoBufUtil.getProtobufFieldList(clazz, - clazz.getAnnotation(ProtobufEnableZigZap.class) != null - ); - } -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufClass.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufClass.java deleted file mode 100644 index 6779ae63fc..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufClass.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.taobao.arthas.grpc.server.protobuf.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author: FengYe - * @date: 2024/7/25 上午12:19 - * @description: ProtobufClass 用于标识 protobuf class - */ -@Target({ ElementType.TYPE }) -@Retention(RetentionPolicy.RUNTIME) -@Deprecated -public @interface ProtobufClass { -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufCustomizedField.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufCustomizedField.java deleted file mode 100644 index 0d9fceefa5..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufCustomizedField.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.taobao.arthas.grpc.server.protobuf.annotation; - -import com.taobao.arthas.grpc.server.protobuf.ProtobufFieldTypeEnum; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author: FengYe - * @date: 2024/7/25 上午12:21 - * @description: ProtobufField 用于自定义标识字段 - */ -@Target({ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -@Deprecated -public @interface ProtobufCustomizedField { - int order() default 0; - - ProtobufFieldTypeEnum protoBufFieldType() default ProtobufFieldTypeEnum.DEFAULT; -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufEnableZigZap.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufEnableZigZap.java deleted file mode 100644 index 7912c2fed4..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufEnableZigZap.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.taobao.arthas.grpc.server.protobuf.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author: FengYe - * @date: 2024/7/28 下午7:27 - * @description: EnableZigZap 是否启用 zigzap 编码 - */ -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Deprecated -public @interface ProtobufEnableZigZap { -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufIgnore.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufIgnore.java deleted file mode 100644 index 9dabb0394d..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufIgnore.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.taobao.arthas.grpc.server.protobuf.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author: FengYe - * @date: 2024/7/25 上午12:44 - * @description: ProtobufIgnore - */ -@Target({ElementType.TYPE, ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -@Deprecated -public @interface ProtobufIgnore { -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufPacked.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufPacked.java deleted file mode 100644 index 5a3c8113da..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/annotation/ProtobufPacked.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.taobao.arthas.grpc.server.protobuf.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author: FengYe - * @date: 2024/7/30 上午2:01 - * @description: Packed 是否将 List 打包。对于基础类型的 List,打包可以减少无意义的 tag,提高压缩率 - * - */ -@Target({ ElementType.FIELD }) -@Retention(RetentionPolicy.RUNTIME) -@Deprecated -public @interface ProtobufPacked { -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/CodedOutputStreamCache.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/CodedOutputStreamCache.java deleted file mode 100644 index 8c7709b01b..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/CodedOutputStreamCache.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.taobao.arthas.grpc.server.protobuf.utils;/** - * @author: 風楪 - * @date: 2024/8/3 下午7:20 - */ - -import com.google.protobuf.CodedOutputStream; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Stack; - -/** - * @author: FengYe - * @date: 2024/8/3 下午7:20 - * @description: 针对 CodedOutputStream 的缓存处理,避免创建大量 CodedOutputStream;使用 threadLocal 中的 stack 来缓存对象 - */ -public class CodedOutputStreamCache { - private static final ThreadLocal> instanceGetter = ThreadLocal.withInitial(Stack::new); - private final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - private final CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(byteArrayOutputStream, 0); - - // 每个线程 stack 中最多存储的 buffer 数量 - private static final int MAX_ELEMENT = 5; - - public static CodedOutputStreamCache get() { - Stack stack = instanceGetter.get(); - if (!stack.isEmpty()) { - return stack.pop(); - } - return new CodedOutputStreamCache(); - } - - public byte[] getData() throws IOException { - this.codedOutputStream.flush(); - byte[] bytes = this.byteArrayOutputStream.toByteArray(); - this.recycle(); - return bytes; - } - - public CodedOutputStream getCodedOutputStream() { - return codedOutputStream; - } - - private void recycle(){ - this.byteArrayOutputStream.reset(); - Stack stack = instanceGetter.get(); - if (stack.size() >= MAX_ELEMENT) { - return; - } - stack.push(this); - } -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/EnumHandler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/EnumHandler.java deleted file mode 100644 index f2f1ab9db2..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/EnumHandler.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.taobao.arthas.grpc.server.protobuf.utils;/** - * @author: 風楪 - * @date: 2024/8/6 上午1:19 - */ - -/** - * @author: FengYe - * @date: 2024/8/6 上午1:19 - * @description: EnumHandler 处理 enum 泛型类型 - */ -public interface EnumHandler { - V handle(int value); -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/MiniTemplator.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/MiniTemplator.java deleted file mode 100644 index 4fe073fda0..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/MiniTemplator.java +++ /dev/null @@ -1,1305 +0,0 @@ -// Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland -// www.source-code.biz, www.inventec.ch/chdh -// -// This module is multi-licensed and may be used under the terms -// of any of the following licenses: -// -// EPL, Eclipse Public License, http://www.eclipse.org/legal -// LGPL, GNU Lesser General Public License, http://www.gnu.org/licenses/lgpl.html -// -// Please contact the author if you need another license. -// This module is provided "as is", without warranties of any kind. - -package com.taobao.arthas.grpc.server.protobuf.utils;; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStreamReader; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.io.Writer; -import java.nio.charset.Charset; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * A compact template engine for HTML files. - * - *

- * Template syntax:
- *

- *    Variables:
- *       ${VariableName}
- *
- *    Blocks:
- *       <!-- $beginBlock blockName -->
- *         ... block contents ...
- *       <!-- $endBlock blockName -->
- *
- *    Conditional blocks:
- *       <!-- $if flag1 flag2 -->
- *         ... included if flag1 or flag2 is set ...
- *       <!-- $elseIf !flag3 flag4 -->
- *         ... included if flag3 is not set or flag4 is set ...
- *       <!-- $else -->
- *         ... included if none of the above conditions is met ...
- *       <!-- $endIf -->
- *
- *    Short form of conditional blocks:
- *    (only recognized if {@link TemplateSpecification#shortFormEnabled TemplateSpecification.shortFormEnabled} is true)
- *       <$? flag1 flag2 >
- *         ... included if flag1 or flag2 is set ...
- *       <$: !flag3 flag4 >
- *         ... included if flag3 is not set or flag4 is set ...
- *       <$:>
- *         ... included if none of the above conditions is met ...
- *       <$/?>
- *    Example:
- *       <$?de> Hallo Welt!
- *       <$:fr> Bonjour tout le monde!
- *       <$:  > Hello world!
- *       <$/?>
- *
- *    Include a subtemplate:
- *       <!-- $include relativeFileName -->
- * - *

- * General remarks:

- *
    - *
  • Variable names, block names, condition flags and commands (e.g. "$beginBlock") are case-insensitive.
  • - *
  • The same variable may be used multiple times within a template.
  • - *
  • Multiple blocks with the same name may occur within a template.
  • - *
  • Blocks can be nested.
  • - *
  • Conditional blocks ($if) and includes ($include) are resolved when the template is parsed. - * Parsing is done within the MiniTemplator constructor. - * Condition flags can be passed to the constructor using {@link TemplateSpecification}. - *
  • Normal blocks ($beginBlock) must be added (and can be repeated) by the application program using addBlock(). - *
  • The {@link MiniTemplatorCache} class may be used to cache MiniTemplator objects with parsed templates.
  • - *
- * - *

- * Project home page: www.source-code.biz/MiniTemplator
- * Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland - */ -public class MiniTemplator { - -//--- exceptions ----------------------------------------------------- - - /** - * Thrown when a syntax error is encountered within the template. - */ - public static class TemplateSyntaxException extends RuntimeException { - private static final long serialVersionUID = 1; - public TemplateSyntaxException (String msg) { - super("Syntax error in template: " + msg); }} - - /** - * Thrown when {@link MiniTemplator#setVariable(String, String, boolean) Minitemplator.setVariable} - * is called with a variableName that is not defined - * within the template and the isOptional parameter is false. - */ - public static class VariableNotDefinedException extends RuntimeException { - private static final long serialVersionUID = 1; - public VariableNotDefinedException (String variableName) { - super("Variable \"" + variableName + "\" not defined in template."); }} - - /** - * Thrown when {@link MiniTemplator#addBlock Minitemplator.addBlock} - * is called with a blockName that is not defined - * within the template. - */ - public static class BlockNotDefinedException extends RuntimeException { - private static final long serialVersionUID = 1; - public BlockNotDefinedException (String blockName) { - super("Block \"" + blockName + "\" not defined in template."); }} - -//--- public nested classes ------------------------------------------ - - /** - * Specifies the parameters for constructing a {@link MiniTemplator} object. - */ - public static class TemplateSpecification { // template specification - - /** - * The file name of the template file. - */ - public String templateFileName; - - /** - * The path of the base directory for reading subtemplate files. - * This path is used to convert the relative paths of subtemplate files (specified with the $include commands) - * into absolute paths. - * If this field is null, the parent directory of the main template file (specified by templateFileName) is used. - */ - public String subtemplateBasePath; - - /** - * The character set to be used for reading and writing files. - * This charset is used for reading the template and subtemplate files and for - * writing output with {@link #generateOutput(String outputFileName)}. - * If this field is null, the default charset of the Java VM is used. - */ - public Charset charset; - - /** - * The contents of the template file. - * This field may be used instead of templateFileName to pass the template text in memory. - * If this field is not null, templateFileName will be ignored. - */ - public String templateText; - - /** - * Flags for the conditional commands ($if, $elseIf). - * A set of flag names, that can be used with the $if and $elseIf commands. - * The flag names are case-insensitive. - */ - public Set conditionFlags; - - /** - * Enables the short form syntax for conditional blocks. - */ - public boolean shortFormEnabled; } - -//--- private nested classes ----------------------------------------- - - private static class BlockDynTabRec { // block dynamic data table record structure - int instances; // number of instances of this block - int firstBlockInstNo; // block instance no of first instance of this block or -1 - int lastBlockInstNo; // block instance no of last instance of this block or -1 - int currBlockInstNo; } // current block instance no, used during generation of output file - private static class BlockInstTabRec { // block instance table record structure - int blockNo; // block number - int instanceLevel; // instance level of this block - // InstanceLevel is an instance counter per block. - // (In contrast to blockInstNo, which is an instance counter over the instances of all blocks) - int parentInstLevel; // instance level of parent block - int nextBlockInstNo; // pointer to next instance of this block or -1 - // Forward chain for instances of same block. - String[] blockVarTab; } // block instance variables - -//--- private variables ---------------------------------------------- - - private MiniTemplatorParser mtp; // contains the parsed template - private Charset charset; // charset used for reading and writing files - private String subtemplateBasePath; // base path for relative file names of subtemplates, may be null - - private String[] varValuesTab; // variable values table, entries may be null - - private BlockDynTabRec[] blockDynTab; // dynamic block-specific values - private BlockInstTabRec[] blockInstTab; // block instances table - // This table contains an entry for each block instance that has been added. - // Indexed by BlockInstNo. - private int blockInstTabCnt; // no of entries used in BlockInstTab - -//--- constructors --------------------------------------------------- - - /** - * Constructs a MiniTemplator object. - *

During construction, the template and subtemplate files are read and parsed. - *

Note: The {@link MiniTemplatorCache} class may be used to cache MiniTemplator objects. - * @param templateSpec the template specification. - * @throws TemplateSyntaxException when a syntax error is detected within the template. - * @throws IOException when an i/o error occurs while reading the template. - */ - public MiniTemplator (TemplateSpecification templateSpec) - throws IOException, TemplateSyntaxException { - init(templateSpec); } - - /** - * Constructs a MiniTemplator object by specifying only the file name. - *

This is a convenience constructor that may be used when only the file name has to be specified. - * @param templateFileName the file name of the template file. - * @throws TemplateSyntaxException when a syntax error is detected within the template. - * @throws IOException when an i/o error occurs while reading the template. - * @see #MiniTemplator(TemplateSpecification) - */ - public MiniTemplator (String templateFileName) - throws IOException, TemplateSyntaxException { - TemplateSpecification templateSpec = new TemplateSpecification(); - templateSpec.templateFileName = templateFileName; - init(templateSpec); } - - private void init (TemplateSpecification templateSpec) - throws IOException, TemplateSyntaxException { - charset = templateSpec.charset; - if (charset == null) { - charset = Charset.defaultCharset(); } - subtemplateBasePath = templateSpec.subtemplateBasePath; - if (subtemplateBasePath == null && templateSpec.templateFileName != null) { - subtemplateBasePath = new File(templateSpec.templateFileName).getParent(); } - String templateText = templateSpec.templateText; - if (templateText == null && templateSpec.templateFileName != null) { - templateText = readFileIntoString(templateSpec.templateFileName); } - if (templateText == null) { - throw new IllegalArgumentException("No templateFileName or templateText specified."); } - mtp = new MiniTemplatorParser(templateText, templateSpec.conditionFlags, templateSpec.shortFormEnabled, this); - reset(); } - - /** - * Dummy constructor, used internally in newInstance(). - */ - protected MiniTemplator() {} - - /** - * Allocates a new uninitialized MiniTemplator object. - * This method is intended to be overridden in a derived class. - * It is called from cloneReset() to create a new MiniTemplator object. - */ - protected MiniTemplator newInstance() { - return new MiniTemplator(); } - -//--- loadSubtemplate ------------------------------------------------ - - /** - * Loads the template string of a subtemplate (used for the $Include command). - * This method can be overridden in a subclass, to load subtemplates from - * somewhere else, e.g. from a database. - *

This implementation of the method interprets subtemplateName - * as a relative file path name and reads the template string from that file. - * {@link MiniTemplator.TemplateSpecification#subtemplateBasePath} is used to convert - * the relative path of the subtemplate into an absolute path. - * @param subtemplateName the name of the subtemplate. - * Normally a relative file path. - * This is the argument string that was specified with the "$Include" command. - * If the string has quotes, the quotes are removed before this method is called. - * @return the template text string of the subtemplate. - */ - protected String loadSubtemplate (String subtemplateName) throws IOException { - String fileName = new File(subtemplateBasePath, subtemplateName).getPath(); - return readFileIntoString(fileName); } - -//--- build up (template variables and blocks) ------------------------ - - /** - * Resets the MiniTemplator object to the initial state. - * All variable values are cleared and all added block instances are deleted. - * This method can be used to produce another HTML page with the same - * template. It is faster than creating another MiniTemplator object, - * because the template does not have to be read and parsed again. - */ - public void reset() { - if (varValuesTab == null) { - varValuesTab = new String[mtp.varTabCnt]; } - else { - for (int varNo=0; varNoThis method is used by the {@link MiniTemplatorCache} class to - * clone the cached MiniTemplator objects. - */ - public MiniTemplator cloneReset() { - MiniTemplator m = newInstance(); - m.mtp = mtp; // the MiniTemplatorParser object is shared among the clones - m.charset = charset; - // (subtemplateBasePath does not have to be copied, because the subtemplates have already been read) - m.reset(); - return m; } - - /** - * Sets a template variable. - *

For variables that are used in blocks, the variable value - * must be set before addBlock() is called. - * @param variableName the name of the variable to be set. Case-insensitive. - * @param variableValue the new value of the variable. May be null. - * @param isOptional specifies whether an exception should be thrown when the - * variable does not exist in the template. If isOptional is - * false and the variable does not exist, an exception is thrown. - * @throws VariableNotDefinedException when no variable with the - * specified name exists in the template and isOptional is false. - */ - public void setVariable (String variableName, String variableValue, boolean isOptional) - throws VariableNotDefinedException { - int varNo = mtp.lookupVariableName(variableName); - if (varNo == -1) { - if (isOptional) { - return; } - throw new VariableNotDefinedException(variableName); } - varValuesTab[varNo] = variableValue; } - - /** - * Sets a template variable. - *

Convenience method for: setVariable (variableName, variableValue, false) - * @param variableName the name of the variable to be set. Case-insensitive. - * @param variableValue the new value of the variable. May be null. - * @throws VariableNotDefinedException when no variable with the - * specified name exists in the template. - * @see #setVariable(String, String, boolean) - */ - public void setVariable (String variableName, String variableValue) - throws VariableNotDefinedException { - setVariable(variableName, variableValue, false); } - - /** - * Sets a template variable to an integer value. - *

Convenience method for: setVariable (variableName, Integer.toString(variableValue)) - * @param variableName the name of the variable to be set. Case-insensitive. - * @param variableValue the new value of the variable. - * @throws VariableNotDefinedException when no variable with the - * specified name exists in the template. - */ - public void setVariable (String variableName, int variableValue) - throws VariableNotDefinedException { - setVariable(variableName, Integer.toString(variableValue)); } - - /** - * Sets an optional template variable. - *

Convenience method for: setVariable (variableName, variableValue, true) - * @param variableName the name of the variable to be set. Case-insensitive. - * @param variableValue the new value of the variable. May be null. - * @see #setVariable(String, String, boolean) - */ - public void setVariableOpt (String variableName, String variableValue) { - setVariable(variableName, variableValue, true); } - - /** - * Sets an optional template variable to an integer value. - *

Convenience method for: setVariableOpt (variableName, Integer.toString(variableValue)) - * @param variableName the name of the variable to be set. Case-insensitive. - * @param variableValue the new value of the variable. - */ - public void setVariableOpt (String variableName, int variableValue) { - // We want to avoid the integer to string conversion if the template variable does not exist. - int varNo = mtp.lookupVariableName(variableName); - if (varNo == -1) { - return; } - varValuesTab[varNo] = Integer.toString(variableValue); } - - /** - * Sets a template variable to an escaped value. - *

Convenience method for: setVariable (variableName, MiniTemplator.escapeHtml(variableValue), isOptional) - * @param variableName the name of the variable to be set. - * @param variableValue the new value of the variable. May be null. - * Special HTML/XML characters are escaped. - * @param isOptional specifies whether an exception should be thrown when the - * variable does not exist in the template. If isOptional is - * false and the variable does not exist, an exception is thrown. - * @throws VariableNotDefinedException when no variable with the - * specified name exists in the template and isOptional is false. - * @see #setVariable(String, String, boolean) - * @see #escapeHtml(String) - */ - public void setVariableEsc (String variableName, String variableValue, boolean isOptional) - throws VariableNotDefinedException { - setVariable(variableName, escapeHtml(variableValue), isOptional); } - - /** - * Sets a template variable to an escaped value. - *

Convenience method for: setVariable (variableName, MiniTemplator.escapeHtml(variableValue), false) - * @param variableName the name of the variable to be set. Case-insensitive. - * @param variableValue the new value of the variable. May be null. - * Special HTML/XML characters are escaped. - * @throws VariableNotDefinedException when no variable with the - * specified name exists in the template. - * @see #setVariable(String, String, boolean) - * @see #escapeHtml(String) - */ - public void setVariableEsc (String variableName, String variableValue) - throws VariableNotDefinedException { - setVariable(variableName, escapeHtml(variableValue), false); } - - /** - * Sets an optional template variable to an escaped value. - *

Convenience method for: setVariable (variableName, MiniTemplator.escapeHtml(variableValue), true) - * @param variableName the name of the variable to be set. Case-insensitive. - * @param variableValue the new value of the variable. May be null. - * Special HTML/XML characters are escaped. - * @see #setVariable(String, String, boolean) - * @see #escapeHtml(String) - */ - public void setVariableOptEsc (String variableName, String variableValue) { - setVariable(variableName, escapeHtml(variableValue), true); } - - /** - * Checks whether a variable with the specified name exists within the template. - * @param variableName the name of the variable. Case-insensitive. - * @return true if the variable exists.
- * false if no variable with the specified name exists in the template. - */ - public boolean variableExists (String variableName) { - return mtp.lookupVariableName(variableName) != -1; } - - /** - * Returns a map with the names and current values of the template variables. - */ - public Map getVariables() { - HashMap map = new HashMap(mtp.varTabCnt); - for (int varNo = 0; varNo < mtp.varTabCnt; varNo++) - map.put(mtp.varTab[varNo], varValuesTab[varNo]); - return map; } - - /** - * Adds an instance of a template block. - *

If the block contains variables, these variables must be set - * before the block is added. - * If the block contains subblocks (nested blocks), the subblocks - * must be added before this block is added. - * If multiple blocks exist with the specified name, an instance - * is added for each block occurrence. - * @param blockName the name of the block to be added. Case-insensitive. - * @param isOptional specifies whether an exception should be thrown when the - * block does not exist in the template. If isOptional is - * false and the block does not exist, an exception is thrown. - * @throws BlockNotDefinedException when no block with the specified name - * exists in the template and isOptional is false. - */ - public void addBlock (String blockName, boolean isOptional) - throws BlockNotDefinedException { - int blockNo = mtp.lookupBlockName(blockName); - if(blockNo == -1) { - if (isOptional) { - return; } - throw new BlockNotDefinedException(blockName); } - while (blockNo != -1) { - addBlockByNo(blockNo); - blockNo = mtp.blockTab[blockNo].nextWithSameName; }} - - /** - * Adds an instance of a template block. - *

Convenience method for: addBlock (blockName, false) - * @param blockName the name of the block to be added. Case-insensitive. - * @throws BlockNotDefinedException when no block with the specified name - * exists in the template. - * @see #addBlock(String, boolean) - */ - public void addBlock (String blockName) - throws BlockNotDefinedException { - addBlock(blockName, false); } - - /** - * Adds an instance of an optional template block. - *

Convenience method for: addBlock (blockName, true) - * @param blockName the name of the block to be added. Case-insensitive. - * @see #addBlock(String, boolean) - */ - public void addBlockOpt (String blockName) { - addBlock(blockName, true); } - - private void addBlockByNo (int blockNo) { - MiniTemplatorParser.BlockTabRec btr = mtp.blockTab[blockNo]; - BlockDynTabRec bdtr = blockDynTab[blockNo]; - int blockInstNo = registerBlockInstance(); - BlockInstTabRec bitr = blockInstTab[blockInstNo]; - if (bdtr.firstBlockInstNo == -1) { - bdtr.firstBlockInstNo = blockInstNo; } - if (bdtr.lastBlockInstNo != -1) { - blockInstTab[bdtr.lastBlockInstNo].nextBlockInstNo = blockInstNo; } // set forward pointer of chain - bdtr.lastBlockInstNo = blockInstNo; - bitr.blockNo = blockNo; - bitr.instanceLevel = bdtr.instances++; - if (btr.parentBlockNo == -1) { - bitr.parentInstLevel = -1; } - else { - bitr.parentInstLevel = blockDynTab[btr.parentBlockNo].instances; } - bitr.nextBlockInstNo = -1; - if (btr.blockVarCnt > 0) { - bitr.blockVarTab = new String[btr.blockVarCnt]; } - for (int blockVarNo=0; blockVarNo blockInstTab.length) { - blockInstTab = (BlockInstTabRec[])MiniTemplatorParser.resizeArray(blockInstTab, 2*blockInstTabCnt); } - blockInstTab[blockInstNo] = new BlockInstTabRec(); - return blockInstNo; } - - /** - * Checks whether a block with the specified name exists within the template. - * @param blockName the name of the block. - * @return true if the block exists.
- * false if no block with the specified name exists in the template. - */ - public boolean blockExists (String blockName) { - return mtp.lookupBlockName(blockName) != -1; } - -//--- output generation ---------------------------------------------- - - /** - * Generates the HTML page and writes it into a file. - * @param outputFileName name of the file to which the generated HTML page will be written. - * @throws IOException when an i/o error occurs while writing to the file. - */ - public void generateOutput (String outputFileName) - throws IOException { - FileOutputStream stream = null; - OutputStreamWriter writer = null; - try { - stream = new FileOutputStream(outputFileName); - writer = new OutputStreamWriter(stream, charset); - generateOutput(writer); } - finally { - if (writer != null) { - writer.close(); } - if (stream != null) { - stream.close(); }}} - - /** - * Generates the HTML page and writes it to a character stream. - * @param outputWriter a character stream (writer) to which - * the HTML page will be written. - * @throws IOException when an i/o error occurs while writing to the stream. - */ - public void generateOutput (Writer outputWriter) - throws IOException { - String s = generateOutput(); - outputWriter.write(s); } - - /** - * Generates the HTML page and returns it as a string. - * @return A string that contains the generated HTML page. - */ - public String generateOutput() { - if (blockDynTab[0].instances == 0) { - addBlockByNo(0); } // add main block - for (int blockNo=0; blockNo parentInstLevel) { - break; } - writeBlockInstance(out, blockInstNo); - bdtr.currBlockInstNo = bitr.nextBlockInstNo; }} - - private void writeBlockInstance (StringBuilder out, int blockInstNo) { - BlockInstTabRec bitr = blockInstTab[blockInstNo]; - int blockNo = bitr.blockNo; - MiniTemplatorParser.BlockTabRec btr = mtp.blockTab[blockNo]; - int tPos = btr.tPosContentsBegin; - int subBlockNo = blockNo + 1; - int varRefNo = btr.firstVarRefNo; - while (true) { - int tPos2 = btr.tPosContentsEnd; - int kind = 0; // assume end-of-block - if (varRefNo != -1 && varRefNo < mtp.varRefTabCnt) { // check for variable reference - MiniTemplatorParser.VarRefTabRec vrtr = mtp.varRefTab[varRefNo]; - if (vrtr.tPosBegin < tPos) { - varRefNo++; - continue; } - if (vrtr.tPosBegin < tPos2) { - tPos2 = vrtr.tPosBegin; - kind = 1; }} - if (subBlockNo < mtp.blockTabCnt) { // check for subblock - MiniTemplatorParser.BlockTabRec subBtr = mtp.blockTab[subBlockNo]; - if (subBtr.tPosBegin < tPos) { - subBlockNo++; - continue; } - if (subBtr.tPosBegin < tPos2) { - tPos2 = subBtr.tPosBegin; - kind = 2; }} - if (tPos2 > tPos) { - out.append(mtp.templateText.substring(tPos, tPos2)); } - switch (kind) { - case 0: // end of block - return; - case 1: { // variable - MiniTemplatorParser.VarRefTabRec vrtr = mtp.varRefTab[varRefNo]; - if (vrtr.blockNo != blockNo) { - throw new AssertionError(); } - String variableValue = bitr.blockVarTab[vrtr.blockVarNo]; - if (variableValue != null) { - out.append(variableValue); } - tPos = vrtr.tPosEnd; - varRefNo++; - break; } - case 2: { // sub block - MiniTemplatorParser.BlockTabRec subBtr = mtp.blockTab[subBlockNo]; - if (subBtr.parentBlockNo != blockNo) { - throw new AssertionError(); } - writeBlockInstances(out, subBlockNo, bitr.instanceLevel); // recursive call - tPos = subBtr.tPosEnd; - subBlockNo++; - break; }}}} - -//--- general utility routines --------------------------------------- - - // Reads the contents of a file into a string variable. - private String readFileIntoString (String fileName) - throws IOException { - FileInputStream stream = null; - InputStreamReader reader = null; - try { - stream = new FileInputStream(fileName); - reader = new InputStreamReader(stream, charset); - return readStreamIntoString(reader); } - finally { - if (reader != null) { - reader.close(); } - if (stream != null) { - stream.close(); }}} - - // Reads the contents of a stream into a string variable. - private static String readStreamIntoString (Reader reader) - throws IOException { - StringBuilder s = new StringBuilder(); - char a[] = new char[0x10000]; - while (true) { - int l = reader.read(a); - if (l == -1) { - break; } - if (l <= 0) { - throw new IOException(); } - s.append(a, 0, l); } - return s.toString(); } - - /** - * Escapes special HTML characters. - * Replaces the characters <, >, &, ' and " by their corresponding - * HTML/XML character entity codes. - * @param s the input string. - * @return the escaped output string. - */ - public static String escapeHtml (String s) { - // (The code of this method is a bit redundant in order to optimize speed) - if (s == null) { - return null; } - int sLength = s.length(); - boolean found = false; - int p; - loop1: - for (p=0; p': case '&': case '\'': case '"': found = true; break loop1; }} - if (!found) { - return s; } - StringBuilder sb = new StringBuilder(sLength+16); - sb.append(s.substring(0, p)); - for (; p': sb.append (">"); break; - case '&': sb.append ("&"); break; - case '\'': sb.append ("'"); break; - case '"': sb.append ("""); break; - default: sb.append (c); }} - return sb.toString(); } - -} // End class MiniTemplator - -//==================================================================================================================== - -// MiniTemplatorParser is an immutable object that contains the parsed template text. -class MiniTemplatorParser { - -//--- constants ------------------------------------------------------ - - private static final int maxNestingLevel = 20; // maximum number of block nestings - private static final int maxCondLevels = 20; // maximum number of nested conditional commands ($if) - private static final int maxInclTemplateSize = 1000000; // maximum length of template string when including subtemplates - private static final String cmdStartStr = ""; // command end string - private static final String cmdStartStrShort = "<$"; // short form command start string - private static final String cmdEndStrShort = ">"; // short form command end string - -//--- nested classes ------------------------------------------------- - - public static class VarRefTabRec { // variable reference table record structure - int varNo; // variable no - int tPosBegin; // template position of begin of variable reference - int tPosEnd; // template position of end of variable reference - int blockNo; // block no of the (innermost) block that contains this variable reference - int blockVarNo; } // block variable no. Index into BlockInstTab.BlockVarTab - public static class BlockTabRec { // block table record structure - String blockName; // block name - int nextWithSameName; // block no of next block with same name or -1 (blocks are backward linked related to their position within the template) - int tPosBegin; // template position of begin of block - int tPosContentsBegin; // template pos of begin of block contents - int tPosContentsEnd; // template pos of end of block contents - int tPosEnd; // template position of end of block - int nestingLevel; // block nesting level - int parentBlockNo; // block no of parent block - boolean definitionIsOpen; // true while $BeginBlock processed but no $EndBlock - int blockVarCnt; // number of variables in block - int[] blockVarNoToVarNoMap; // maps block variable numbers to variable numbers - int firstVarRefNo; // variable reference no of first variable of this block or -1 - boolean dummy; } // true if this is a dummy block that will never be included in the output - -//--- variables ------------------------------------------------------ - - public String templateText; // contents of the template file - private HashSet conditionFlags; // set of the condition flags, converted to uppercase - private boolean shortFormEnabled; // true to enable the short form of commands ("<$...>") - - public String[] varTab; // variables table, contains variable names, array index is variable no - public int varTabCnt; // no of entries used in VarTab - private HashMap varNameToNoMap; // maps variable names to variable numbers - public VarRefTabRec[] varRefTab; // variable references table - // Contains an entry for each variable reference in the template. Ordered by templatePos. - public int varRefTabCnt; // no of entries used in VarRefTab - - public BlockTabRec[] blockTab; // Blocks table, array index is block no - // Contains an entry for each block in the template. Ordered by tPosBegin. - public int blockTabCnt; // no of entries used in BlockTab - private HashMap blockNameToNoMap; // maps block names to block numbers - - // The following variables are only used temporarilly during parsing of the template. - private int currentNestingLevel; // current block nesting level during parsing - private int[] openBlocksTab; // indexed by the block nesting level - // During parsing, this table contains the block numbers of the open parent blocks (nested outer blocks). - private int condLevel; // current nesting level of conditional commands ($if), -1 = main level - private boolean[] condEnabled; // enabled/disables state for the conditions of each level - private boolean[] condPassed; // true if an enabled condition clause has already been processed (separate for each level) - private MiniTemplator miniTemplator; // the MiniTemplator who created this parser object - // The reference to the MiniTemplator object is only used to call MiniTemplator.loadSubtemplate(). - private boolean resumeCmdParsingFromStart; // true = resume command parsing from the start position of the last command - -//--- constructor ---------------------------------------------------- - - // (The MiniTemplator object is only passed to the parser, because the -// parser needs to call MiniTemplator.loadSubtemplate() to load subtemplates.) - public MiniTemplatorParser (String templateText, Set conditionFlags, boolean shortFormEnabled, MiniTemplator miniTemplator) - throws MiniTemplator.TemplateSyntaxException { - this.templateText = templateText; - this.conditionFlags = createConditionFlagsSet(conditionFlags); - this.shortFormEnabled = shortFormEnabled; - this.miniTemplator = miniTemplator; - parseTemplate(); - this.miniTemplator = null; } - - private HashSet createConditionFlagsSet (Set flags) { - if (flags == null || flags.isEmpty()) { - return null; } - HashSet flags2 = new HashSet(flags.size()); - for (String flag : flags) { - flags2.add (flag.toUpperCase()); } - return flags2; } - -//--- template parsing ----------------------------------------------- - - private void parseTemplate() - throws MiniTemplator.TemplateSyntaxException { - initParsing(); - beginMainBlock(); - parseTemplateCommands(); - endMainBlock(); - checkBlockDefinitionsComplete(); - if (condLevel != -1) { - throw new MiniTemplator.TemplateSyntaxException ("$if without matching $endIf."); } - parseTemplateVariables(); - associateVariablesWithBlocks(); - terminateParsing(); } - - private void initParsing() { - varTab = new String[64]; - varTabCnt = 0; - varNameToNoMap = new HashMap(); - varRefTab = new VarRefTabRec[64]; - varRefTabCnt = 0; - blockTab = new BlockTabRec[16]; - blockTabCnt = 0; - currentNestingLevel = 0; - blockNameToNoMap = new HashMap(); - openBlocksTab = new int[maxNestingLevel+1]; - condLevel = -1; - condEnabled = new boolean[maxCondLevels]; - condPassed = new boolean[maxCondLevels]; } - - private void terminateParsing() { - openBlocksTab = null; } - - // Registers the main block. -// The main block is an implicitly defined block that covers the whole template. - private void beginMainBlock() { - int blockNo = registerBlock(null); // =0 - BlockTabRec btr = blockTab[blockNo]; - btr.tPosBegin = 0; - btr.tPosContentsBegin = 0; - openBlocksTab[currentNestingLevel] = blockNo; - currentNestingLevel++; } - - // Completes the main block registration. - private void endMainBlock() { - BlockTabRec btr = blockTab[0]; - btr.tPosContentsEnd = templateText.length(); - btr.tPosEnd = templateText.length(); - btr.definitionIsOpen = false; - currentNestingLevel--; } - -//--- Template commands -------------------------------------------------------- - - // Parses commands within the template in the format "". -// If shortFormEnabled is true, the short form commands in the format "<$...>" are also recognized. - private void parseTemplateCommands() - throws MiniTemplator.TemplateSyntaxException { - int p = 0; // p is the current position within templateText - while (true) { - int p0 = templateText.indexOf(cmdStartStr, p); // p0 is the start of the current command - boolean shortForm = false; - if (shortFormEnabled && p0 != p) { - if (p0 == -1) { - p0 = templateText.indexOf(cmdStartStrShort, p); - shortForm = true; } - else { - int p2 = templateText.substring(p, p0).indexOf(cmdStartStrShort); - if (p2 != -1) { - p0 = p + p2; - shortForm = true; }}} - if (p0 == -1) { // no more commands - break; } - conditionalExclude(p, p0); // process text up to the start of the current command - if (shortForm) { // short form command - p = templateText.indexOf(cmdEndStrShort, p0 + cmdStartStrShort.length()); - if (p == -1) { // if no terminating ">" is found, we process it as normal text - p = p0 + cmdStartStrShort.length(); - conditionalExclude(p0, p); - continue; } - p += cmdEndStrShort.length(); - String cmdLine = templateText.substring(p0 + cmdStartStrShort.length(), p - cmdEndStrShort.length()); - if (!processShortFormTemplateCommand(cmdLine, p0, p)) { - // If a short form command is not recognized, we process the whole command structure are normal text. - conditionalExclude(p0, p); }} - else { // normal (long) form command - p = templateText.indexOf(cmdEndStr, p0 + cmdStartStr.length()); - if (p == -1) { - throw new MiniTemplator.TemplateSyntaxException("Invalid HTML comment in template at offset " + p0 + "."); } - p += cmdEndStr.length(); - String cmdLine = templateText.substring(p0 + cmdStartStr.length(), p - cmdEndStr.length()); - resumeCmdParsingFromStart = false; - if (!processTemplateCommand(cmdLine, p0, p)) { - conditionalExclude(p0, p); } // process as normal temlate text - if (resumeCmdParsingFromStart) { // (if a subtemplate has been included) - p = p0; }}}} - - // Returns false if the command should be treatet as normal template text. - private boolean processTemplateCommand (String cmdLine, int cmdTPosBegin, int cmdTPosEnd) - throws MiniTemplator.TemplateSyntaxException { - int p0 = skipBlanks(cmdLine, 0); - if (p0 >= cmdLine.length()) { - return false; } - int p = skipNonBlanks(cmdLine, p0); - String cmd = cmdLine.substring(p0, p); - String parms = cmdLine.substring(p); - /* select */ - if (cmd.equalsIgnoreCase("$beginBlock")) { - processBeginBlockCmd(parms, cmdTPosBegin, cmdTPosEnd); } - else if (cmd.equalsIgnoreCase("$endBlock")) { - processEndBlockCmd(parms, cmdTPosBegin, cmdTPosEnd); } - else if (cmd.equalsIgnoreCase("$include")) { - processIncludeCmd(parms, cmdTPosBegin, cmdTPosEnd); } - else if (cmd.equalsIgnoreCase("$if")) { - processIfCmd(parms, cmdTPosBegin, cmdTPosEnd); } - else if (cmd.equalsIgnoreCase("$elseIf")) { - processElseIfCmd(parms, cmdTPosBegin, cmdTPosEnd); } - else if (cmd.equalsIgnoreCase("$else")) { - processElseCmd(parms, cmdTPosBegin, cmdTPosEnd); } - else if (cmd.equalsIgnoreCase("$endIf")) { - processEndIfCmd(parms, cmdTPosBegin, cmdTPosEnd); } - else { - if (cmd.startsWith("$") && !cmd.startsWith("${")) { - throw new MiniTemplator.TemplateSyntaxException("Unknown command \"" + cmd + "\" in template at offset " + cmdTPosBegin + "."); } - else { - return false; }} - return true; } - - // Returns false if the command is not recognized and should be treatet as normal temlate text. - private boolean processShortFormTemplateCommand (String cmdLine, int cmdTPosBegin, int cmdTPosEnd) - throws MiniTemplator.TemplateSyntaxException { - int p0 = skipBlanks(cmdLine, 0); - if (p0 >= cmdLine.length()) { - return false; } - int p = p0; - char cmd1 = cmdLine.charAt(p++); - if (cmd1 == '/' && p < cmdLine.length() && !Character.isWhitespace(cmdLine.charAt(p))) { - p++; } - String cmd = cmdLine.substring(p0, p); - String parms = cmdLine.substring(p).trim(); - /* select */ - if (cmd.equals("?")) { - processIfCmd(parms, cmdTPosBegin, cmdTPosEnd); } - else if (cmd.equals(":")) { - if (parms.length() > 0) { - processElseIfCmd(parms, cmdTPosBegin, cmdTPosEnd); } - else { - processElseCmd(parms, cmdTPosBegin, cmdTPosEnd); }} - else if (cmd.equals("/?")) { - processEndIfCmd(parms, cmdTPosBegin, cmdTPosEnd); } - else { - return false; } - return true; } - - // Processes the $beginBlock command. - private void processBeginBlockCmd (String parms, int cmdTPosBegin, int cmdTPosEnd) - throws MiniTemplator.TemplateSyntaxException { - if (conditionalExclude(cmdTPosBegin, cmdTPosEnd)) { - return; } - int p0 = skipBlanks(parms, 0); - if (p0 >= parms.length()) { - throw new MiniTemplator.TemplateSyntaxException("Missing block name in $BeginBlock command in template at offset " + cmdTPosBegin + "."); } - int p = skipNonBlanks(parms, p0); - String blockName = parms.substring(p0, p); - if (!isRestOfStringBlank(parms, p)) { - throw new MiniTemplator.TemplateSyntaxException("Extra parameter in $BeginBlock command in template at offset " + cmdTPosBegin + "."); } - int blockNo = registerBlock(blockName); - BlockTabRec btr = blockTab[blockNo]; - btr.tPosBegin = cmdTPosBegin; - btr.tPosContentsBegin = cmdTPosEnd; - openBlocksTab[currentNestingLevel] = blockNo; - currentNestingLevel++; - if (currentNestingLevel > maxNestingLevel) { - throw new MiniTemplator.TemplateSyntaxException("Block nesting overflow for block \"" + blockName + "\" in template at offset " + cmdTPosBegin + "."); }} - - // Processes the $endBlock command. - private void processEndBlockCmd (String parms, int cmdTPosBegin, int cmdTPosEnd) - throws MiniTemplator.TemplateSyntaxException { - if (conditionalExclude(cmdTPosBegin, cmdTPosEnd)) { - return; } - int p0 = skipBlanks(parms, 0); - if (p0 >= parms.length()) { - throw new MiniTemplator.TemplateSyntaxException("Missing block name in $EndBlock command in template at offset " + cmdTPosBegin + "."); } - int p = skipNonBlanks(parms, p0); - String blockName = parms.substring(p0, p); - if (!isRestOfStringBlank(parms, p)) { - throw new MiniTemplator.TemplateSyntaxException("Extra parameter in $EndBlock command in template at offset " + cmdTPosBegin + "."); } - int blockNo = lookupBlockName(blockName); - if (blockNo == -1) { - throw new MiniTemplator.TemplateSyntaxException("Undefined block name \"" + blockName + "\" in $EndBlock command in template at offset " + cmdTPosBegin + "."); } - currentNestingLevel--; - BlockTabRec btr = blockTab[blockNo]; - if (!btr.definitionIsOpen) { - throw new MiniTemplator.TemplateSyntaxException("Multiple $EndBlock command for block \"" + blockName + "\" in template at offset " + cmdTPosBegin + "."); } - if (btr.nestingLevel != currentNestingLevel) { - throw new MiniTemplator.TemplateSyntaxException("Block nesting level mismatch at $EndBlock command for block \"" + blockName + "\" in template at offset " + cmdTPosBegin + "."); } - btr.tPosContentsEnd = cmdTPosBegin; - btr.tPosEnd = cmdTPosEnd; - btr.definitionIsOpen = false; } - - // Returns the block number of the newly registered block. - private int registerBlock (String blockName) { - int blockNo = blockTabCnt++; - if (blockTabCnt > blockTab.length) { - blockTab = (BlockTabRec[])resizeArray(blockTab, 2*blockTabCnt); } - BlockTabRec btr = new BlockTabRec(); - blockTab[blockNo] = btr; - btr.blockName = blockName; - if (blockName != null) { - btr.nextWithSameName = lookupBlockName(blockName); } - else { - btr.nextWithSameName = -1; } - btr.nestingLevel = currentNestingLevel; - if (currentNestingLevel > 0) { - btr.parentBlockNo = openBlocksTab[currentNestingLevel-1]; } - else { - btr.parentBlockNo = -1; } - btr.definitionIsOpen = true; - btr.blockVarCnt = 0; - btr.firstVarRefNo = -1; - btr.blockVarNoToVarNoMap = new int[32]; - btr.dummy = false; - if (blockName != null) { - blockNameToNoMap.put(blockName.toUpperCase(), new Integer(blockNo)); } - return blockNo; } - - // Registers a dummy block to exclude a range within the template text. - private void excludeTemplateRange (int tPosBegin, int tPosEnd) { - if (blockTabCnt > 0) { - // Check whether we can extend the previous block. - BlockTabRec btr = blockTab[blockTabCnt-1]; - if (btr.dummy && btr.tPosEnd == tPosBegin) { - btr.tPosContentsEnd = tPosEnd; - btr.tPosEnd = tPosEnd; - return; }} - int blockNo = registerBlock(null); - BlockTabRec btr = blockTab[blockNo]; - btr.tPosBegin = tPosBegin; - btr.tPosContentsBegin = tPosBegin; - btr.tPosContentsEnd = tPosEnd; - btr.tPosEnd = tPosEnd; - btr.definitionIsOpen = false; - btr.dummy = true; } - - // Checks that all block definitions are closed. - private void checkBlockDefinitionsComplete() - throws MiniTemplator.TemplateSyntaxException { - for (int blockNo=0; blockNo= parms.length()) { - throw new MiniTemplator.TemplateSyntaxException("Missing subtemplate name in $Include command in template at offset " + cmdTPosBegin + "."); } - int p; - if (parms.charAt(p0) == '"') { // subtemplate name is quoted - p0++; - p = parms.indexOf('"', p0); - if (p == -1) { - throw new MiniTemplator.TemplateSyntaxException("Missing closing quote for subtemplate name in $Include command in template at offset " + cmdTPosBegin + "."); }} - else { - p = skipNonBlanks(parms, p0); } - String subtemplateName = parms.substring(p0, p); - p++; - if (!isRestOfStringBlank(parms, p)) { - throw new MiniTemplator.TemplateSyntaxException("Extra parameter in $Include command in template at offset " + cmdTPosBegin + "."); } - insertSubtemplate(subtemplateName, cmdTPosBegin, cmdTPosEnd); } - - private void insertSubtemplate (String subtemplateName, int tPos1, int tPos2) { - if (templateText.length() > maxInclTemplateSize) { - throw new RuntimeException("Subtemplate include aborted because the internal template string is longer than "+maxInclTemplateSize+" characters."); } - String subtemplate; - try { - subtemplate = miniTemplator.loadSubtemplate(subtemplateName); } - catch (IOException e) { - throw new RuntimeException("Error while loading subtemplate \""+subtemplateName+"\"", e); } - // (Copying the template to insert a subtemplate is a bit slow. In a future implementation of MiniTemplator, - // a table could be used that contains references to the string fragments.) - StringBuilder s = new StringBuilder(templateText.length()+subtemplate.length()); - s.append(templateText, 0, tPos1); - s.append(subtemplate); - s.append(templateText, tPos2, templateText.length()); - templateText = s.toString(); - resumeCmdParsingFromStart = true; } - -//--- Conditional commands ----------------------------------------------------- - - // Returns the enabled/disabled state of the condition at level condLevel2. - private boolean isCondEnabled (int condLevel2) { - if (condLevel2 < 0) { - return true; } - return condEnabled[condLevel2]; } - - // If the current condition is disabled, the text from tPosBegin to tPosEnd -// is excluded and true is returned. -// Otherwise nothing is done and false is returned. - private boolean conditionalExclude (int tPosBegin, int tPosEnd) { - if (isCondEnabled(condLevel)) { - return false; } - excludeTemplateRange(tPosBegin, tPosEnd); - return true; } - - // Evaluates a condition expression of a conditional command, by comparing the -// flags in the expression with the flags in TemplateSpecification.conditionFlags. -// Returns true the condition is met. - private boolean evaluateConditionFlags (String flags) { - int p = 0; - while (true) { - p = skipBlanks(flags, p); - if (p >= flags.length()) { - break; } - boolean complement = false; - if (flags.charAt(p) == '!') { - complement = true; p++; } - p = skipBlanks(flags, p); - if (p >= flags.length()) { - break; } - int p0 = p; - p = skipNonBlanks(flags, p0+1); - String flag = flags.substring(p0, p).toUpperCase(); - if ((conditionFlags != null && conditionFlags.contains(flag)) ^ complement) { - return true; }} - return false; } - - // Processes the $if command. - private void processIfCmd (String parms, int cmdTPosBegin, int cmdTPosEnd) - throws MiniTemplator.TemplateSyntaxException { - excludeTemplateRange(cmdTPosBegin, cmdTPosEnd); - if (condLevel >= maxCondLevels-1) { - throw new MiniTemplator.TemplateSyntaxException ("Too many nested $if commands."); } - condLevel++; - boolean enabled = isCondEnabled(condLevel-1) && evaluateConditionFlags(parms); - condEnabled[condLevel] = enabled; - condPassed[condLevel] = enabled; } - - // Processes the $elseIf command. - private void processElseIfCmd (String parms, int cmdTPosBegin, int cmdTPosEnd) - throws MiniTemplator.TemplateSyntaxException { - excludeTemplateRange(cmdTPosBegin, cmdTPosEnd); - if (condLevel < 0) { - throw new MiniTemplator.TemplateSyntaxException ("$elseIf without matching $if."); } - boolean enabled = isCondEnabled(condLevel-1) && !condPassed[condLevel] && evaluateConditionFlags(parms); - condEnabled[condLevel] = enabled; - if (enabled) { - condPassed[condLevel] = true; }} - - // Processes the $else command. - private void processElseCmd (String parms, int cmdTPosBegin, int cmdTPosEnd) - throws MiniTemplator.TemplateSyntaxException { - excludeTemplateRange(cmdTPosBegin, cmdTPosEnd); - if (parms.trim().length() != 0) { - throw new MiniTemplator.TemplateSyntaxException ("Invalid parameters for $else command."); } - if (condLevel < 0) { - throw new MiniTemplator.TemplateSyntaxException ("$else without matching $if."); } - boolean enabled = isCondEnabled(condLevel-1) && !condPassed[condLevel]; - condEnabled[condLevel] = enabled; - if (enabled) { - condPassed[condLevel] = true; }} - - // Processes the $endIf command. - private void processEndIfCmd (String parms, int cmdTPosBegin, int cmdTPosEnd) - throws MiniTemplator.TemplateSyntaxException { - excludeTemplateRange(cmdTPosBegin, cmdTPosEnd); - if (parms.trim().length() != 0) { - throw new MiniTemplator.TemplateSyntaxException ("Invalid parameters for $endIf command."); } - if (condLevel < 0) { - throw new MiniTemplator.TemplateSyntaxException ("$endif without matching $if."); } - condLevel--; } - -//------------------------------------------------------------------------------ - - // Associates variable references with blocks. - private void associateVariablesWithBlocks() { - int varRefNo = 0; - int activeBlockNo = 0; - int nextBlockNo = 1; - while (varRefNo < varRefTabCnt) { - VarRefTabRec vrtr = varRefTab[varRefNo]; - int varRefTPos = vrtr.tPosBegin; - int varNo = vrtr.varNo; - if (varRefTPos >= blockTab[activeBlockNo].tPosEnd) { - activeBlockNo = blockTab[activeBlockNo].parentBlockNo; - continue; } - if (nextBlockNo < blockTabCnt && varRefTPos >= blockTab[nextBlockNo].tPosBegin) { - activeBlockNo = nextBlockNo; - nextBlockNo++; - continue; } - BlockTabRec btr = blockTab[activeBlockNo]; - if (varRefTPos < btr.tPosBegin) { - throw new AssertionError(); } - int blockVarNo = btr.blockVarCnt++; - if (btr.blockVarCnt > btr.blockVarNoToVarNoMap.length) { - btr.blockVarNoToVarNoMap = (int[])resizeArray(btr.blockVarNoToVarNoMap, 2*btr.blockVarCnt); } - btr.blockVarNoToVarNoMap[blockVarNo] = varNo; - if (btr.firstVarRefNo == -1) { - btr.firstVarRefNo = varRefNo; } - vrtr.blockNo = activeBlockNo; - vrtr.blockVarNo = blockVarNo; - varRefNo++; }} - - // Parses variable references within the template in the format "${VarName}" . - private void parseTemplateVariables() - throws MiniTemplator.TemplateSyntaxException { - int p = 0; - while (true) { - p = templateText.indexOf("${", p); - if (p == -1) { - break; } - int p0 = p; - p = templateText.indexOf("}", p); - if (p == -1) { - throw new MiniTemplator.TemplateSyntaxException("Invalid variable reference in template at offset " + p0 + "."); } - p++; - String varName = templateText.substring(p0+2, p-1).trim(); - if (varName.length() == 0) { - throw new MiniTemplator.TemplateSyntaxException("Empty variable name in template at offset " + p0 + "."); } - registerVariableReference(varName, p0, p); }} - - private void registerVariableReference (String varName, int tPosBegin, int tPosEnd) { - int varNo; - varNo = lookupVariableName(varName); - if (varNo == -1) { - varNo = registerVariable(varName); } - int varRefNo = varRefTabCnt++; - if (varRefTabCnt > varRefTab.length) { - varRefTab = (VarRefTabRec[])resizeArray(varRefTab, 2*varRefTabCnt); } - VarRefTabRec vrtr = new VarRefTabRec(); - varRefTab[varRefNo] = vrtr; - vrtr.tPosBegin = tPosBegin; - vrtr.tPosEnd = tPosEnd; - vrtr.varNo = varNo; } - - // Returns the variable number of the newly registered variable. - private int registerVariable (String varName) { - int varNo = varTabCnt++; - if (varTabCnt > varTab.length) { - varTab = (String[])resizeArray(varTab, 2*varTabCnt); } - varTab[varNo] = varName; - varNameToNoMap.put(varName.toUpperCase(), new Integer(varNo)); - return varNo; } - -//--- name lookup routines ------------------------------------------- - - // Maps variable name to variable number. -// Returns -1 if the variable name is not found. - public int lookupVariableName (String varName) { - Integer varNoWrapper = varNameToNoMap.get(varName.toUpperCase()); - if (varNoWrapper == null) { - return -1; } - int varNo = varNoWrapper.intValue(); - return varNo; } - - // Maps block name to block number. -// If there are multiple blocks with the same name, the block number of the last -// registered block with that name is returned. -// Returns -1 if the block name is not found. - public int lookupBlockName (String blockName) { - Integer blockNoWrapper = blockNameToNoMap.get(blockName.toUpperCase()); - if (blockNoWrapper == null) { - return -1; } - int blockNo = blockNoWrapper.intValue(); - return blockNo; } - -//--- general utility routines --------------------------------------- - - // Reallocates an array with a new size and copies the contents -// of the old array to the new array. - public static Object resizeArray (Object oldArray, int newSize) { - int oldSize = java.lang.reflect.Array.getLength(oldArray); - Class elementType = oldArray.getClass().getComponentType(); - Object newArray = java.lang.reflect.Array.newInstance( - elementType, newSize); - int preserveLength = Math.min(oldSize, newSize); - if (preserveLength > 0) { - System.arraycopy(oldArray, 0, newArray, 0, preserveLength); } - return newArray; } - - // Skips blanks (white space) in string s starting at position p. - private static int skipBlanks (String s, int p) { - while (p < s.length() && Character.isWhitespace(s.charAt(p))) p++; - return p; } - - // Skips non-blanks (no-white space) in string s starting at position p. - private static int skipNonBlanks (String s, int p) { - while (p < s.length() && !Character.isWhitespace(s.charAt(p))) p++; - return p; } - - // Returns true if string s is blank (white space) from position p to the end. - public static boolean isRestOfStringBlank (String s, int p) { - return skipBlanks(s, p) >= s.length(); } - -} // End class MiniTemplatorParser \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufClassCompiler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufClassCompiler.java deleted file mode 100644 index 7ca26e4a81..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufClassCompiler.java +++ /dev/null @@ -1,283 +0,0 @@ -package com.taobao.arthas.grpc.server.protobuf.utils; - -import javax.tools.*; -import java.io.*; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.*; - -/** - * @author: FengYe - * @date: 2024/8/9 01:21 - * @description: ProtoBufClassCompiler - */ -public class ProtoBufClassCompiler { - - private final Map fileObjects = new HashMap(); - - private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - - private final ClassLoaderImpl classLoader; - - private final JavaFileManagerImpl javaFileManager; - - private volatile List options; - - private static final String jdkVersion = "1.8"; - - - public ProtoBufClassCompiler(final ClassLoader loader) { - options = new ArrayList(); - options.add("-source"); - options.add(jdkVersion); - options.add("-target"); - options.add(jdkVersion); - - DiagnosticCollector diagnosticCollector = new DiagnosticCollector(); - StandardJavaFileManager manager = - compiler.getStandardFileManager(diagnosticCollector, null, StandardCharsets.UTF_8); - - classLoader = AccessController.doPrivileged(new PrivilegedAction() { - public ClassLoaderImpl run() { - return new ClassLoaderImpl(loader); - } - }); - - javaFileManager = new JavaFileManagerImpl(manager, classLoader); - } - - public synchronized Class compile(String className, String code, ClassLoader classLoader) { - FileOutputStream fos = null; - code = code.trim(); - try { - return Class.forName(className, true, classLoader); - } catch (ClassNotFoundException e) { - if (!code.endsWith("}")) { - throw new IllegalStateException("The java code not endsWith \"}\", code: \n" + code + "\n"); - } - try { - return doCompile(className, code); - } catch (RuntimeException t) { - throw t; - } catch (Throwable t) { - throw new IllegalStateException("Failed to compile class, cause: " + t.getMessage() + ", class: " - + className + ", code: \n" + code + "\n, stack: " + ProtoBufUtil.toString(t)); - } - } - } - - public synchronized Class doCompile(String name, String sourceCode) throws Throwable { - int i = name.lastIndexOf('.'); - String packageName = i < 0 ? "" : name.substring(0, i); - String className = i < 0 ? name : name.substring(i + 1); - JavaFileObjectImpl javaFileObject = new JavaFileObjectImpl(className, sourceCode); - fileObjects.put(new URI(StandardLocation.SOURCE_PATH.getName() + "/" + packageName + "/" + className + ".java"), javaFileObject); - javaFileManager.putFileForInput(StandardLocation.SOURCE_PATH, packageName, - className + ProtoBufUtil.JAVA_EXTENSION, javaFileObject); - - DiagnosticCollector diagnosticCollector = new DiagnosticCollector(); - Boolean result = compiler.getTask(null, javaFileManager, diagnosticCollector, options, null, - Arrays.asList(new JavaFileObject[]{javaFileObject})).call(); - if (result == null || !result.booleanValue()) { - throw new IllegalStateException( - "Compilation failed. class: " + name + ", diagnostics: " + diagnosticCollector.getDiagnostics()); - } - - Class retClass = classLoader.loadClass(name); - - return retClass; - } - - private static final class JavaFileManagerImpl extends ForwardingJavaFileManager { - - private final ClassLoaderImpl classLoader; - - private final Map fileObjects = new HashMap(); - - public JavaFileManagerImpl(JavaFileManager fileManager, ClassLoaderImpl classLoader) { - super(fileManager); - this.classLoader = classLoader; - } - - @Override - public FileObject getFileForInput(Location location, String packageName, String relativeName) - throws IOException { - FileObject o = fileObjects.get(uri(location, packageName, relativeName)); - if (o != null) { - return o; - } - return super.getFileForInput(location, packageName, relativeName); - } - - public void putFileForInput(StandardLocation location, String packageName, String relativeName, - JavaFileObject file) { - fileObjects.put(uri(location, packageName, relativeName), file); - } - - private URI uri(Location location, String packageName, String relativeName) { - return ProtoBufUtil.toURI(location.getName() + '/' + packageName + '/' + relativeName); - } - - @Override - public JavaFileObject getJavaFileForOutput(Location location, String qualifiedName, JavaFileObject.Kind kind, - FileObject outputFile) throws IOException { - JavaFileObject file = new JavaFileObjectImpl(qualifiedName, kind); - classLoader.add(qualifiedName, file); - return file; - } - - @Override - public ClassLoader getClassLoader(JavaFileManager.Location location) { - return classLoader; - } - - @Override - public String inferBinaryName(Location loc, JavaFileObject file) { - if (file instanceof JavaFileObjectImpl) { - return file.getName(); - } - return super.inferBinaryName(loc, file); - } - - @Override - public Iterable list(Location location, String packageName, Set kinds, boolean recurse) - throws IOException { - Iterable result = super.list(location, packageName, kinds, recurse); - - ArrayList files = new ArrayList(); - - if (location == StandardLocation.CLASS_PATH && kinds.contains(JavaFileObject.Kind.CLASS)) { - for (JavaFileObject file : fileObjects.values()) { - if (file.getKind() == JavaFileObject.Kind.CLASS && file.getName().startsWith(packageName)) { - files.add(file); - } - } - - files.addAll(classLoader.files()); - } else if (location == StandardLocation.SOURCE_PATH && kinds.contains(JavaFileObject.Kind.SOURCE)) { - for (JavaFileObject file : fileObjects.values()) { - if (file.getKind() == JavaFileObject.Kind.SOURCE && file.getName().startsWith(packageName)) { - files.add(file); - } - } - } - - for (JavaFileObject file : result) { - files.add(file); - } - - return files; - } - } - - - private final class ClassLoaderImpl extends ClassLoader { - - private final Map classes = new HashMap(); - - ClassLoaderImpl(final ClassLoader parentClassLoader) { - super(parentClassLoader); - } - - Collection files() { - return Collections.unmodifiableCollection(classes.values()); - } - - public byte[] loadClassBytes(final String qualifiedClassName) { - JavaFileObject file = classes.get(qualifiedClassName); - if (file != null) { - byte[] bytes = ((JavaFileObjectImpl) file).getByteCode(); - return bytes; - } - return null; - } - - @Override - protected Class findClass(final String qualifiedClassName) throws ClassNotFoundException { - JavaFileObject file = classes.get(qualifiedClassName); - if (file != null) { - byte[] bytes = ((JavaFileObjectImpl) file).getByteCode(); - return defineClass(qualifiedClassName, bytes, 0, bytes.length); - } - try { - return ProtoBufUtil.forNameWithCallerClassLoader(qualifiedClassName, getClass()); - } catch (ClassNotFoundException nf) { - return super.findClass(qualifiedClassName); - } - } - - void add(final String qualifiedClassName, final JavaFileObject javaFile) { - classes.put(qualifiedClassName, javaFile); - } - - @Override - protected synchronized Class loadClass(final String name, final boolean resolve) - throws ClassNotFoundException { - return super.loadClass(name, resolve); - } - - @Override - public InputStream getResourceAsStream(final String name) { - if (name.endsWith(ProtoBufUtil.CLASS_EXTENSION)) { - String qualifiedClassName = - name.substring(0, name.length() - ProtoBufUtil.CLASS_EXTENSION.length()).replace('/', '.'); - JavaFileObjectImpl file = (JavaFileObjectImpl) classes.get(qualifiedClassName); - if (file != null) { - return new ByteArrayInputStream(file.getByteCode()); - } - } - return super.getResourceAsStream(name); - } - } - - - private static final class JavaFileObjectImpl extends SimpleJavaFileObject { - - private ByteArrayOutputStream bytecode; - - private final CharSequence source; - - public JavaFileObjectImpl(final String baseName, final CharSequence source) throws URISyntaxException { - super(new URI(baseName + ProtoBufUtil.JAVA_EXTENSION), Kind.SOURCE); - this.source = source; - } - - JavaFileObjectImpl(final String name, final Kind kind) { - super(ProtoBufUtil.toURI(name), kind); - source = null; - } - - public JavaFileObjectImpl(URI uri, Kind kind) { - super(uri, kind); - source = null; - } - - @Override - public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws UnsupportedOperationException { - if (source == null) { - throw new UnsupportedOperationException("source == null"); - } - return source; - } - - @Override - public InputStream openInputStream() { - return new ByteArrayInputStream(getByteCode()); - } - - @Override - public OutputStream openOutputStream() { - return bytecode = new ByteArrayOutputStream(); - } - - public byte[] getByteCode() { - if (bytecode == null) { - return null; - } - return bytecode.toByteArray(); - } - } -} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java deleted file mode 100644 index d4c23882e0..0000000000 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/protobuf/utils/ProtoBufUtil.java +++ /dev/null @@ -1,1248 +0,0 @@ -package com.taobao.arthas.grpc.server.protobuf.utils; - -import com.alibaba.arthas.deps.org.slf4j.Logger; -import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; -import com.google.protobuf.*; -import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; -import com.taobao.arthas.grpc.server.protobuf.ProtobufField; -import com.taobao.arthas.grpc.server.protobuf.ProtobufFieldTypeEnum; -import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; -import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufPacked; -import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufCustomizedField; -import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufIgnore; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.lang.Enum; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Array; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.*; - -/** - * @author: FengYe - * @date: 2024/7/25 上午12:33 - * @description: ProtoBufUtil - */ -public class ProtoBufUtil { - - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass().getName()); - - public static final Map, ProtobufFieldTypeEnum> TYPE_MAPPER; - - public static final Map PRIMITIVE_TYPE_MAPPING; - - private static final Map> PRIMIIIVE_TYPE_CLASS_MAPPING = new HashMap>(16); - - private static final Map, Class> PRIMITIVE_WRAPPER_TYPE_MAP = new HashMap, Class>(8); - - public static final String DYNAMIC_TARGET = "target"; - - public static final String PACKAGE_SEPARATOR = "."; - - public static final String LINE_BREAK = "\n"; - - public static final String JAVA_LINE_BREAK = ";" + LINE_BREAK; - - public static final String CODE_OUTPUT_STREAM_OBJ_NAME = "output"; - - public static final String WIREFORMAT_CLSNAME = com.google.protobuf.WireFormat.FieldType.class.getCanonicalName(); - - public static final String ARRAY_SUFFIX = "[]"; - - private static final String INTERNAL_ARRAY_PREFIX = "[L"; - - public static final String JAVA_EXTENSION = ".java"; - - public static final String CLASS_EXTENSION = ".class"; - - static { - - PRIMITIVE_TYPE_MAPPING = new HashMap(); - - PRIMITIVE_TYPE_MAPPING.put(int.class.getSimpleName(), Integer.class.getSimpleName()); - PRIMITIVE_TYPE_MAPPING.put(long.class.getSimpleName(), Long.class.getSimpleName()); - PRIMITIVE_TYPE_MAPPING.put(short.class.getSimpleName(), Short.class.getSimpleName()); - PRIMITIVE_TYPE_MAPPING.put(boolean.class.getSimpleName(), Boolean.class.getSimpleName()); - PRIMITIVE_TYPE_MAPPING.put(double.class.getSimpleName(), Double.class.getSimpleName()); - PRIMITIVE_TYPE_MAPPING.put(float.class.getSimpleName(), Float.class.getSimpleName()); - PRIMITIVE_TYPE_MAPPING.put(char.class.getSimpleName(), Character.class.getSimpleName()); - PRIMITIVE_TYPE_MAPPING.put(byte.class.getSimpleName(), Byte.class.getSimpleName()); - - PRIMITIVE_WRAPPER_TYPE_MAP.put(Boolean.class, boolean.class); - PRIMITIVE_WRAPPER_TYPE_MAP.put(Byte.class, byte.class); - PRIMITIVE_WRAPPER_TYPE_MAP.put(Character.class, char.class); - PRIMITIVE_WRAPPER_TYPE_MAP.put(Double.class, double.class); - PRIMITIVE_WRAPPER_TYPE_MAP.put(Float.class, float.class); - PRIMITIVE_WRAPPER_TYPE_MAP.put(Integer.class, int.class); - PRIMITIVE_WRAPPER_TYPE_MAP.put(Long.class, long.class); - PRIMITIVE_WRAPPER_TYPE_MAP.put(Short.class, short.class); - - Set> primitiveTypeNames = new HashSet>(16); - primitiveTypeNames.addAll(PRIMITIVE_WRAPPER_TYPE_MAP.values()); - primitiveTypeNames.addAll(Arrays.asList(new Class[]{boolean[].class, byte[].class, char[].class, - double[].class, float[].class, int[].class, long[].class, short[].class})); - for (Iterator> it = primitiveTypeNames.iterator(); it.hasNext(); ) { - Class primitiveClass = (Class) it.next(); - PRIMIIIVE_TYPE_CLASS_MAPPING.put(primitiveClass.getName(), primitiveClass); - } - } - - static { - TYPE_MAPPER = new HashMap, ProtobufFieldTypeEnum>(); - - TYPE_MAPPER.put(int.class, ProtobufFieldTypeEnum.INT32); - TYPE_MAPPER.put(Integer.class, ProtobufFieldTypeEnum.INT32); - TYPE_MAPPER.put(short.class, ProtobufFieldTypeEnum.INT32); - TYPE_MAPPER.put(Short.class, ProtobufFieldTypeEnum.INT32); - TYPE_MAPPER.put(Byte.class, ProtobufFieldTypeEnum.INT32); - TYPE_MAPPER.put(byte.class, ProtobufFieldTypeEnum.INT32); - TYPE_MAPPER.put(long.class, ProtobufFieldTypeEnum.INT64); - TYPE_MAPPER.put(Long.class, ProtobufFieldTypeEnum.INT64); - TYPE_MAPPER.put(String.class, ProtobufFieldTypeEnum.STRING); - TYPE_MAPPER.put(byte[].class, ProtobufFieldTypeEnum.BYTES); - TYPE_MAPPER.put(Byte[].class, ProtobufFieldTypeEnum.BYTES); - TYPE_MAPPER.put(Float.class, ProtobufFieldTypeEnum.FLOAT); - TYPE_MAPPER.put(float.class, ProtobufFieldTypeEnum.FLOAT); - TYPE_MAPPER.put(double.class, ProtobufFieldTypeEnum.DOUBLE); - TYPE_MAPPER.put(Double.class, ProtobufFieldTypeEnum.DOUBLE); - TYPE_MAPPER.put(Boolean.class, ProtobufFieldTypeEnum.BOOL); - TYPE_MAPPER.put(boolean.class, ProtobufFieldTypeEnum.BOOL); - TYPE_MAPPER.put(Date.class, ProtobufFieldTypeEnum.DATE); - TYPE_MAPPER.put(BigDecimal.class, ProtobufFieldTypeEnum.BIGDECIMAL); - TYPE_MAPPER.put(BigInteger.class, ProtobufFieldTypeEnum.BIGINTEGER); - } - - - /** - * 将指定类的所有 java 字段转化为 protobuf 字段 - * 字段编号逻辑:优先处理自定义的字段,其余字段在最大的自定义字段基础上递增 - * - * @param clazz - * @param enableZigZap - * @return - */ - public static List getProtobufFieldList(Class clazz, boolean enableZigZap) { - // 获取所有的 java field - List fields = new ArrayList<>(); - Field[] fieldsArray = clazz.getDeclaredFields(); - for (Field field : fieldsArray) { - if (field.getAnnotation(ProtobufIgnore.class) == null) { - fields.add(field); - } - } - - // 转化为 protobuf field - List res = new ArrayList<>(); - List unOrderFields = new ArrayList<>(); - Set orders = new HashSet<>(); - int maxOrder = 0; - - for (Field field : fields) { - Class fieldType = field.getType(); - String fieldName = field.getName(); - String filedTypeName = fieldType.getName(); - ProtobufCustomizedField customizedField = field.getAnnotation(ProtobufCustomizedField.class); - int order = 0; - - if (field.getAnnotation(ProtobufIgnore.class) != null || Modifier.isTransient(field.getModifiers())) { - continue; - } - - // protobuf 不支持除字节数组以外任何数组 - if (filedTypeName.startsWith("[")) { - if ((!filedTypeName.equals(byte[].class.getName())) && (!filedTypeName.equals(Byte[].class.getName()))) { - throw new RuntimeException("Array type of field '" + fieldName + "' on class '" - + field.getDeclaringClass().getName() + "' is not support, please use List instead."); - } - } - - ProtobufField protobufField = new ProtobufField(); - protobufField.parseListOrMap(field); - protobufField.setJavaField(field); - - ProtobufFieldTypeEnum protobufFieldType = ProtobufFieldTypeEnum.DEFAULT; - if (customizedField != null) { - order = customizedField.order(); - protobufFieldType = customizedField.protoBufFieldType(); - } - - // 如果不是自定义字段,则通过 javaType 和 protobufType 映射关系来决定 - if (protobufFieldType == ProtobufFieldTypeEnum.DEFAULT) { - if (protobufField.isList()) { - fieldType = protobufField.getGenericKeyType(); - } - if (fieldType == null) { - fieldType = Object.class; - } - - protobufFieldType = TYPE_MAPPER.get(fieldType); - if (protobufFieldType == null) { - if (Enum.class.isAssignableFrom(fieldType)) { - protobufFieldType = ProtobufFieldTypeEnum.ENUM; - } else if (protobufField.isMap()) { - protobufFieldType = ProtobufFieldTypeEnum.MAP; - } else { - protobufFieldType = ProtobufFieldTypeEnum.OBJECT; - } - } - - // 处理 zigzap 编码 - if (enableZigZap) { - if (protobufFieldType == ProtobufFieldTypeEnum.INT32) { - protobufFieldType = ProtobufFieldTypeEnum.SINT32; // to convert to sint32 to enable zagzip - } else if (protobufFieldType == ProtobufFieldTypeEnum.INT64) { - protobufFieldType = ProtobufFieldTypeEnum.SINT64; // to convert to sint64 to enable zagzip - } - } - } - protobufField.setProtobufFieldType(protobufFieldType); - - // 如果是自定义字段,则取自定义值中的order,否则记录到未排序的 list 中,等待后续处理 - if (order > 0) { - if (orders.contains(order)) { - throw new RuntimeException( - "order id '" + order + "' from field name '" + fieldName + "' is duplicate"); - } - orders.add(order); - protobufField.setOrder(order); - maxOrder = Math.max(maxOrder, order); - } else { - unOrderFields.add(protobufField); - } - - res.add(protobufField); - - // 如果使用 packed 注解则打包 - if (protobufField.isList() && (protobufField.getProtobufFieldType().isPrimitive() || protobufField.getProtobufFieldType().isEnum())) { - protobufField.setPacked(field.getAnnotation(ProtobufPacked.class) != null); - } - } - - if (unOrderFields.isEmpty()) { - return res; - } - - for (ProtobufField protobufField : unOrderFields) { - protobufField.setOrder(++maxOrder); - } - - return res; - } - - /** - * 在 clazz 上寻找字段名为 name 的字段 - * - * @param clazz - * @param name - * @return - */ - public static Field findField(Class clazz, String name) { - return findField(clazz, name, null); - } - - /** - * 在 clazz 上寻找字段名为 name、字段类型为 type 的字段 - * - * @param clazz - * @param name - * @param type - * @return - */ - public static Field findField(Class clazz, String name, Class type) { - if (clazz == null) { - throw new IllegalArgumentException("Class must not be null"); - } - if (name == null && type == null) { - throw new IllegalArgumentException( - "Either name or type of the field must be specified"); - } - Class searchType = clazz; - while (!Object.class.equals(searchType) && searchType != null) { - Field[] fields = searchType.getDeclaredFields(); - for (int i = 0; i < fields.length; i++) { - Field field = fields[i]; - if ((name == null || name.equals(field.getName())) - && (type == null || type.equals(field.getType()))) { - return field; - } - } - searchType = searchType.getSuperclass(); - } - return null; - } - - /** - * 获取对象 t 上的字段名为 name 的字段值 - * - * @param t - * @param name - * @return - */ - public static Object getField(Object t, String name) { - Field field = findField(t.getClass(), name, null); - if (field == null) { - return null; - } - field.setAccessible(true); - try { - return field.get(t); - } catch (Exception e) { - logger.error("ProtoBufUtil getFiled error, t:{}, name:{}", t, name, e); - } - return null; - } - - public static void setField(Object t, String name, Object value) { - Field field = findField(t.getClass(), name); - if (field == null) { - return; - } - field.setAccessible(true); - try { - field.set(t, value); - } catch (Exception e) { - logger.error("ProtoBufUtil setFiled error, t:{}, name:{}, value:{}", t, name, value, e); - } - } - - /** - * 读取 input put 进 map - * - * @param input - * @param map - * @param keyType - * @param defaultKey - * @param valueType - * @param defalutValue - * @param - * @param - * @throws IOException - */ - public static void putMapValue(CodedInputStream input, Map map, - com.google.protobuf.WireFormat.FieldType keyType, K defaultKey, - com.google.protobuf.WireFormat.FieldType valueType, V defalutValue) throws IOException { - putMapValue(input, map, keyType, defaultKey, valueType, defalutValue, null); - } - - public static void putMapValue(CodedInputStream input, Map map, - com.google.protobuf.WireFormat.FieldType keyType, K defaultKey, - com.google.protobuf.WireFormat.FieldType valueType, V defalutValue, EnumHandler handler) - throws IOException { - putMapValue(input, map, keyType, defaultKey, valueType, defalutValue, null, handler); - - } - - public static void putMapValue(CodedInputStream input, Map map, - com.google.protobuf.WireFormat.FieldType keyType, K defaultKey, - com.google.protobuf.WireFormat.FieldType valueType, V defalutValue, EnumHandler keyHandler, EnumHandler valHandler) - throws IOException { - MapEntry valuesDefaultEntry = MapEntry - .newDefaultInstance(null, keyType, defaultKey, valueType, defalutValue); - - MapEntry values = - input.readMessage(valuesDefaultEntry.getParserForType(), null); - - Object value = values.getValue(); - Object key = values.getKey(); - if (keyHandler != null) { - key = keyHandler.handle((int) key); - } - - if (valHandler != null) { - value = valHandler.handle((int) value); - } - map.put((K) key, (V) value); - } - - - public static String getGetterDynamicString(ProtobufField protobufField, Class dynamicTargetClass) { - Field field = protobufField.getJavaField(); - boolean wildcardType = protobufField.isWildcardType(); - - if (field.getModifiers() == Modifier.PUBLIC && !wildcardType) { - return DYNAMIC_TARGET + PACKAGE_SEPARATOR + field.getName(); - } - - String getter; - if ("boolean".equalsIgnoreCase(field.getType().getCanonicalName())) { - getter = "is" + capitalize(field.getName()); - } else { - getter = "get" + capitalize(field.getName()); - } - - try { - dynamicTargetClass.getMethod(getter, new Class[0]); - return DYNAMIC_TARGET + PACKAGE_SEPARATOR + getter + "()"; - } catch (Exception e) { - logger.error("ProtoBufUtil getGetterDynamicString error, protobufField:{}, dynamicTargetClass:{}", protobufField, dynamicTargetClass, e); - } - - String type = field.getType().getCanonicalName(); - if ("[B".equals(type) || "[Ljava.lang.Byte;".equals(type) || "java.lang.Byte[]".equals(type)) { - type = "byte[]"; - } - - String code = "(" + toObjectType(type) + ") "; - code += "ProtoBufUtil.getField(" + DYNAMIC_TARGET + ", \"" + field.getName() + "\")"; - - return code; - } - - /** - * 获取计算 size 动态字符串 - * - * @param field - * @return - */ - public static String getSizeDynamicString(ProtobufField field) { - ProtobufFieldTypeEnum protobufFieldType = field.getProtobufFieldType(); - int order = field.getOrder(); - boolean isList = field.isList(); - boolean isMap = field.isMap(); - String typeName = protobufFieldType.getType().toUpperCase(); - String dynamicFieldName = getDynamicFieldName(order); - - - if (isList) { - return "ProtoBufUtil.getListSize(" + order + "," + dynamicFieldName + "," + ProtobufFieldTypeEnum.class.getName() + "." + typeName - + "," + field.isPacked() + ");" + LINE_BREAK; - } else if (isMap) { - return "ProtoBufUtil.getMapSize(" + order + "," + dynamicFieldName + "," + getMapFieldGenericParameterString(field) + ");" + LINE_BREAK; - } - - if (protobufFieldType == ProtobufFieldTypeEnum.OBJECT) { - return "ProtoBufUtil.getObjectSize(" + order + "," + dynamicFieldName + ", " + ProtobufFieldTypeEnum.class.getName() + "." - + typeName + ");" + LINE_BREAK; - } - - String javaType = protobufFieldType.getType(); - if (protobufFieldType == ProtobufFieldTypeEnum.STRING) { - javaType = "String"; - } - - if (protobufFieldType == ProtobufFieldTypeEnum.BYTES) { - javaType = "ByteArray"; - } - javaType = capitalize(javaType); - dynamicFieldName = dynamicFieldName + protobufFieldType.getToPrimitiveType(); - return "com.google.protobuf.CodedOutputStream.compute" + javaType + "Size(" + order + "," + dynamicFieldName - + ");" + LINE_BREAK; - } - - /** - * 获取写入 CodedOutputStream 动态字符串 - * - * @param protobufField - * @return - */ - public static String getWriteByteDynamicString(ProtobufField protobufField) { - ProtobufFieldTypeEnum protobufFieldType = protobufField.getProtobufFieldType(); - int order = protobufField.getOrder(); - String dynamicFieldName = getDynamicFieldName(protobufField.getOrder()); - StringBuilder sb = new StringBuilder(); - sb.append("if (").append(dynamicFieldName).append(" != null){").append(LINE_BREAK); - - if (protobufField.isList()) { - String typeString = protobufFieldType.getType().toUpperCase(); - sb.append("ProtoBufUtil.writeList(").append(CODE_OUTPUT_STREAM_OBJ_NAME).append(","); - sb.append(order).append(",").append(ProtobufFieldTypeEnum.class.getName()).append(".").append(typeString); - sb.append(",").append(dynamicFieldName).append(",").append(Boolean.valueOf(protobufField.isPacked())).append(")") - .append(JAVA_LINE_BREAK).append("}").append(LINE_BREAK); - return sb.toString(); - } else if (protobufField.isMap()) { - sb.append("ProtoBufUtil.writeMap(").append(CODE_OUTPUT_STREAM_OBJ_NAME).append(","); - sb.append(order).append(",").append(dynamicFieldName); - - String joinedSentence = getMapFieldGenericParameterString(protobufField); - sb.append(",").append(joinedSentence); - - sb.append(")").append(JAVA_LINE_BREAK).append("}").append(LINE_BREAK); - return sb.toString(); - } else { - dynamicFieldName = dynamicFieldName + protobufFieldType.getToPrimitiveType(); - } - - if (protobufFieldType == ProtobufFieldTypeEnum.OBJECT) { - String typeString = protobufFieldType.getType().toUpperCase(); - sb.append("Field.writeObject(").append(CODE_OUTPUT_STREAM_OBJ_NAME).append(","); - sb.append(order).append(",").append(ProtobufFieldTypeEnum.class.getName()).append(".").append(typeString); - sb.append(",").append(dynamicFieldName).append(", false)").append(JAVA_LINE_BREAK).append("}") - .append(LINE_BREAK); - return sb.toString(); - } - - if (protobufFieldType == ProtobufFieldTypeEnum.STRING) { - sb.append(CODE_OUTPUT_STREAM_OBJ_NAME).append(".writeString(").append(order); - sb.append(", ").append(dynamicFieldName).append(")").append(JAVA_LINE_BREAK).append("}") - .append(LINE_BREAK); - return sb.toString(); - } - - if (protobufFieldType == ProtobufFieldTypeEnum.BYTES) { - sb.append(CODE_OUTPUT_STREAM_OBJ_NAME).append(".writeByteArray(").append(order); - sb.append(", ").append(dynamicFieldName).append(")").append(JAVA_LINE_BREAK).append("}") - .append(LINE_BREAK); - return sb.toString(); - } - - String t = protobufFieldType.getType(); - t = capitalize(t); - - sb.append(CODE_OUTPUT_STREAM_OBJ_NAME).append(".write").append(t).append("(").append(order); - sb.append(", ").append(dynamicFieldName).append(")").append(JAVA_LINE_BREAK).append("}") - .append(LINE_BREAK); - return sb.toString(); - } - - public static void writeList(CodedOutputStream out, int order, ProtobufFieldTypeEnum type, Collection list) - throws IOException { - writeList(out, order, type, list, false); - } - - /** - * java list 写入 CodedOutputStream - */ - public static void writeList(CodedOutputStream out, int order, ProtobufFieldTypeEnum type, Collection list, boolean packed) - throws IOException { - if (list == null || list.isEmpty()) { - return; - } - - CodedOutputStreamCache output = CodedOutputStreamCache.get(); - for (Object object : list) { - if (object == null) { - throw new NullPointerException("List can not include Null value."); - } - writeObject(output.getCodedOutputStream(), order, type, object, true, !packed); - } - byte[] byteArray = output.getData(); - - if (packed) { - out.writeUInt32NoTag(makeTag(order, WireFormat.WIRETYPE_LENGTH_DELIMITED)); - out.writeUInt32NoTag(byteArray.length); - } - - out.write(byteArray, 0, byteArray.length); - - } - - /** - * java map 写入 output - */ - public static void writeMap(CodedOutputStream output, int order, Map map, - com.google.protobuf.WireFormat.FieldType keyType, K defaultKey, - com.google.protobuf.WireFormat.FieldType valueType, V defalutValue) throws IOException { - MapEntry valuesDefaultEntry = MapEntry - .newDefaultInstance(null, keyType, defaultKey, valueType, defalutValue); - for (java.util.Map.Entry entry : map.entrySet()) { - MapEntry values = - valuesDefaultEntry.newBuilderForType().setKey(entry.getKey()).setValue(entry.getValue()).build(); - output.writeMessage(order, values); - } - } - - /** - * java object 写入 CodedOutputStream - */ - public static void writeObject(CodedOutputStream out, int order, ProtobufFieldTypeEnum type, Object o, boolean list, - boolean withTag) throws IOException { - if (o == null) { - return; - } - - if (type == ProtobufFieldTypeEnum.OBJECT) { - - Class cls = o.getClass(); - ProtobufCodec target = ProtobufProxy.create(cls); - - if (withTag) { - out.writeUInt32NoTag(makeTag(order, WireFormat.WIRETYPE_LENGTH_DELIMITED)); - } - - byte[] byteArray = target.encode(o); - out.writeUInt32NoTag(byteArray.length); - out.write(byteArray, 0, byteArray.length); - - return; - } - - if (type == ProtobufFieldTypeEnum.BOOL) { - if (withTag) { - out.writeBool(order, (Boolean) o); - } else { - out.writeBoolNoTag((Boolean) o); - } - } else if (type == ProtobufFieldTypeEnum.BYTES) { - byte[] bb = (byte[]) o; - if (withTag) { - out.writeBytes(order, ByteString.copyFrom(bb)); - } else { - out.writeBytesNoTag(ByteString.copyFrom(bb)); - } - } else if (type == ProtobufFieldTypeEnum.DOUBLE) { - if (withTag) { - out.writeDouble(order, (Double) o); - } else { - out.writeDoubleNoTag((Double) o); - } - } else if (type == ProtobufFieldTypeEnum.FIXED32) { - if (withTag) { - out.writeFixed32(order, (Integer) o); - } else { - out.writeFixed32NoTag((Integer) o); - } - } else if (type == ProtobufFieldTypeEnum.FIXED64) { - if (withTag) { - out.writeFixed64(order, (Long) o); - } else { - out.writeFixed64NoTag((Long) o); - } - } else if (type == ProtobufFieldTypeEnum.FLOAT) { - if (withTag) { - out.writeFloat(order, (Float) o); - } else { - out.writeFloatNoTag((Float) o); - } - } else if (type == ProtobufFieldTypeEnum.INT32) { - if (withTag) { - out.writeInt32(order, (Integer) o); - } else { - out.writeInt32NoTag((Integer) o); - } - } else if (type == ProtobufFieldTypeEnum.INT64) { - if (withTag) { - out.writeInt64(order, (Long) o); - } else { - out.writeInt64NoTag((Long) o); - } - } else if (type == ProtobufFieldTypeEnum.SFIXED32) { - if (withTag) { - out.writeSFixed32(order, (Integer) o); - } else { - out.writeSFixed32NoTag((Integer) o); - } - } else if (type == ProtobufFieldTypeEnum.SFIXED64) { - if (withTag) { - out.writeSFixed64(order, (Long) o); - } else { - out.writeSFixed64NoTag((Long) o); - } - } else if (type == ProtobufFieldTypeEnum.SINT32) { - if (withTag) { - out.writeSInt32(order, (Integer) o); - } else { - out.writeSInt32NoTag((Integer) o); - } - } else if (type == ProtobufFieldTypeEnum.SINT64) { - if (withTag) { - out.writeSInt64(order, (Long) o); - } else { - out.writeSInt64NoTag((Long) o); - } - } else if (type == ProtobufFieldTypeEnum.STRING) { - if (withTag) { - out.writeBytes(order, ByteString.copyFromUtf8(java.lang.String.valueOf(o))); - } else { - out.writeBytesNoTag(ByteString.copyFromUtf8(java.lang.String.valueOf(o))); - } - } else if (type == ProtobufFieldTypeEnum.UINT32) { - if (withTag) { - out.writeUInt32(order, (Integer) o); - } else { - out.writeUInt32NoTag((Integer) o); - } - } else if (type == ProtobufFieldTypeEnum.UINT64) { - if (withTag) { - out.writeUInt64(order, (Long) o); - } else { - out.writeUInt64NoTag((Long) o); - } - } else if (type == ProtobufFieldTypeEnum.ENUM) { - int value; - value = ((Enum) o).ordinal(); - if (withTag) { - out.writeEnum(order, value); - } else { - out.writeEnumNoTag(value); - } - } - } - - public static String getMapFieldGenericParameterString(ProtobufField field) { - String wireFormatClassName = WireFormat.FieldType.class.getCanonicalName(); - ProtobufFieldTypeEnum fieldType = TYPE_MAPPER.get(field.getGenericKeyType()); - String keyClass; - String defaultKeyValue; - if (fieldType == null) { - if (Enum.class.isAssignableFrom(field.getGenericKeyType())) { - keyClass = wireFormatClassName + ".ENUM"; - Class declaringClass = field.getGenericKeyType(); - Field[] fields = declaringClass.getFields(); - if (fields.length > 0) { - defaultKeyValue = field.getGenericKeyType().getCanonicalName() + "." - + fields[0].getName(); - } else { - defaultKeyValue = "0"; - } - - } else { - keyClass = wireFormatClassName + ".MESSAGE"; - boolean hasDefaultConstructor = hasDefaultConstructor(field.getGenericKeyType()); - if (!hasDefaultConstructor) { - throw new IllegalArgumentException("Class '" + field.getGenericKeyType().getCanonicalName() - + "' must has default constructor method with no parameters."); - } - defaultKeyValue = - "new " + field.getGenericKeyType().getCanonicalName() + "()"; - } - } else { - keyClass = wireFormatClassName + "." + fieldType.toString(); - - defaultKeyValue = fieldType.getDefaultValue(); - } - - fieldType = TYPE_MAPPER.get(field.getGenericValueType()); - String valueClass; - String defaultValueValue; - if (fieldType == null) { - if (Enum.class.isAssignableFrom(field.getGenericValueType())) { - valueClass = wireFormatClassName + ".ENUM"; - Class declaringClass = field.getGenericValueType(); - Field[] fields = declaringClass.getFields(); - if (fields.length > 0) { - defaultValueValue = field.getGenericValueType().getCanonicalName() - + "." + fields[0].getName(); - } else { - defaultValueValue = "0"; - } - - } else { - valueClass = wireFormatClassName + ".MESSAGE"; - // check constructor - boolean hasDefaultConstructor = hasDefaultConstructor(field.getGenericValueType()); - if (!hasDefaultConstructor) { - throw new IllegalArgumentException("Class '" + field.getGenericValueType().getCanonicalName() - + "' must has default constructor method with no parameters."); - } - defaultValueValue = - "new " + field.getGenericValueType().getCanonicalName() + "()"; - } - } else { - valueClass = wireFormatClassName + "." + fieldType; - defaultValueValue = fieldType.getDefaultValue(); - } - String joinedSentence = keyClass + "," + defaultKeyValue + "," + valueClass + "," + defaultValueValue; - return joinedSentence; - } - - public static boolean hasDefaultConstructor(Class cls) { - if (cls == null) { - return false; - } - try { - cls.getConstructor(new Class[0]); - } catch (NoSuchMethodException e) { - return false; - } catch (SecurityException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - return true; - } - - /** - * 通过 order 获取动态生成的字段名 - * - * @param order - * @return - */ - public static String getDynamicFieldName(int order) { - return "f_" + order; - } - - /** - * 获取 set 指定对象指定字段的方法 - * - * @param protobufField - * @param dynamicTargetClass - * @param express - * @return - */ - public static String getSetFieldDynamicString(ProtobufField protobufField, Class dynamicTargetClass, String express) { - StringBuilder sb = new StringBuilder(); - boolean isMap = protobufField.isMap(); - boolean isList = protobufField.isList(); - boolean isWildcardType = protobufField.isWildcardType(); - boolean isPacked = protobufField.isPacked(); - Field javaField = protobufField.getJavaField(); - - - if (isList || isMap) { - sb.append("if ((").append(getGetterDynamicString(protobufField, dynamicTargetClass)).append(") == null) {") - .append(LINE_BREAK); - } - - String collectionTypetoCreate = ""; - String collectionType = ""; - if (List.class.isAssignableFrom(javaField.getType())) { - collectionTypetoCreate = "new ArrayList()"; - collectionType = "List"; - } else if (Set.class.isAssignableFrom(javaField.getType())) { - collectionTypetoCreate = "new HashSet()"; - collectionType = "Set"; - } - - // if field of public modifier we can access directly - if (Modifier.isPublic(javaField.getModifiers()) && !isWildcardType) { - if (isList) { - // should initialize list - sb.append(DYNAMIC_TARGET).append(PACKAGE_SEPARATOR).append(javaField.getName()).append("= ") - .append(collectionTypetoCreate).append(JAVA_LINE_BREAK).append("}") - .append(LINE_BREAK); - if (express != null) { - if (isPacked) { - sb.append("while (input.getBytesUntilLimit() > 0) {").append(LINE_BREAK); - } - sb.append(DYNAMIC_TARGET).append(PACKAGE_SEPARATOR).append(javaField.getName()).append(".add(") - .append(express).append(")"); - if (isPacked) { - sb.append(";}").append(LINE_BREAK); - } - } - return sb.toString(); - } else if (isMap) { - sb.append(DYNAMIC_TARGET).append(PACKAGE_SEPARATOR).append(javaField.getName()) - .append("= new HashMap()").append(JAVA_LINE_BREAK).append("}") - .append(LINE_BREAK); - return sb.append(express).toString(); - } - // if date type - if (javaField.getType().equals(Date.class)) { - express = "new Date(" + express + ")"; - } - return DYNAMIC_TARGET + PACKAGE_SEPARATOR + javaField.getName() + "=" + express + LINE_BREAK; - } - String setter = "set" + capitalize(javaField.getName()); - // check method exist - try { - dynamicTargetClass.getMethod(setter, new Class[]{javaField.getType()}); - if (isList) { - sb.append(collectionType).append(" __list = ").append(collectionTypetoCreate) - .append(JAVA_LINE_BREAK); - sb.append(DYNAMIC_TARGET).append(PACKAGE_SEPARATOR).append(setter).append("(__list)") - .append(JAVA_LINE_BREAK).append("}").append(LINE_BREAK); - - if (express != null) { - if (isPacked) { - sb.append("while (input.getBytesUntilLimit() > 0) {").append(LINE_BREAK); - } - sb.append("(").append(getGetterDynamicString(protobufField, dynamicTargetClass)).append(").add(") - .append(express).append(")"); - if (isPacked) { - sb.append(";}").append(LINE_BREAK); - } - } - return sb.toString(); - } else if (isMap) { - sb.append("Map __map = new HashMap()").append(JAVA_LINE_BREAK); - sb.append(DYNAMIC_TARGET).append(PACKAGE_SEPARATOR).append(setter).append("(__map)") - .append(JAVA_LINE_BREAK).append("}").append(LINE_BREAK); - return sb + express; - } - - // fix date type - if (javaField.getType().equals(Date.class)) { - express = "new Date(" + express + ")"; - } - - return DYNAMIC_TARGET + PACKAGE_SEPARATOR + setter + "(" + express + ")\n"; - } catch (Exception e) { - logger.error("ProtoBufUtil getSetFieldDynamicString error, protobufField:{}, dynamicTargetClass:{}, express:{}", protobufField, dynamicTargetClass, express, e); - } - - if (isList) { - sb.append(collectionType).append(" __list = ").append(collectionTypetoCreate) - .append(JAVA_LINE_BREAK); - sb.append("ProtoBufUtil.setField(").append(DYNAMIC_TARGET).append(", \"").append(javaField.getName()) - .append("\", __list)").append(JAVA_LINE_BREAK).append("}").append(LINE_BREAK); - if (express != null) { - if (isPacked) { - sb.append("while (input.getBytesUntilLimit() > 0) {").append(LINE_BREAK); - } - sb.append("(").append(getGetterDynamicString(protobufField, dynamicTargetClass)).append(").add(") - .append(express).append(")"); - if (isPacked) { - sb.append(";}").append(LINE_BREAK); - } - } - return sb.toString(); - } else if (isMap) { - sb.append("Map __map = new HashMap()").append(JAVA_LINE_BREAK); - sb.append("ProtoBufUtil.setField(").append(DYNAMIC_TARGET).append(", \"").append(javaField.getName()) - .append("\", __map)").append(JAVA_LINE_BREAK).append("}").append(LINE_BREAK); - return sb + express; - } - - // use reflection to get value - String code = ""; - if (express != null) { - // if date type - if (javaField.getType().equals(Date.class)) { - express = "new Date(" + express + ")"; - } - - code = "ProtoBufUtil.setField(" + DYNAMIC_TARGET + ", \"" + javaField.getName() + "\", " + express + ")" - + LINE_BREAK; - } - return code; - } - - public static int getEnumOrdinal(Enum en) { - if (en != null) { - return en.ordinal(); - } - return -1; - } - - public static > T getEnumValue(Class enumType, String name) { - if (isEmpty(name)) { - return null; - } - - try { - T v = Enum.valueOf(enumType, name); - return v; - } catch (IllegalArgumentException e) { - return null; - } - } - - public static String getEnumName(Enum[] e, int value) { - if (e != null) { - int toCompareValue; - for (Enum en : e) { - toCompareValue = en.ordinal(); - if (value == toCompareValue) { - return en.name(); - } - } - } - return ""; - } - - /** - * 获取初始化 list、map 字段的动态字符串 - * - * @param protobufField - * @return - */ - public static String getInitListMapFieldDynamicString(ProtobufField protobufField, String express) { - return "ProtoBufUtil.setField(" + DYNAMIC_TARGET + ", \"" + protobufField.getJavaField().getName() + "\", " + express + ");" - + LINE_BREAK; - } - - - public static int getListSize(int order, Collection list, ProtobufFieldTypeEnum type, boolean packed) { - int size = 0; - if (list == null || list.isEmpty()) { - return size; - } - - int dataSize = 0; - for (Object object : list) { - dataSize += getObjectSize(order, object, type); - } - size += dataSize; - if (type != ProtobufFieldTypeEnum.OBJECT) { - if (packed) { - size += com.google.protobuf.CodedOutputStream.computeInt32SizeNoTag(dataSize); - int tag = makeTag(order, WireFormat.WIRETYPE_LENGTH_DELIMITED); - size += com.google.protobuf.CodedOutputStream.computeUInt32SizeNoTag(tag); - } else { - size += list.size() * CodedOutputStream.computeTagSize(order); - } - } - return size; - } - - public static int getMapSize(int order, Map map, com.google.protobuf.WireFormat.FieldType keyType, - K defaultKey, com.google.protobuf.WireFormat.FieldType valueType, V defalutValue) { - int size = 0; - for (java.util.Map.Entry entry : map.entrySet()) { - MapEntry valuesDefaultEntry = MapEntry - .newDefaultInstance(null, keyType, defaultKey, valueType, defalutValue); - - MapEntry values = - valuesDefaultEntry.newBuilderForType().setKey(entry.getKey()).setValue(entry.getValue()).build(); - - size += com.google.protobuf.CodedOutputStream.computeMessageSize(order, values); - } - return size; - } - - /** - * 获取 object protobuf size - * - * @param order - * @param object - * @param type - * @return - */ - public static int getObjectSize(int order, Object object, ProtobufFieldTypeEnum type) { - int size = 0; - if (object == null) { - return size; - } - - if (type == ProtobufFieldTypeEnum.OBJECT) { - try { - Class cls = object.getClass(); - ProtobufCodec target = ProtobufProxy.create(cls); - size = target.size(object); - size = size + CodedOutputStream.computeRawVarint32Size(size); - return size + CodedOutputStream.computeTagSize(order); - } catch (Exception e) { - throw new RuntimeException(e.getMessage(), e); - } - } - - if (type == ProtobufFieldTypeEnum.STRING) { - size = CodedOutputStream.computeStringSizeNoTag(java.lang.String.valueOf(object)); - } else if (type == ProtobufFieldTypeEnum.BOOL) { - size = CodedOutputStream.computeBoolSizeNoTag(Boolean.valueOf(java.lang.String.valueOf(object))); - } else if (type == ProtobufFieldTypeEnum.BYTES) { - byte[] bb = (byte[]) object; - size = CodedOutputStream.computeBytesSizeNoTag(ByteString.copyFrom(bb)); - } else if (type == ProtobufFieldTypeEnum.DOUBLE) { - size = CodedOutputStream.computeDoubleSizeNoTag(Double.valueOf(object.toString())); - } else if (type == ProtobufFieldTypeEnum.FIXED32 || type == ProtobufFieldTypeEnum.SFIXED32) { - size = CodedOutputStream.computeFixed32SizeNoTag(Integer.valueOf(object.toString())); - } else if (type == ProtobufFieldTypeEnum.INT32 || type == ProtobufFieldTypeEnum.SINT32 || type == ProtobufFieldTypeEnum.UINT32) { - size = CodedOutputStream.computeInt32SizeNoTag(Integer.valueOf(object.toString())); - } else if (type == ProtobufFieldTypeEnum.FIXED64 || type == ProtobufFieldTypeEnum.SFIXED64) { - size = CodedOutputStream.computeSFixed64SizeNoTag(Long.valueOf(object.toString())); - } else if (type == ProtobufFieldTypeEnum.INT64 || type == ProtobufFieldTypeEnum.SINT64 || type == ProtobufFieldTypeEnum.UINT64) { - size = CodedOutputStream.computeInt64SizeNoTag(Long.valueOf(object.toString())); - } else if (type == ProtobufFieldTypeEnum.FLOAT) { - size = CodedOutputStream.computeFloatSizeNoTag(Float.valueOf(object.toString())); - } else if (type == ProtobufFieldTypeEnum.ENUM) { - size = CodedOutputStream.computeInt32SizeNoTag(((Enum) object).ordinal()); - } - return size; - } - - /** - * 首字母大写 - * - * @param str - * @return - */ - public static String capitalize(String str) { - if (str == null || str.isEmpty()) { - return str; - } - return Character.toTitleCase(str.charAt(0)) + str.substring(1); - } - - /** - * 生成 protobuf tag - * - * @param fieldNumber - * @param wireType - * @return - */ - public static int makeTag(final int fieldNumber, final int wireType) { - return (fieldNumber << 3) | wireType; - } - - /** - * 基础类型转为包装对象 - * - * @param primitiveType - * @return - */ - public static String toObjectType(String primitiveType) { - if (PRIMITIVE_TYPE_MAPPING.containsKey(primitiveType)) { - return PRIMITIVE_TYPE_MAPPING.get(primitiveType); - } - return primitiveType; - } - - public static String getFullClassName(Class cls) { - if (isEmpty(getPackage(cls))) { - return getClassName(cls); - } - - return getPackage(cls) + PACKAGE_SEPARATOR + getClassName(cls); - } - - public static String getPackage(Class cls) { - Package pkg = cls.getPackage(); - // maybe null if package is blank or dynamic load class - if (pkg == null) { - String fullName = cls.getName(); - int index = fullName.lastIndexOf(PACKAGE_SEPARATOR); - if (index != -1) { - return fullName.substring(0, index); - } - return ""; - } - - return pkg.getName(); - } - - public static String getClassName(Class cls) { - if (cls.isMemberClass()) { - String name = cls.getName(); - name = substringAfterLast(name, PACKAGE_SEPARATOR); - return name; - } - - return cls.getSimpleName(); - } - - public static boolean isEmpty(String s) { - return s == null || s.isEmpty(); - } - - public static String substringAfterLast(String str, String separator) { - if (isEmpty(str)) { - return str; - } else if (isEmpty(separator)) { - return ""; - } else { - int pos = str.lastIndexOf(separator); - return pos != -1 && pos != str.length() - separator.length() ? str.substring(pos + separator.length()) : ""; - } - } - - public static Class forNameWithCallerClassLoader(String name, Class caller) throws ClassNotFoundException { - return forName(name, caller.getClassLoader()); - } - - public static Class forName(String name, ClassLoader classLoader) throws ClassNotFoundException, LinkageError { - - Class clazz = resolvePrimitiveClassName(name); - if (clazz != null) { - return clazz; - } - - // "java.lang.String[]" style arrays - if (name.endsWith(ARRAY_SUFFIX)) { - String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length()); - Class elementClass = forName(elementClassName, classLoader); - return Array.newInstance(elementClass, 0).getClass(); - } - - // "[Ljava.lang.String;" style arrays - int internalArrayMarker = name.indexOf(INTERNAL_ARRAY_PREFIX); - if (internalArrayMarker != -1 && name.endsWith(";")) { - String elementClassName = null; - if (internalArrayMarker == 0) { - elementClassName = name.substring(INTERNAL_ARRAY_PREFIX.length(), name.length() - 1); - } else if (name.startsWith("[")) { - elementClassName = name.substring(1); - } - Class elementClass = forName(elementClassName, classLoader); - return Array.newInstance(elementClass, 0).getClass(); - } - - ClassLoader classLoaderToUse = classLoader; - if (classLoaderToUse == null) { - classLoaderToUse = getClassLoader(); - } - return classLoaderToUse.loadClass(name); - } - - public static Class resolvePrimitiveClassName(String name) { - Class result = null; - // Most class names will be quite long, considering that they - // SHOULD sit in a package, so a length check is worthwhile. - if (name != null && name.length() <= 8) { - // Could be a primitive - likely. - result = (Class) PRIMIIIVE_TYPE_CLASS_MAPPING.get(name); - } - return result; - } - - public static ClassLoader getClassLoader() { - return getClassLoader(ProtoBufUtil.class); - } - - public static ClassLoader getClassLoader(Class cls) { - ClassLoader cl = null; - try { - cl = Thread.currentThread().getContextClassLoader(); - } catch (Throwable ex) { - // Cannot access thread context ClassLoader - falling back to system - // class loader... - } - if (cl == null) { - // No thread context class loader -> use class loader of this class. - cl = cls.getClassLoader(); - } - return cl; - } - - public static URI toURI(String name) { - try { - return new URI(name); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - - public static String toString(Throwable e) { - StringWriter w = new StringWriter(); - PrintWriter p = new PrintWriter(w); - p.print(e.getClass().getName() + ": "); - if (e.getMessage() != null) { - p.print(e.getMessage() + "\n"); - } - p.println(); - try { - e.printStackTrace(p); - return w.toString(); - } finally { - p.close(); - } - } - - public static boolean isNull(Object o) { - return o == null; - } - - public static boolean isNull(double o) { - return false; - } - - public static boolean isNull(int o) { - return false; - } - - public static boolean isNull(byte o) { - return false; - } - - public static boolean isNull(short o) { - return false; - } - - public static boolean isNull(long o) { - return false; - } - - public static boolean isNull(float o) { - return false; - } - - public static boolean isNull(char o) { - return false; - } -} diff --git a/arthas-grpc-server/src/main/proto/arthasUnittest.proto b/arthas-grpc-server/src/main/proto/arthasUnittest.proto index b615b056aa..81282b11cd 100644 --- a/arthas-grpc-server/src/main/proto/arthasUnittest.proto +++ b/arthas-grpc-server/src/main/proto/arthasUnittest.proto @@ -5,12 +5,18 @@ package arthas.grpc.unittest; service ArthasUnittestService { rpc trace(ArthasUnittestRequest) returns (ArthasUnittestResponse); rpc watch(stream ArthasUnittestRequest) returns (stream ArthasUnittestResponse); + rpc addSum(ArthasUnittestRequest) returns (ArthasUnittestResponse); + rpc getSum(ArthasUnittestRequest) returns (ArthasUnittestResponse); } message ArthasUnittestRequest { - string message = 1; + int32 id = 1; + string message = 2; + int32 num = 3; } message ArthasUnittestResponse{ - string message = 1; + int32 id = 1; + string message = 2; + int32 num = 3; } diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java index 5c3f1d4195..294b33d303 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java @@ -11,9 +11,14 @@ import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import java.util.Random; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; /** * @author: FengYe @@ -27,6 +32,8 @@ public class GrpcTest { private static final String UNIT_TEST_GRPC_SERVICE_PACKAGE_NAME = "unittest.grpc.service.impl"; private ArthasUnittestServiceGrpc.ArthasUnittestServiceBlockingStub blockingStub = null; private ArthasUnittestServiceGrpc.ArthasUnittestServiceStub stub = null; + Random random; + ExecutorService threadPool; @Before public void startServer() { @@ -35,6 +42,8 @@ public void startServer() { arthasGrpcServer.start(); }); grpcWebProxyStart.start(); + random = new Random(); + threadPool = Executors.newFixedThreadPool(10); } @Test @@ -67,13 +76,37 @@ public void testStream() { } } + @Test + public void testSum() throws InterruptedException { + ManagedChannel channel = ManagedChannelBuilder.forTarget(HOST_PORT) + .usePlaintext() + .build(); + + blockingStub = ArthasUnittestServiceGrpc.newBlockingStub(channel); + for (int i = 0; i < 10; i++) { + AtomicInteger sum = new AtomicInteger(0); + int finalId = i; + for (int j = 0; j < 100; j++) { + int num = random.nextInt(101); + sum.addAndGet(num); + threadPool.submit(() -> { + addSum(finalId, num); + }); + } + Thread.sleep(1000); + int grpcSum = getSum(finalId); + System.out.println("id:" + finalId + ",sum:" + sum.get() + ",grpcSum:" + grpcSum); + Assert.assertEquals(sum.get(), grpcSum); + } + } + private void trace(String name) { ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.newBuilder().setMessage(name).build(); ArthasUnittest.ArthasUnittestResponse res = blockingStub.trace(request); System.out.println(res.getMessage()); } - private void watch(String... names){ + private void watch(String... names) { // 使用 CountDownLatch 来等待所有响应 CountDownLatch finishLatch = new CountDownLatch(1); @@ -115,4 +148,15 @@ public void onCompleted() { e.printStackTrace(); } } + + private void addSum(int id, int num) { + ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.newBuilder().setId(id).setNum(num).build(); + ArthasUnittest.ArthasUnittestResponse res = blockingStub.addSum(request); + } + + private int getSum(int id) { + ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.newBuilder().setId(id).build(); + ArthasUnittest.ArthasUnittestResponse res = blockingStub.getSum(request); + return res.getNum(); + } } diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java index d9f17088b9..bf4f40575f 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java @@ -10,4 +10,6 @@ public interface ArthasSampleService { ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittestRequest command); ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittestRequest command); + ArthasUnittest.ArthasUnittestResponse addSum(ArthasUnittest.ArthasUnittestRequest command); + ArthasUnittest.ArthasUnittestResponse getSum(ArthasUnittest.ArthasUnittestRequest command); } diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java index e7cc9d4df0..c2a738dd55 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java @@ -5,6 +5,9 @@ import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; import unittest.grpc.service.ArthasSampleService; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + /** * @author: FengYe * @date: 2024/6/30 下午11:43 @@ -13,6 +16,8 @@ @GrpcService("arthas.grpc.unittest.ArthasUnittestService") public class ArthasSampleServiceImpl implements ArthasSampleService { + private ConcurrentHashMap map = new ConcurrentHashMap<>(); + @Override @GrpcMethod("trace") public ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittestRequest command) { @@ -33,4 +38,23 @@ public ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittest builder.setMessage(command.getMessage()); return builder.build(); } + + @Override + @GrpcMethod(value = "addSum") + public ArthasUnittest.ArthasUnittestResponse addSum(ArthasUnittest.ArthasUnittestRequest command) { + ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); + builder.setMessage(command.getMessage()); + map.merge(command.getId(), command.getNum(), Integer::sum); + return builder.build(); + } + + @Override + @GrpcMethod(value = "getSum") + public ArthasUnittest.ArthasUnittestResponse getSum(ArthasUnittest.ArthasUnittestRequest command) { + ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); + builder.setMessage(command.getMessage()); + Integer sum = map.getOrDefault(command.getId(), 0); + builder.setNum(sum); + return builder.build(); + } } \ No newline at end of file diff --git a/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTest.java b/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTest.java deleted file mode 100644 index 915457a210..0000000000 --- a/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package unittest.protobuf; - -import com.taobao.arthas.grpc.server.protobuf.ProtobufCodec; -import com.taobao.arthas.grpc.server.protobuf.ProtobufProxy; -import org.junit.Assert; -import org.junit.Test; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @author: FengYe - * @date: 2024/9/19 22:35 - * @description: protobuf.ProtoBufTest - */ -public class ProtoBufTest { - @Test - public void testEncodeAndDecode() { - ProtoBufTestReq protoBufTestReq = new ProtoBufTestReq(); - protoBufTestReq.setName("test"); - protoBufTestReq.setAge(18); - protoBufTestReq.setPrice(100L); - protoBufTestReq.setStatus(ProtoBufTestReq.StatusEnum.START); - List list = new ArrayList<>(); - list.add(new ProtoBufTestReq.TestClass("test1")); - list.add(new ProtoBufTestReq.TestClass("test2")); - list.add(new ProtoBufTestReq.TestClass("test3")); - Map map = new HashMap<>(); - map.put("key1","value1"); - map.put("key2","value2"); - map.put("key3","value3"); - protoBufTestReq.setTestList(list); - protoBufTestReq.setTestMap(map); - - try { - ProtobufCodec protobufCodec = ProtobufProxy.getCodecCacheSide(ProtoBufTestReq.class); - byte[] encode = protobufCodec.encode(protoBufTestReq); - ProtoBufTestReq decode = protobufCodec.decode(encode); - Assert.assertEquals(protoBufTestReq.toString(), decode.toString()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTestReq.java b/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTestReq.java deleted file mode 100644 index 6c27901da0..0000000000 --- a/arthas-grpc-server/src/test/java/unittest/protobuf/ProtoBufTestReq.java +++ /dev/null @@ -1,124 +0,0 @@ -package unittest.protobuf; - -import com.taobao.arthas.grpc.server.protobuf.annotation.ProtobufClass; - -import java.util.List; -import java.util.Map; - -/** - * @author: FengYe - * @date: 2024/9/19 22:38 - * @description: ProtoBufTestReq - */ -@ProtobufClass -public class ProtoBufTestReq { - private String name; - private double age; - private long price; - private ProtoBufTestReq.StatusEnum status; - private List testList; - private Map testMap; - - @ProtobufClass - public enum StatusEnum { - START(1, "开始"), - STOP(2, "结束"); - - StatusEnum(int code, String desc) { - this.code = code; - this.desc = desc; - } - - private int code; - private String desc; - } - - @ProtobufClass - public static class TestClass { - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - // 注意被 @ProtobufClass 注解的 class 必须添加无参构造函数 - public TestClass() { - } - - public TestClass(String name) { - this.name = name; - } - - @Override - public String toString() { - return "TestClass{" + - "name='" + name + '\'' + - '}'; - } - } - - public List getTestList() { - return testList; - } - - public void setTestList(List testList) { - this.testList = testList; - } - - public ProtoBufTestReq.StatusEnum getStatus() { - return status; - } - - public void setStatus(ProtoBufTestReq.StatusEnum status) { - this.status = status; - } - - public long getPrice() { - return price; - } - - public void setPrice(long price) { - this.price = price; - } - - public double getAge() { - return age; - } - - public void setAge(double age) { - this.age = age; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Map getTestMap() { - return testMap; - } - - public void setTestMap(Map testMap) { - this.testMap = testMap; - } - - @Override - public java.lang.String toString() { - return "ProtoBufTestReq{" + - "name='" + name + '\'' + - ", age=" + age + - ", price=" + price + - ", status=" + status + - ", testList=" + testList + - ", testMap=" + testMap + - '}'; - } -} - From 7ae26febc089e73c1d52303741b3d66284971eb1 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Thu, 24 Oct 2024 02:40:49 +0800 Subject: [PATCH 42/48] update: Refactored the gRPC execution part, dividing it into four types of call methods; added StreamObserver model, pending implementation --- .../arthas/grpc/server/ArthasGrpcServer.java | 5 +- .../grpc/server/handler/GrpcDispatcher.java | 17 ++-- .../grpc/server/handler/GrpcRequest.java | 14 +++ .../grpc/server/handler/Http2Handler.java | 89 ++++++------------- .../grpc/server/handler/StreamObserver.java | 15 ++++ .../server/handler/annotation/GrpcMethod.java | 4 + .../handler/constant/GrpcCallTypeEnum.java | 13 +++ .../executor/AbstractGrpcExecutor.java | 16 ++++ .../handler/executor/BiStreamExecutor.java | 54 +++++++++++ .../executor/ClientStreamExecutor.java | 29 ++++++ .../server/handler/executor/GrpcExecutor.java | 17 ++++ .../handler/executor/GrpcExecutorFactory.java | 55 ++++++++++++ .../executor/ServerStreamExecutor.java | 29 ++++++ .../handler/executor/UnaryExecutor.java | 38 ++++++++ .../service/impl/ArthasSampleServiceImpl.java | 3 +- .../src/main/proto/arthasUnittest.proto | 6 +- .../src/test/java/unittest/grpc/GrpcTest.java | 4 +- .../grpc/service/ArthasSampleService.java | 4 +- .../service/impl/ArthasSampleServiceImpl.java | 11 +-- 19 files changed, 344 insertions(+), 79 deletions(-) create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/StreamObserver.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/constant/GrpcCallTypeEnum.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutor.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutorFactory.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java create mode 100644 arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/UnaryExecutor.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java index f46f4dda5b..a931f54284 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java @@ -6,6 +6,7 @@ import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; import com.taobao.arthas.grpc.server.handler.Http2Handler; +import com.taobao.arthas.grpc.server.handler.executor.GrpcExecutorFactory; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; @@ -44,6 +45,8 @@ public void start() { GrpcDispatcher grpcDispatcher = new GrpcDispatcher(); grpcDispatcher.loadGrpcService(grpcServicePackageName); + GrpcExecutorFactory grpcExecutorFactory = new GrpcExecutorFactory(); + grpcExecutorFactory.loadExecutor(grpcDispatcher); try { ServerBootstrap b = new ServerBootstrap(); @@ -54,7 +57,7 @@ public void start() { @Override public void initChannel(SocketChannel ch) { ch.pipeline().addLast(Http2FrameCodecBuilder.forServer().build()); - ch.pipeline().addLast(new Http2Handler(grpcDispatcher)); + ch.pipeline().addLast(new Http2Handler(grpcDispatcher, grpcExecutorFactory)); } }); Channel channel = b.bind(port).sync().channel(); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java index 087830750f..ff86a3946f 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java @@ -1,8 +1,11 @@ package com.taobao.arthas.grpc.server.handler; +import com.alibaba.arthas.deps.org.slf4j.Logger; +import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; +import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; import com.taobao.arthas.grpc.server.utils.ReflectUtil; import java.lang.invoke.MethodHandle; @@ -21,6 +24,8 @@ */ public class GrpcDispatcher { + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass().getName()); + public static final String DEFAULT_GRPC_SERVICE_PACKAGE_NAME = "com.taobao.arthas.grpc.server.service.impl"; public static Map grpcMethodInvokeMap = new HashMap<>(); @@ -31,7 +36,7 @@ public class GrpcDispatcher { public static Map responseParseFromMap = new HashMap<>(); public static Map responseToByteArrayMap = new HashMap<>(); - public static Map grpcMethodStreamMap = new HashMap<>(); + public static Map grpcMethodStreamMap = new HashMap<>(); public void loadGrpcService(String grpcServicePackageName) { List> classes = ReflectUtil.findClasses(Optional.ofNullable(grpcServicePackageName).orElse(DEFAULT_GRPC_SERVICE_PACKAGE_NAME)); @@ -57,7 +62,7 @@ public void loadGrpcService(String grpcServicePackageName) { MethodHandle responseToByteArray = lookup.findVirtual(responseClass, "toByteArray", MethodType.methodType(byte[].class)); String grpcMethodKey = generateGrpcMethodKey(grpcService.value(), grpcMethod.value()); grpcMethodInvokeMap.put(grpcMethodKey, grpcInvoke.bindTo(instance)); - grpcMethodStreamMap.put(grpcMethodKey, grpcMethod.stream()); + grpcMethodStreamMap.put(grpcMethodKey, grpcMethod.grpcType()); requestParseFromMap.put(grpcMethodKey, requestParseFrom); responseParseFromMap.put(grpcMethodKey, responseParseFrom); requestToByteArrayMap.put(grpcMethodKey, requestToByteArray); @@ -65,7 +70,7 @@ public void loadGrpcService(String grpcServicePackageName) { } } } catch (Exception e) { - e.printStackTrace(); + logger.error("GrpcDispatcher loadGrpcService error.", e); } } } @@ -106,10 +111,10 @@ public static String generateGrpcMethodKey(String serviceName, String methodName return serviceName + "." + methodName; } - public static void checkGrpcStream(GrpcRequest request) { - request.setStream( + public static void checkGrpcType(GrpcRequest request) { + request.setGrpcType( Optional.ofNullable(grpcMethodStreamMap.get(generateGrpcMethodKey(request.getService(), request.getMethod()))) - .orElse(false) + .orElse(GrpcCallTypeEnum.UNARY) ); request.setStreamFirstData(true); } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java index 5deb390f88..eb41590feb 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java @@ -1,5 +1,6 @@ package com.taobao.arthas.grpc.server.handler; +import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; import com.taobao.arthas.grpc.server.utils.ByteUtil; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http2.Http2Headers; @@ -62,6 +63,11 @@ public class GrpcRequest { */ private Http2Headers headers; + /** + * grpc 调用类型 + */ + private GrpcCallTypeEnum grpcType; + public GrpcRequest(Integer streamId, String path, String method) { this.streamId = streamId; @@ -172,4 +178,12 @@ public Http2Headers getHeaders() { public void setHeaders(Http2Headers headers) { this.headers = headers; } + + public GrpcCallTypeEnum getGrpcType() { + return grpcType; + } + + public void setGrpcType(GrpcCallTypeEnum grpcType) { + this.grpcType = grpcType; + } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java index 6572c8c68a..4b2776abe1 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java @@ -4,6 +4,7 @@ import arthas.grpc.common.ArthasGrpc; import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; +import com.taobao.arthas.grpc.server.handler.executor.GrpcExecutorFactory; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; @@ -14,7 +15,6 @@ import java.io.*; import java.lang.invoke.MethodHandles; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.ReentrantLock; /** * @author: FengYe @@ -27,6 +27,8 @@ public class Http2Handler extends SimpleChannelInboundHandler { private GrpcDispatcher grpcDispatcher; + private GrpcExecutorFactory grpcExecutorFactory; + private final EventExecutorGroup executorGroup = new NioEventLoopGroup(); /** @@ -36,8 +38,9 @@ public class Http2Handler extends SimpleChannelInboundHandler { private static final String HEADER_PATH = ":path"; - public Http2Handler(GrpcDispatcher grpcDispatcher) { + public Http2Handler(GrpcDispatcher grpcDispatcher, GrpcExecutorFactory grpcExecutorFactory) { this.grpcDispatcher = grpcDispatcher; + this.grpcExecutorFactory = grpcExecutorFactory; } @Override @@ -69,7 +72,7 @@ private void handleGrpcRequest(Http2HeadersFrame headersFrame, ChannelHandlerCon String[] parts = path.substring(1).split("/"); GrpcRequest grpcRequest = new GrpcRequest(headersFrame.stream().id(), parts[0], parts[1]); grpcRequest.setHeaders(headersFrame.headers()); - GrpcDispatcher.checkGrpcStream(grpcRequest); + GrpcDispatcher.checkGrpcType(grpcRequest); dataBuffer.put(id, grpcRequest); System.out.println("Received headers: " + headersFrame.headers()); } @@ -81,62 +84,28 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) grpcRequest.writeData(content); executorGroup.execute(() -> { - if (grpcRequest.isStream()) { - // 流式调用,即刻响应 - try { - GrpcResponse response = new GrpcResponse(); - byte[] bytes = grpcRequest.readData(); - while (bytes != null) { - response = grpcDispatcher.execute(grpcRequest.getService(), grpcRequest.getMethod(), bytes); - - // 针对第一个响应发送 header - if (grpcRequest.isStreamFirstData()) { - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(dataFrame.stream())); - grpcRequest.setStreamFirstData(false); - } - ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(dataFrame.stream())); - - bytes = grpcRequest.readData(); - } - - grpcRequest.clearData(); - - if (dataFrame.isEndStream()) { - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(dataFrame.stream())); - } - } catch (Throwable e) { - processError(ctx, e, dataFrame.stream()); - } - } else { - // 非流式调用,等到 endStream 再响应 - if (dataFrame.isEndStream()) { - try { - GrpcResponse response = grpcDispatcher.execute(grpcRequest); - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(dataFrame.stream())); - ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(dataFrame.stream())); - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(dataFrame.stream())); - } catch (Throwable e) { - processError(ctx, e, dataFrame.stream()); - } - } + try { + grpcExecutorFactory.getExecutor(grpcRequest.getGrpcType()).execute(grpcRequest, dataFrame, ctx); + } catch (Throwable e) { + processError(ctx, e, dataFrame.stream()); } - }); - } - - private void handleResetStream(Http2ResetFrame resetFrame, ChannelHandlerContext ctx) { - int id = resetFrame.stream().id(); - System.out.println("handleResetStream"); - dataBuffer.remove(id); - } - - private void processError(ChannelHandlerContext ctx, Throwable e, Http2FrameStream stream) { - GrpcResponse response = new GrpcResponse(); - ArthasGrpc.ErrorRes.Builder builder = ArthasGrpc.ErrorRes.newBuilder(); - ArthasGrpc.ErrorRes errorRes = builder.setErrorMsg(e.getMessage()).build(); - response.setClazz(ArthasGrpc.ErrorRes.class); - response.writeResponseData(errorRes); - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(stream)); - ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(stream)); - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(stream)); - } + }); +} + +private void handleResetStream(Http2ResetFrame resetFrame, ChannelHandlerContext ctx) { + int id = resetFrame.stream().id(); + System.out.println("handleResetStream"); + dataBuffer.remove(id); +} + +private void processError(ChannelHandlerContext ctx, Throwable e, Http2FrameStream stream) { + GrpcResponse response = new GrpcResponse(); + ArthasGrpc.ErrorRes.Builder builder = ArthasGrpc.ErrorRes.newBuilder(); + ArthasGrpc.ErrorRes errorRes = builder.setErrorMsg(e.getMessage()).build(); + response.setClazz(ArthasGrpc.ErrorRes.class); + response.writeResponseData(errorRes); + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(stream)); + ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(stream)); + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(stream)); +} } \ No newline at end of file diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/StreamObserver.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/StreamObserver.java new file mode 100644 index 0000000000..6cabf638bc --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/StreamObserver.java @@ -0,0 +1,15 @@ +package com.taobao.arthas.grpc.server.handler; + +/** + * @author: FengYe + * @date: 2024/10/24 00:22 + * @description: StreamObserver + */ +public interface StreamObserver { + + void onNext(V value); + + void onError(Throwable t); + + void onCompleted(); +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java index 10f1c9b724..14bef63bfa 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java @@ -1,5 +1,7 @@ package com.taobao.arthas.grpc.server.handler.annotation; +import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -16,4 +18,6 @@ String value() default ""; boolean stream() default false; + + GrpcCallTypeEnum grpcType() default GrpcCallTypeEnum.UNARY; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/constant/GrpcCallTypeEnum.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/constant/GrpcCallTypeEnum.java new file mode 100644 index 0000000000..0707548306 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/constant/GrpcCallTypeEnum.java @@ -0,0 +1,13 @@ +package com.taobao.arthas.grpc.server.handler.constant; + +/** + * @author: FengYe + * @date: 2024/10/24 01:06 + * @description: StreamTypeEnum + */ +public enum GrpcCallTypeEnum { + UNARY, + SERVER_STREAM, + CLIENT_STREAM, + BI_STREAM; +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java new file mode 100644 index 0000000000..0fbc01bab9 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java @@ -0,0 +1,16 @@ +package com.taobao.arthas.grpc.server.handler.executor; + +import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; + +/** + * @author: FengYe + * @date: 2024/10/24 02:07 + * @description: AbstractGrpcExecutor + */ +public abstract class AbstractGrpcExecutor implements GrpcExecutor{ + protected GrpcDispatcher dispatcher; + + public AbstractGrpcExecutor(GrpcDispatcher dispatcher) { + this.dispatcher = dispatcher; + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java new file mode 100644 index 0000000000..94e343ad52 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java @@ -0,0 +1,54 @@ +package com.taobao.arthas.grpc.server.handler.executor; + +import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; +import com.taobao.arthas.grpc.server.handler.GrpcRequest; +import com.taobao.arthas.grpc.server.handler.GrpcResponse; +import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http2.DefaultHttp2DataFrame; +import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; +import io.netty.handler.codec.http2.Http2DataFrame; + +/** + * @author: FengYe + * @date: 2024/10/24 01:52 + * @description: BiStreamProcessor + */ +public class BiStreamExecutor extends AbstractGrpcExecutor { + + public BiStreamExecutor(GrpcDispatcher dispatcher) { + super(dispatcher); + } + + @Override + public GrpcCallTypeEnum supportGrpcType() { + return GrpcCallTypeEnum.BI_STREAM; + } + + @Override + public void execute(GrpcRequest request, Http2DataFrame frame, ChannelHandlerContext context) throws Throwable { + // todo 下面是迁移过来的,后面改掉 + // 流式调用,即刻响应 + + GrpcResponse response = new GrpcResponse(); + byte[] bytes = request.readData(); + while (bytes != null) { + response = dispatcher.execute(request.getService(), request.getMethod(), bytes); + + // 针对第一个响应发送 header + if (request.isStreamFirstData()) { + context.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(frame.stream())); + request.setStreamFirstData(false); + } + context.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(frame.stream())); + + bytes = request.readData(); + } + + request.clearData(); + + if (frame.isEndStream()) { + context.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(frame.stream())); + } + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java new file mode 100644 index 0000000000..cca600b6d0 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java @@ -0,0 +1,29 @@ +package com.taobao.arthas.grpc.server.handler.executor; + +import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; +import com.taobao.arthas.grpc.server.handler.GrpcRequest; +import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http2.Http2DataFrame; + +/** + * @author: FengYe + * @date: 2024/10/24 01:51 + * @description: UnaryProcessor + */ +public class ClientStreamExecutor extends AbstractGrpcExecutor { + + public ClientStreamExecutor(GrpcDispatcher dispatcher) { + super(dispatcher); + } + + @Override + public GrpcCallTypeEnum supportGrpcType() { + return GrpcCallTypeEnum.CLIENT_STREAM; + } + + @Override + public void execute(GrpcRequest request, Http2DataFrame frame, ChannelHandlerContext context) throws Throwable { + + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutor.java new file mode 100644 index 0000000000..540da337ec --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutor.java @@ -0,0 +1,17 @@ +package com.taobao.arthas.grpc.server.handler.executor; + +import com.taobao.arthas.grpc.server.handler.GrpcRequest; +import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http2.Http2DataFrame; + +/** + * @author: FengYe + * @date: 2024/10/24 01:50 + * @description: GrpcProcessor + */ +public interface GrpcExecutor { + GrpcCallTypeEnum supportGrpcType(); + + void execute(GrpcRequest request, Http2DataFrame frame, ChannelHandlerContext context) throws Throwable; +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutorFactory.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutorFactory.java new file mode 100644 index 0000000000..d36b298ee7 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutorFactory.java @@ -0,0 +1,55 @@ +package com.taobao.arthas.grpc.server.handler.executor; + +import com.alibaba.arthas.deps.org.slf4j.Logger; +import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; +import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; +import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import com.taobao.arthas.grpc.server.utils.ReflectUtil; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author: FengYe + * @date: 2024/10/24 01:56 + * @description: GrpcExecutorFactory + */ +public class GrpcExecutorFactory { + + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass().getName()); + + public static final String DEFAULT_GRPC_EXECUTOR_PACKAGE_NAME = "com.taobao.arthas.grpc.server.handler.executor"; + + private final Map map = new HashMap<>(); + + public void loadExecutor(GrpcDispatcher dispatcher) { + List> classes = ReflectUtil.findClasses(DEFAULT_GRPC_EXECUTOR_PACKAGE_NAME); + for (Class clazz : classes) { + if (GrpcExecutor.class.isAssignableFrom(clazz)) { + try { + if (AbstractGrpcExecutor.class.equals(clazz)) { + continue; + } + if (AbstractGrpcExecutor.class.isAssignableFrom(clazz)) { + Constructor constructor = clazz.getConstructor(GrpcDispatcher.class); + GrpcExecutor executor = (GrpcExecutor) constructor.newInstance(dispatcher); + map.put(executor.supportGrpcType(), executor); + } else { + Constructor constructor = clazz.getConstructor(); + GrpcExecutor executor = (GrpcExecutor) constructor.newInstance(); + map.put(executor.supportGrpcType(), executor); + } + } catch (Exception e) { + logger.error("GrpcExecutorFactory loadExecutor error", e); + } + } + } + } + + public GrpcExecutor getExecutor(GrpcCallTypeEnum grpcType) { + return map.get(grpcType); + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java new file mode 100644 index 0000000000..1c49861286 --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java @@ -0,0 +1,29 @@ +package com.taobao.arthas.grpc.server.handler.executor; + +import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; +import com.taobao.arthas.grpc.server.handler.GrpcRequest; +import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http2.Http2DataFrame; + +/** + * @author: FengYe + * @date: 2024/10/24 01:51 + * @description: UnaryProcessor + */ +public class ServerStreamExecutor extends AbstractGrpcExecutor { + + public ServerStreamExecutor(GrpcDispatcher dispatcher) { + super(dispatcher); + } + + @Override + public GrpcCallTypeEnum supportGrpcType() { + return GrpcCallTypeEnum.SERVER_STREAM; + } + + @Override + public void execute(GrpcRequest request, Http2DataFrame frame, ChannelHandlerContext context) throws Throwable { + + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/UnaryExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/UnaryExecutor.java new file mode 100644 index 0000000000..93836bb79f --- /dev/null +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/UnaryExecutor.java @@ -0,0 +1,38 @@ +package com.taobao.arthas.grpc.server.handler.executor; + +import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; +import com.taobao.arthas.grpc.server.handler.GrpcRequest; +import com.taobao.arthas.grpc.server.handler.GrpcResponse; +import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http2.DefaultHttp2DataFrame; +import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; +import io.netty.handler.codec.http2.Http2DataFrame; + +/** + * @author: FengYe + * @date: 2024/10/24 01:51 + * @description: UnaryProcessor + */ +public class UnaryExecutor extends AbstractGrpcExecutor { + + public UnaryExecutor(GrpcDispatcher dispatcher) { + super(dispatcher); + } + + @Override + public GrpcCallTypeEnum supportGrpcType() { + return GrpcCallTypeEnum.UNARY; + } + + @Override + public void execute(GrpcRequest request, Http2DataFrame frame, ChannelHandlerContext context) throws Throwable { + // 一元调用,等到 endStream 再响应 + if (frame.isEndStream()) { + GrpcResponse response = dispatcher.execute(request); + context.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(frame.stream())); + context.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(frame.stream())); + context.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(frame.stream())); + } + } +} diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java index c2973f5afd..20560f64dc 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java @@ -3,6 +3,7 @@ import arthas.grpc.unittest.ArthasUnittest; import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; +import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; import com.taobao.arthas.grpc.server.service.ArthasSampleService; /** @@ -27,7 +28,7 @@ public ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittest } @Override - @GrpcMethod(value = "watch", stream = true) + @GrpcMethod(value = "watch", grpcType = GrpcCallTypeEnum.BI_STREAM) public ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittestRequest command) { ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); builder.setMessage(command.getMessage()); diff --git a/arthas-grpc-server/src/main/proto/arthasUnittest.proto b/arthas-grpc-server/src/main/proto/arthasUnittest.proto index 81282b11cd..69e02ae6c2 100644 --- a/arthas-grpc-server/src/main/proto/arthasUnittest.proto +++ b/arthas-grpc-server/src/main/proto/arthasUnittest.proto @@ -5,8 +5,10 @@ package arthas.grpc.unittest; service ArthasUnittestService { rpc trace(ArthasUnittestRequest) returns (ArthasUnittestResponse); rpc watch(stream ArthasUnittestRequest) returns (stream ArthasUnittestResponse); - rpc addSum(ArthasUnittestRequest) returns (ArthasUnittestResponse); - rpc getSum(ArthasUnittestRequest) returns (ArthasUnittestResponse); + rpc unaryAddSum(ArthasUnittestRequest) returns (ArthasUnittestResponse); + rpc unaryGetSum(ArthasUnittestRequest) returns (ArthasUnittestResponse); + rpc clientStreamSum(stream ArthasUnittestRequest) returns (ArthasUnittestResponse); + rpc serverStreamSum(ArthasUnittestRequest) returns (stream ArthasUnittestResponse); } message ArthasUnittestRequest { diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java index 294b33d303..9363377d80 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java @@ -151,12 +151,12 @@ public void onCompleted() { private void addSum(int id, int num) { ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.newBuilder().setId(id).setNum(num).build(); - ArthasUnittest.ArthasUnittestResponse res = blockingStub.addSum(request); + ArthasUnittest.ArthasUnittestResponse res = blockingStub.unaryAddSum(request); } private int getSum(int id) { ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.newBuilder().setId(id).build(); - ArthasUnittest.ArthasUnittestResponse res = blockingStub.getSum(request); + ArthasUnittest.ArthasUnittestResponse res = blockingStub.unaryGetSum(request); return res.getNum(); } } diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java index bf4f40575f..1889b619e8 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java @@ -10,6 +10,6 @@ public interface ArthasSampleService { ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittestRequest command); ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittestRequest command); - ArthasUnittest.ArthasUnittestResponse addSum(ArthasUnittest.ArthasUnittestRequest command); - ArthasUnittest.ArthasUnittestResponse getSum(ArthasUnittest.ArthasUnittestRequest command); + ArthasUnittest.ArthasUnittestResponse unaryAddSum(ArthasUnittest.ArthasUnittestRequest command); + ArthasUnittest.ArthasUnittestResponse unaryGetSum(ArthasUnittest.ArthasUnittestRequest command); } diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java index c2a738dd55..560a20f388 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java @@ -3,6 +3,7 @@ import arthas.grpc.unittest.ArthasUnittest; import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; +import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; import unittest.grpc.service.ArthasSampleService; import java.util.concurrent.ConcurrentHashMap; @@ -32,7 +33,7 @@ public ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittest } @Override - @GrpcMethod(value = "watch", stream = true) + @GrpcMethod(value = "watch", grpcType = GrpcCallTypeEnum.BI_STREAM) public ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittestRequest command) { ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); builder.setMessage(command.getMessage()); @@ -40,8 +41,8 @@ public ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittest } @Override - @GrpcMethod(value = "addSum") - public ArthasUnittest.ArthasUnittestResponse addSum(ArthasUnittest.ArthasUnittestRequest command) { + @GrpcMethod(value = "unaryAddSum") + public ArthasUnittest.ArthasUnittestResponse unaryAddSum(ArthasUnittest.ArthasUnittestRequest command) { ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); builder.setMessage(command.getMessage()); map.merge(command.getId(), command.getNum(), Integer::sum); @@ -49,8 +50,8 @@ public ArthasUnittest.ArthasUnittestResponse addSum(ArthasUnittest.ArthasUnittes } @Override - @GrpcMethod(value = "getSum") - public ArthasUnittest.ArthasUnittestResponse getSum(ArthasUnittest.ArthasUnittestRequest command) { + @GrpcMethod(value = "unaryGetSum") + public ArthasUnittest.ArthasUnittestResponse unaryGetSum(ArthasUnittest.ArthasUnittestRequest command) { ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); builder.setMessage(command.getMessage()); Integer sum = map.getOrDefault(command.getId(), 0); From f63c4f869b7a86dfc4efa6294037a54bff1745f2 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Thu, 24 Oct 2024 02:42:08 +0800 Subject: [PATCH 43/48] update: reformat --- .../grpc/server/handler/Http2Handler.java | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java index 4b2776abe1..dbc6da1737 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java @@ -89,23 +89,23 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) } catch (Throwable e) { processError(ctx, e, dataFrame.stream()); } - }); -} - -private void handleResetStream(Http2ResetFrame resetFrame, ChannelHandlerContext ctx) { - int id = resetFrame.stream().id(); - System.out.println("handleResetStream"); - dataBuffer.remove(id); -} - -private void processError(ChannelHandlerContext ctx, Throwable e, Http2FrameStream stream) { - GrpcResponse response = new GrpcResponse(); - ArthasGrpc.ErrorRes.Builder builder = ArthasGrpc.ErrorRes.newBuilder(); - ArthasGrpc.ErrorRes errorRes = builder.setErrorMsg(e.getMessage()).build(); - response.setClazz(ArthasGrpc.ErrorRes.class); - response.writeResponseData(errorRes); - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(stream)); - ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(stream)); - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(stream)); -} + }); + } + + private void handleResetStream(Http2ResetFrame resetFrame, ChannelHandlerContext ctx) { + int id = resetFrame.stream().id(); + System.out.println("handleResetStream"); + dataBuffer.remove(id); + } + + private void processError(ChannelHandlerContext ctx, Throwable e, Http2FrameStream stream) { + GrpcResponse response = new GrpcResponse(); + ArthasGrpc.ErrorRes.Builder builder = ArthasGrpc.ErrorRes.newBuilder(); + ArthasGrpc.ErrorRes errorRes = builder.setErrorMsg(e.getMessage()).build(); + response.setClazz(ArthasGrpc.ErrorRes.class); + response.writeResponseData(errorRes); + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(stream)); + ctx.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(stream)); + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(stream)); + } } \ No newline at end of file From bde1171e7fc82cfbde511a3a669f2ac97ad4b480 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Thu, 24 Oct 2024 02:50:54 +0800 Subject: [PATCH 44/48] update: add README.md --- arthas-grpc-server/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 arthas-grpc-server/README.md diff --git a/arthas-grpc-server/README.md b/arthas-grpc-server/README.md new file mode 100644 index 0000000000..20bf89a4c4 --- /dev/null +++ b/arthas-grpc-server/README.md @@ -0,0 +1,3 @@ +# Arthas Grpc + +这个模块提供了一个轻量级的 Grpc 实现,目前任在开发中 \ No newline at end of file From c49c4bad2f0d8dcb9d6858e60ddb5a41f9da4e0c Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Fri, 25 Oct 2024 03:26:35 +0800 Subject: [PATCH 45/48] update: completed client stream --- arthas-grpc-server/pom.xml | 7 + .../grpc/server/handler/GrpcDispatcher.java | 101 +++++++++--- .../grpc/server/handler/GrpcRequest.java | 16 +- .../grpc/server/handler/GrpcResponse.java | 6 +- .../grpc/server/handler/Http2Handler.java | 1 + .../grpc/server/handler/StreamObserver.java | 4 +- .../server/handler/annotation/GrpcMethod.java | 4 +- ...lTypeEnum.java => GrpcInvokeTypeEnum.java} | 2 +- .../handler/executor/BiStreamExecutor.java | 8 +- .../executor/ClientStreamExecutor.java | 30 +++- .../server/handler/executor/GrpcExecutor.java | 4 +- .../handler/executor/GrpcExecutorFactory.java | 8 +- .../executor/ServerStreamExecutor.java | 6 +- .../handler/executor/UnaryExecutor.java | 8 +- .../server/service/ArthasSampleService.java | 9 +- .../service/impl/ArthasSampleServiceImpl.java | 47 +++++- .../src/test/java/unittest/grpc/GrpcTest.java | 147 ++++++++++++++---- .../grpc/service/ArthasSampleService.java | 18 ++- .../service/impl/ArthasSampleServiceImpl.java | 70 +++++++-- 19 files changed, 387 insertions(+), 109 deletions(-) rename arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/constant/{GrpcCallTypeEnum.java => GrpcInvokeTypeEnum.java} (86%) diff --git a/arthas-grpc-server/pom.xml b/arthas-grpc-server/pom.xml index c073413454..afcce2de17 100644 --- a/arthas-grpc-server/pom.xml +++ b/arthas-grpc-server/pom.xml @@ -62,6 +62,12 @@ 1.5.0 + + com.taobao.arthas + arthas-common + ${project.version} + + @@ -90,6 +96,7 @@ junit-jupiter test + javax.annotation javax.annotation-api diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java index ff86a3946f..9fb93fa1fd 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java @@ -5,13 +5,16 @@ import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; -import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import com.taobao.arthas.grpc.server.handler.constant.GrpcInvokeTypeEnum; import com.taobao.arthas.grpc.server.utils.ReflectUtil; +import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -28,15 +31,19 @@ public class GrpcDispatcher { public static final String DEFAULT_GRPC_SERVICE_PACKAGE_NAME = "com.taobao.arthas.grpc.server.service.impl"; - public static Map grpcMethodInvokeMap = new HashMap<>(); + public static Map grpcInvokeMap = new HashMap<>(); + +// public static Map clientStreamInvokeMap = new HashMap<>(); public static Map requestParseFromMap = new HashMap<>(); + public static Map requestToByteArrayMap = new HashMap<>(); public static Map responseParseFromMap = new HashMap<>(); + public static Map responseToByteArrayMap = new HashMap<>(); - public static Map grpcMethodStreamMap = new HashMap<>(); + public static Map grpcInvokeTypeMap = new HashMap<>(); public void loadGrpcService(String grpcServicePackageName) { List> classes = ReflectUtil.findClasses(Optional.ofNullable(grpcServicePackageName).orElse(DEFAULT_GRPC_SERVICE_PACKAGE_NAME)); @@ -46,7 +53,6 @@ public void loadGrpcService(String grpcServicePackageName) { // 处理 service GrpcService grpcService = clazz.getAnnotation(GrpcService.class); Object instance = clazz.getDeclaredConstructor().newInstance(); - // 处理 method MethodHandles.Lookup lookup = MethodHandles.lookup(); Method[] declaredMethods = clazz.getDeclaredMethods(); @@ -54,31 +60,58 @@ public void loadGrpcService(String grpcServicePackageName) { if (method.isAnnotationPresent(GrpcMethod.class)) { GrpcMethod grpcMethod = method.getAnnotation(GrpcMethod.class); MethodHandle grpcInvoke = lookup.unreflect(method); - Class requestClass = grpcInvoke.type().parameterType(1); - Class responseClass = grpcInvoke.type().returnType(); + String grpcMethodKey = generateGrpcMethodKey(grpcService.value(), grpcMethod.value()); + grpcInvokeTypeMap.put(grpcMethodKey, grpcMethod.grpcType()); + grpcInvokeMap.put(grpcMethodKey, grpcInvoke.bindTo(instance)); + + + Class requestClass = null; + Class responseClass = null; + if (GrpcInvokeTypeEnum.UNARY.equals(grpcMethod.grpcType())) { + requestClass = grpcInvoke.type().parameterType(1); + responseClass = grpcInvoke.type().returnType(); + } else { + responseClass = getInnerGenericClass(method.getGenericParameterTypes()[0]); + requestClass = getInnerGenericClass(method.getGenericReturnType()); + } MethodHandle requestParseFrom = lookup.findStatic(requestClass, "parseFrom", MethodType.methodType(requestClass, byte[].class)); MethodHandle responseParseFrom = lookup.findStatic(responseClass, "parseFrom", MethodType.methodType(responseClass, byte[].class)); MethodHandle requestToByteArray = lookup.findVirtual(requestClass, "toByteArray", MethodType.methodType(byte[].class)); MethodHandle responseToByteArray = lookup.findVirtual(responseClass, "toByteArray", MethodType.methodType(byte[].class)); - String grpcMethodKey = generateGrpcMethodKey(grpcService.value(), grpcMethod.value()); - grpcMethodInvokeMap.put(grpcMethodKey, grpcInvoke.bindTo(instance)); - grpcMethodStreamMap.put(grpcMethodKey, grpcMethod.grpcType()); requestParseFromMap.put(grpcMethodKey, requestParseFrom); responseParseFromMap.put(grpcMethodKey, responseParseFrom); requestToByteArrayMap.put(grpcMethodKey, requestToByteArray); responseToByteArrayMap.put(grpcMethodKey, responseToByteArray); + + +// switch (grpcMethod.grpcType()) { +// case UNARY: +// unaryInvokeMap.put(grpcMethodKey, grpcInvoke.bindTo(instance)); +// return; +// case CLIENT_STREAM: +// Object invoke = grpcInvoke.bindTo(instance).invoke(); +// if (!(invoke instanceof StreamObserver)) { +// throw new RuntimeException(grpcMethodKey + " return class is not StreamObserver!"); +// } +// clientStreamInvokeMap.put(grpcMethodKey, (StreamObserver) invoke); +// return; +// case SERVER_STREAM: +// return; +// case BI_STREAM: +// return; +// } } } - } catch (Exception e) { + } catch (Throwable e) { logger.error("GrpcDispatcher loadGrpcService error.", e); } } } } - public GrpcResponse execute(String service, String method, byte[] arg) throws Throwable { - MethodHandle methodHandle = grpcMethodInvokeMap.get(generateGrpcMethodKey(service, method)); - MethodType type = grpcMethodInvokeMap.get(generateGrpcMethodKey(service, method)).type(); + public GrpcResponse doUnaryExecute(String service, String method, byte[] arg) throws Throwable { + MethodHandle methodHandle = grpcInvokeMap.get(generateGrpcMethodKey(service, method)); + MethodType type = grpcInvokeMap.get(generateGrpcMethodKey(service, method)).type(); Object req = requestParseFromMap.get(generateGrpcMethodKey(service, method)).invoke(arg); Object execute = methodHandle.invoke(req); GrpcResponse grpcResponse = new GrpcResponse(); @@ -89,10 +122,22 @@ public GrpcResponse execute(String service, String method, byte[] arg) throws Th return grpcResponse; } - public GrpcResponse execute(GrpcRequest request) throws Throwable { - String service = request.getService(); - String method = request.getMethod(); - return this.execute(service, method, request.readData()); + public GrpcResponse unaryExecute(GrpcRequest request) throws Throwable { + MethodHandle methodHandle = grpcInvokeMap.get(request.getGrpcMethodKey()); + MethodType type = grpcInvokeMap.get(request.getGrpcMethodKey()).type(); + Object req = requestParseFromMap.get(request.getGrpcMethodKey()).invoke(request.readData()); + Object execute = methodHandle.invoke(req); + GrpcResponse grpcResponse = new GrpcResponse(); + grpcResponse.setClazz(type.returnType()); + grpcResponse.setService(request.getService()); + grpcResponse.setMethod(request.getMethod()); + grpcResponse.writeResponseData(execute); + return grpcResponse; + } + + public StreamObserver clientStreamExecute(GrpcRequest request, StreamObserver responseObserver) throws Throwable { + MethodHandle methodHandle = grpcInvokeMap.get(request.getGrpcMethodKey()); + return (StreamObserver) methodHandle.invoke(responseObserver); } /** @@ -104,7 +149,7 @@ public GrpcResponse execute(GrpcRequest request) throws Throwable { */ public static Class getRequestClass(String serviceName, String methodName) { //protobuf 规范只能有单入参 - return Optional.ofNullable(grpcMethodInvokeMap.get(generateGrpcMethodKey(serviceName, methodName))).orElseThrow(() -> new RuntimeException("The specified grpc method does not exist")).type().parameterArray()[0]; + return Optional.ofNullable(grpcInvokeMap.get(generateGrpcMethodKey(serviceName, methodName))).orElseThrow(() -> new RuntimeException("The specified grpc method does not exist")).type().parameterArray()[0]; } public static String generateGrpcMethodKey(String serviceName, String methodName) { @@ -113,9 +158,25 @@ public static String generateGrpcMethodKey(String serviceName, String methodName public static void checkGrpcType(GrpcRequest request) { request.setGrpcType( - Optional.ofNullable(grpcMethodStreamMap.get(generateGrpcMethodKey(request.getService(), request.getMethod()))) - .orElse(GrpcCallTypeEnum.UNARY) + Optional.ofNullable(grpcInvokeTypeMap.get(generateGrpcMethodKey(request.getService(), request.getMethod()))) + .orElse(GrpcInvokeTypeEnum.UNARY) ); request.setStreamFirstData(true); } + + public static Class getInnerGenericClass(Type type) { + if (type instanceof ParameterizedType) { + ParameterizedType paramType = (ParameterizedType) type; + Type[] actualTypeArguments = paramType.getActualTypeArguments(); + if (actualTypeArguments.length > 0) { + Type innerType = actualTypeArguments[0]; // 获取第一个实际类型参数 + if (innerType instanceof ParameterizedType) { + return getInnerGenericClass(innerType); // 递归调用获取最内层类型 + } else if (innerType instanceof Class) { + return (Class) innerType; // 直接返回 Class 类型 + } + } + } + return null; // 如果没有找到对应的类型 + } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java index eb41590feb..53b6fb1341 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java @@ -1,6 +1,6 @@ package com.taobao.arthas.grpc.server.handler; -import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import com.taobao.arthas.grpc.server.handler.constant.GrpcInvokeTypeEnum; import com.taobao.arthas.grpc.server.utils.ByteUtil; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http2.Http2Headers; @@ -16,7 +16,7 @@ * @date: 2024/9/4 23:07 * @description: GrpcRequest grpc 请求体 */ -public class GrpcRequest { +public class GrpcRequest { /** * 请求对应的 streamId @@ -66,7 +66,7 @@ public class GrpcRequest { /** * grpc 调用类型 */ - private GrpcCallTypeEnum grpcType; + private GrpcInvokeTypeEnum grpcType; public GrpcRequest(Integer streamId, String path, String method) { @@ -93,7 +93,7 @@ public void writeData(ByteBuf byteBuf) { * * @return */ - public byte[] readData() { + public synchronized byte[] readData() { if (byteData.readableBytes() == 0) { return null; } @@ -131,6 +131,10 @@ private byte[] decompressGzip(byte[] compressedData) { } } + public String getGrpcMethodKey() { + return service + "." + method; + } + public Integer getStreamId() { return streamId; } @@ -179,11 +183,11 @@ public void setHeaders(Http2Headers headers) { this.headers = headers; } - public GrpcCallTypeEnum getGrpcType() { + public GrpcInvokeTypeEnum getGrpcType() { return grpcType; } - public void setGrpcType(GrpcCallTypeEnum grpcType) { + public void setGrpcType(GrpcInvokeTypeEnum grpcType) { this.grpcType = grpcType; } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java index 165807e80f..3ccc52fda3 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java @@ -15,7 +15,7 @@ * @date: 2024/9/5 02:05 * @description: GrpcResponse */ -public class GrpcResponse { +public class GrpcResponse { private Map headers; @@ -56,6 +56,10 @@ public Http2Headers getEndStreamHeader() { return new DefaultHttp2Headers().set("grpc-status", "0"); } + public static Http2Headers getDefaultEndStreamHeader() { + return new DefaultHttp2Headers().set("grpc-status", "0"); + } + public ByteBuf getResponseData() { return byteData; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java index dbc6da1737..e234283afc 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java @@ -87,6 +87,7 @@ private void handleGrpcData(Http2DataFrame dataFrame, ChannelHandlerContext ctx) try { grpcExecutorFactory.getExecutor(grpcRequest.getGrpcType()).execute(grpcRequest, dataFrame, ctx); } catch (Throwable e) { + logger.error("handleGrpcData error", e); processError(ctx, e, dataFrame.stream()); } }); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/StreamObserver.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/StreamObserver.java index 6cabf638bc..570661ee3b 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/StreamObserver.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/StreamObserver.java @@ -7,9 +7,7 @@ */ public interface StreamObserver { - void onNext(V value); - - void onError(Throwable t); + void onNext(V req); void onCompleted(); } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java index 14bef63bfa..dec0e2bef0 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java @@ -1,6 +1,6 @@ package com.taobao.arthas.grpc.server.handler.annotation; -import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import com.taobao.arthas.grpc.server.handler.constant.GrpcInvokeTypeEnum; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -19,5 +19,5 @@ boolean stream() default false; - GrpcCallTypeEnum grpcType() default GrpcCallTypeEnum.UNARY; + GrpcInvokeTypeEnum grpcType() default GrpcInvokeTypeEnum.UNARY; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/constant/GrpcCallTypeEnum.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/constant/GrpcInvokeTypeEnum.java similarity index 86% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/constant/GrpcCallTypeEnum.java rename to arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/constant/GrpcInvokeTypeEnum.java index 0707548306..46fb252b15 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/constant/GrpcCallTypeEnum.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/constant/GrpcInvokeTypeEnum.java @@ -5,7 +5,7 @@ * @date: 2024/10/24 01:06 * @description: StreamTypeEnum */ -public enum GrpcCallTypeEnum { +public enum GrpcInvokeTypeEnum { UNARY, SERVER_STREAM, CLIENT_STREAM, diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java index 94e343ad52..0d4fd6bb9c 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java @@ -3,7 +3,7 @@ import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; import com.taobao.arthas.grpc.server.handler.GrpcRequest; import com.taobao.arthas.grpc.server.handler.GrpcResponse; -import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import com.taobao.arthas.grpc.server.handler.constant.GrpcInvokeTypeEnum; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http2.DefaultHttp2DataFrame; import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; @@ -21,8 +21,8 @@ public BiStreamExecutor(GrpcDispatcher dispatcher) { } @Override - public GrpcCallTypeEnum supportGrpcType() { - return GrpcCallTypeEnum.BI_STREAM; + public GrpcInvokeTypeEnum supportGrpcType() { + return GrpcInvokeTypeEnum.BI_STREAM; } @Override @@ -33,7 +33,7 @@ public void execute(GrpcRequest request, Http2DataFrame frame, ChannelHandlerCon GrpcResponse response = new GrpcResponse(); byte[] bytes = request.readData(); while (bytes != null) { - response = dispatcher.execute(request.getService(), request.getMethod(), bytes); + response = dispatcher.doUnaryExecute(request.getService(), request.getMethod(), bytes); // 针对第一个响应发送 header if (request.isStreamFirstData()) { diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java index cca600b6d0..95c96a0187 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java @@ -1,11 +1,18 @@ package com.taobao.arthas.grpc.server.handler.executor; +import arthas.grpc.unittest.ArthasUnittest; import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; import com.taobao.arthas.grpc.server.handler.GrpcRequest; -import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import com.taobao.arthas.grpc.server.handler.GrpcResponse; +import com.taobao.arthas.grpc.server.handler.StreamObserver; +import com.taobao.arthas.grpc.server.handler.constant.GrpcInvokeTypeEnum; import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http2.DefaultHttp2DataFrame; +import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; import io.netty.handler.codec.http2.Http2DataFrame; +import java.util.Observer; + /** * @author: FengYe * @date: 2024/10/24 01:51 @@ -18,12 +25,29 @@ public ClientStreamExecutor(GrpcDispatcher dispatcher) { } @Override - public GrpcCallTypeEnum supportGrpcType() { - return GrpcCallTypeEnum.CLIENT_STREAM; + public GrpcInvokeTypeEnum supportGrpcType() { + return GrpcInvokeTypeEnum.CLIENT_STREAM; } @Override public void execute(GrpcRequest request, Http2DataFrame frame, ChannelHandlerContext context) throws Throwable { + StreamObserver responseObserver = new StreamObserver() { + @Override + public void onNext(GrpcResponse res) { + context.writeAndFlush(new DefaultHttp2HeadersFrame(res.getEndHeader()).stream(frame.stream())); + context.writeAndFlush(new DefaultHttp2DataFrame(res.getResponseData()).stream(frame.stream())); + } + + @Override + public void onCompleted() { + context.writeAndFlush(new DefaultHttp2HeadersFrame(GrpcResponse.getDefaultEndStreamHeader(), true).stream(frame.stream())); + } + }; + StreamObserver requestObserver = dispatcher.clientStreamExecute(request, responseObserver); + requestObserver.onNext(request); + if (frame.isEndStream()) { + requestObserver.onCompleted(); + } } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutor.java index 540da337ec..6f1fa7f00b 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutor.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutor.java @@ -1,7 +1,7 @@ package com.taobao.arthas.grpc.server.handler.executor; import com.taobao.arthas.grpc.server.handler.GrpcRequest; -import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import com.taobao.arthas.grpc.server.handler.constant.GrpcInvokeTypeEnum; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http2.Http2DataFrame; @@ -11,7 +11,7 @@ * @description: GrpcProcessor */ public interface GrpcExecutor { - GrpcCallTypeEnum supportGrpcType(); + GrpcInvokeTypeEnum supportGrpcType(); void execute(GrpcRequest request, Http2DataFrame frame, ChannelHandlerContext context) throws Throwable; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutorFactory.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutorFactory.java index d36b298ee7..c102a25764 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutorFactory.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutorFactory.java @@ -3,7 +3,7 @@ import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; -import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import com.taobao.arthas.grpc.server.handler.constant.GrpcInvokeTypeEnum; import com.taobao.arthas.grpc.server.utils.ReflectUtil; import java.lang.invoke.MethodHandles; @@ -23,14 +23,14 @@ public class GrpcExecutorFactory { public static final String DEFAULT_GRPC_EXECUTOR_PACKAGE_NAME = "com.taobao.arthas.grpc.server.handler.executor"; - private final Map map = new HashMap<>(); + private final Map map = new HashMap<>(); public void loadExecutor(GrpcDispatcher dispatcher) { List> classes = ReflectUtil.findClasses(DEFAULT_GRPC_EXECUTOR_PACKAGE_NAME); for (Class clazz : classes) { if (GrpcExecutor.class.isAssignableFrom(clazz)) { try { - if (AbstractGrpcExecutor.class.equals(clazz)) { + if (AbstractGrpcExecutor.class.equals(clazz) || GrpcExecutor.class.equals(clazz)) { continue; } if (AbstractGrpcExecutor.class.isAssignableFrom(clazz)) { @@ -49,7 +49,7 @@ public void loadExecutor(GrpcDispatcher dispatcher) { } } - public GrpcExecutor getExecutor(GrpcCallTypeEnum grpcType) { + public GrpcExecutor getExecutor(GrpcInvokeTypeEnum grpcType) { return map.get(grpcType); } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java index 1c49861286..689013dc33 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java @@ -2,7 +2,7 @@ import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; import com.taobao.arthas.grpc.server.handler.GrpcRequest; -import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import com.taobao.arthas.grpc.server.handler.constant.GrpcInvokeTypeEnum; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http2.Http2DataFrame; @@ -18,8 +18,8 @@ public ServerStreamExecutor(GrpcDispatcher dispatcher) { } @Override - public GrpcCallTypeEnum supportGrpcType() { - return GrpcCallTypeEnum.SERVER_STREAM; + public GrpcInvokeTypeEnum supportGrpcType() { + return GrpcInvokeTypeEnum.SERVER_STREAM; } @Override diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/UnaryExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/UnaryExecutor.java index 93836bb79f..0995bd3f89 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/UnaryExecutor.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/UnaryExecutor.java @@ -3,7 +3,7 @@ import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; import com.taobao.arthas.grpc.server.handler.GrpcRequest; import com.taobao.arthas.grpc.server.handler.GrpcResponse; -import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import com.taobao.arthas.grpc.server.handler.constant.GrpcInvokeTypeEnum; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http2.DefaultHttp2DataFrame; import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; @@ -21,15 +21,15 @@ public UnaryExecutor(GrpcDispatcher dispatcher) { } @Override - public GrpcCallTypeEnum supportGrpcType() { - return GrpcCallTypeEnum.UNARY; + public GrpcInvokeTypeEnum supportGrpcType() { + return GrpcInvokeTypeEnum.UNARY; } @Override public void execute(GrpcRequest request, Http2DataFrame frame, ChannelHandlerContext context) throws Throwable { // 一元调用,等到 endStream 再响应 if (frame.isEndStream()) { - GrpcResponse response = dispatcher.execute(request); + GrpcResponse response = dispatcher.unaryExecute(request); context.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(frame.stream())); context.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(frame.stream())); context.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(frame.stream())); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java index 28e5a48717..6dc8c7e233 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java @@ -1,6 +1,10 @@ package com.taobao.arthas.grpc.server.service; import arthas.grpc.unittest.ArthasUnittest; +import com.taobao.arthas.grpc.server.handler.GrpcRequest; +import com.taobao.arthas.grpc.server.handler.GrpcResponse; +import com.taobao.arthas.grpc.server.handler.StreamObserver; + /** * @author: FengYe @@ -8,6 +12,7 @@ * @description: ArthasSampleService */ public interface ArthasSampleService { - ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittestRequest command); - ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittestRequest command); + ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittestRequest request); + ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittestRequest request); + StreamObserver clientStreamSum(StreamObserver observer); } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java index 20560f64dc..fb6dc667b7 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java @@ -1,10 +1,17 @@ package com.taobao.arthas.grpc.server.service.impl; import arthas.grpc.unittest.ArthasUnittest; +import com.google.protobuf.InvalidProtocolBufferException; +import com.taobao.arthas.grpc.server.handler.GrpcRequest; +import com.taobao.arthas.grpc.server.handler.GrpcResponse; +import com.taobao.arthas.grpc.server.handler.StreamObserver; import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; -import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import com.taobao.arthas.grpc.server.handler.constant.GrpcInvokeTypeEnum; import com.taobao.arthas.grpc.server.service.ArthasSampleService; +import com.taobao.arthas.grpc.server.utils.ByteUtil; + +import java.util.concurrent.atomic.AtomicInteger; /** * @author: FengYe @@ -14,24 +21,52 @@ @GrpcService("arthas.grpc.unittest.ArthasUnittestService") public class ArthasSampleServiceImpl implements ArthasSampleService { + private AtomicInteger sum = new AtomicInteger(0); + @Override @GrpcMethod("trace") - public ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittestRequest command) { + public ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittestRequest request) { try { Thread.sleep(50000L); } catch (InterruptedException e) { throw new RuntimeException(e); } ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); - builder.setMessage(command.getMessage()); + builder.setMessage(request.getMessage()); return builder.build(); } @Override - @GrpcMethod(value = "watch", grpcType = GrpcCallTypeEnum.BI_STREAM) - public ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittestRequest command) { + @GrpcMethod(value = "watch", grpcType = GrpcInvokeTypeEnum.BI_STREAM) + public ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittestRequest request) { ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); - builder.setMessage(command.getMessage()); + builder.setMessage(request.getMessage()); return builder.build(); } + + @Override + public StreamObserver clientStreamSum(StreamObserver observer) { + return new StreamObserver() { + @Override + public void onNext(GrpcRequest req) { + try { + ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.parseFrom(ByteUtil.getBytes(req.getByteData())); + sum.addAndGet(request.getNum()); + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException(e); + } + } + + @Override + public void onCompleted() { + ArthasUnittest.ArthasUnittestResponse response = ArthasUnittest.ArthasUnittestResponse.newBuilder() + .setNum(sum.get()) + .build(); + GrpcResponse grpcResponse = new GrpcResponse(); + grpcResponse.writeResponseData(response); + observer.onNext(grpcResponse); + observer.onCompleted(); + } + }; + } } \ No newline at end of file diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java index 9363377d80..b64ba34a8b 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java @@ -31,9 +31,8 @@ public class GrpcTest { private static final String HOST_PORT = HOST + ":" + PORT; private static final String UNIT_TEST_GRPC_SERVICE_PACKAGE_NAME = "unittest.grpc.service.impl"; private ArthasUnittestServiceGrpc.ArthasUnittestServiceBlockingStub blockingStub = null; - private ArthasUnittestServiceGrpc.ArthasUnittestServiceStub stub = null; - Random random; - ExecutorService threadPool; + Random random = new Random(); + ExecutorService threadPool = Executors.newFixedThreadPool(10); @Before public void startServer() { @@ -42,8 +41,6 @@ public void startServer() { arthasGrpcServer.start(); }); grpcWebProxyStart.start(); - random = new Random(); - threadPool = Executors.newFixedThreadPool(10); } @Test @@ -52,37 +49,22 @@ public void testUnary() { .usePlaintext() .build(); - blockingStub = ArthasUnittestServiceGrpc.newBlockingStub(channel); + ArthasUnittestServiceGrpc.ArthasUnittestServiceBlockingStub blockingStub = ArthasUnittestServiceGrpc.newBlockingStub(channel); try { - trace("trace"); + trace(blockingStub, "trace"); } finally { channel.shutdownNow(); } } @Test - public void testStream() { + public void testUnarySum() throws InterruptedException { ManagedChannel channel = ManagedChannelBuilder.forTarget(HOST_PORT) .usePlaintext() .build(); - stub = ArthasUnittestServiceGrpc.newStub(channel); - - try { - watch("watch1", "watch2", "watch3"); - } finally { - channel.shutdownNow(); - } - } - - @Test - public void testSum() throws InterruptedException { - ManagedChannel channel = ManagedChannelBuilder.forTarget(HOST_PORT) - .usePlaintext() - .build(); - - blockingStub = ArthasUnittestServiceGrpc.newBlockingStub(channel); + ArthasUnittestServiceGrpc.ArthasUnittestServiceBlockingStub stub = ArthasUnittestServiceGrpc.newBlockingStub(channel); for (int i = 0; i < 10; i++) { AtomicInteger sum = new AtomicInteger(0); int finalId = i; @@ -90,23 +72,120 @@ public void testSum() throws InterruptedException { int num = random.nextInt(101); sum.addAndGet(num); threadPool.submit(() -> { - addSum(finalId, num); + addSum(stub, finalId, num); }); } - Thread.sleep(1000); - int grpcSum = getSum(finalId); + Thread.sleep(2000L); + int grpcSum = getSum(stub, finalId); System.out.println("id:" + finalId + ",sum:" + sum.get() + ",grpcSum:" + grpcSum); Assert.assertEquals(sum.get(), grpcSum); } + channel.shutdown(); + } + + // 用于测试客户端流 + @Test + public void testClientStreamSum() throws Throwable { + ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 9090) + .usePlaintext() + .build(); + + ArthasUnittestServiceGrpc.ArthasUnittestServiceStub stub = ArthasUnittestServiceGrpc.newStub(channel); + + AtomicInteger sum = new AtomicInteger(0); + CountDownLatch latch = new CountDownLatch(1); + StreamObserver clientStreamObserver = stub.clientStreamSum(new StreamObserver() { + @Override + public void onNext(ArthasUnittest.ArthasUnittestResponse response) { + System.out.println("local sum:" + sum + ", grpc sum:" + response.getNum()); + Assert.assertEquals(sum.get(), response.getNum()); + } + + @Override + public void onError(Throwable t) { + System.err.println("Error: " + t); + } + + @Override + public void onCompleted() { + System.out.println("Client streaming completed."); + latch.countDown(); + } + }); + + for (int j = 0; j < 1000; j++) { + int num = random.nextInt(1001); + sum.addAndGet(num); + clientStreamObserver.onNext(ArthasUnittest.ArthasUnittestRequest.newBuilder().setNum(num).build()); + } + + clientStreamObserver.onCompleted(); + latch.await(); + channel.shutdown(); + } + + // 用于测试请求数据隔离性 + @Test + public void testDataIsolation() throws InterruptedException { + //todo 待完善 + ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 9090) + .usePlaintext() + .build(); + + ArthasUnittestServiceGrpc.ArthasUnittestServiceStub stub = ArthasUnittestServiceGrpc.newStub(channel); + for (int i = 0; i < 2; i++) { + threadPool.submit(() -> { + AtomicInteger sum = new AtomicInteger(0); + CountDownLatch latch = new CountDownLatch(1); + StreamObserver clientStreamObserver = stub.clientStreamSum(new StreamObserver() { + @Override + public void onNext(ArthasUnittest.ArthasUnittestResponse response) { + System.out.println("local sum:" + sum + ", grpc sum:" + response.getNum()); + Assert.assertEquals(sum.get(), response.getNum()); + } + + @Override + public void onError(Throwable t) { + System.err.println("Error: " + t); + } + + @Override + public void onCompleted() { + System.out.println("Client streaming completed."); + latch.countDown(); + } + }); + + for (int j = 0; j < 5; j++) { + int num = random.nextInt(101); + try { + Thread.sleep(1000L); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + sum.addAndGet(num); + clientStreamObserver.onNext(ArthasUnittest.ArthasUnittestRequest.newBuilder().setNum(num).build()); + } + + clientStreamObserver.onCompleted(); + try { + latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + channel.shutdown(); + }); + } + Thread.sleep(7000L); } - private void trace(String name) { + private void trace(ArthasUnittestServiceGrpc.ArthasUnittestServiceBlockingStub stub, String name) { ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.newBuilder().setMessage(name).build(); - ArthasUnittest.ArthasUnittestResponse res = blockingStub.trace(request); + ArthasUnittest.ArthasUnittestResponse res = stub.trace(request); System.out.println(res.getMessage()); } - private void watch(String... names) { + private void watch(ArthasUnittestServiceGrpc.ArthasUnittestServiceStub stub, String... names) { // 使用 CountDownLatch 来等待所有响应 CountDownLatch finishLatch = new CountDownLatch(1); @@ -149,14 +228,14 @@ public void onCompleted() { } } - private void addSum(int id, int num) { + private void addSum(ArthasUnittestServiceGrpc.ArthasUnittestServiceBlockingStub stub, int id, int num) { ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.newBuilder().setId(id).setNum(num).build(); - ArthasUnittest.ArthasUnittestResponse res = blockingStub.unaryAddSum(request); + ArthasUnittest.ArthasUnittestResponse res = stub.unaryAddSum(request); } - private int getSum(int id) { + private int getSum(ArthasUnittestServiceGrpc.ArthasUnittestServiceBlockingStub stub, int id) { ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.newBuilder().setId(id).build(); - ArthasUnittest.ArthasUnittestResponse res = blockingStub.unaryGetSum(request); + ArthasUnittest.ArthasUnittestResponse res = stub.unaryGetSum(request); return res.getNum(); } } diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java index 1889b619e8..99277a2428 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java @@ -1,6 +1,11 @@ package unittest.grpc.service; import arthas.grpc.unittest.ArthasUnittest; +import arthas.grpc.unittest.ArthasUnittest.ArthasUnittestRequest; +import arthas.grpc.unittest.ArthasUnittest.ArthasUnittestResponse; +import com.taobao.arthas.grpc.server.handler.GrpcRequest; +import com.taobao.arthas.grpc.server.handler.GrpcResponse; +import com.taobao.arthas.grpc.server.handler.StreamObserver; /** * @author: FengYe @@ -8,8 +13,13 @@ * @description: ArthasSampleService */ public interface ArthasSampleService { - ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittestRequest command); - ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittestRequest command); - ArthasUnittest.ArthasUnittestResponse unaryAddSum(ArthasUnittest.ArthasUnittestRequest command); - ArthasUnittest.ArthasUnittestResponse unaryGetSum(ArthasUnittest.ArthasUnittestRequest command); + ArthasUnittestResponse trace(ArthasUnittestRequest command); + + ArthasUnittestResponse watch(ArthasUnittestRequest command); + + ArthasUnittestResponse unaryAddSum(ArthasUnittestRequest command); + + ArthasUnittestResponse unaryGetSum(ArthasUnittestRequest command); + + StreamObserver> clientStreamSum(StreamObserver> observer); } diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java index 560a20f388..403867d00a 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java @@ -1,11 +1,22 @@ package unittest.grpc.service.impl; import arthas.grpc.unittest.ArthasUnittest; +import arthas.grpc.unittest.ArthasUnittest.ArthasUnittestRequest; +import arthas.grpc.unittest.ArthasUnittest.ArthasUnittestResponse; +import com.google.protobuf.InvalidProtocolBufferException; +import com.taobao.arthas.grpc.server.handler.GrpcRequest; +import com.taobao.arthas.grpc.server.handler.GrpcResponse; +import com.taobao.arthas.grpc.server.handler.StreamObserver; import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; -import com.taobao.arthas.grpc.server.handler.constant.GrpcCallTypeEnum; +import com.taobao.arthas.grpc.server.handler.constant.GrpcInvokeTypeEnum; +import com.taobao.arthas.grpc.server.utils.ByteUtil; +import io.netty.handler.codec.http2.DefaultHttp2DataFrame; +import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; +import org.junit.platform.commons.util.CollectionUtils; import unittest.grpc.service.ArthasSampleService; +import java.util.Collections; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -17,33 +28,37 @@ @GrpcService("arthas.grpc.unittest.ArthasUnittestService") public class ArthasSampleServiceImpl implements ArthasSampleService { + private AtomicInteger sum = new AtomicInteger(0); + + private AtomicInteger num = new AtomicInteger(0); + private ConcurrentHashMap map = new ConcurrentHashMap<>(); @Override @GrpcMethod("trace") - public ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittestRequest command) { + public ArthasUnittestResponse trace(ArthasUnittestRequest command) { try { Thread.sleep(5000L); } catch (InterruptedException e) { throw new RuntimeException(e); } - ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); + ArthasUnittestResponse.Builder builder = ArthasUnittestResponse.newBuilder(); builder.setMessage(command.getMessage()); return builder.build(); } @Override - @GrpcMethod(value = "watch", grpcType = GrpcCallTypeEnum.BI_STREAM) - public ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittestRequest command) { - ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); +// @GrpcMethod(value = "watch", grpcType = GrpcInvokeTypeEnum.BI_STREAM) + public ArthasUnittestResponse watch(ArthasUnittestRequest command) { + ArthasUnittestResponse.Builder builder = ArthasUnittestResponse.newBuilder(); builder.setMessage(command.getMessage()); return builder.build(); } @Override @GrpcMethod(value = "unaryAddSum") - public ArthasUnittest.ArthasUnittestResponse unaryAddSum(ArthasUnittest.ArthasUnittestRequest command) { - ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); + public ArthasUnittestResponse unaryAddSum(ArthasUnittestRequest command) { + ArthasUnittestResponse.Builder builder = ArthasUnittestResponse.newBuilder(); builder.setMessage(command.getMessage()); map.merge(command.getId(), command.getNum(), Integer::sum); return builder.build(); @@ -51,11 +66,46 @@ public ArthasUnittest.ArthasUnittestResponse unaryAddSum(ArthasUnittest.ArthasUn @Override @GrpcMethod(value = "unaryGetSum") - public ArthasUnittest.ArthasUnittestResponse unaryGetSum(ArthasUnittest.ArthasUnittestRequest command) { - ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); + public ArthasUnittestResponse unaryGetSum(ArthasUnittestRequest command) { + ArthasUnittestResponse.Builder builder = ArthasUnittestResponse.newBuilder(); builder.setMessage(command.getMessage()); Integer sum = map.getOrDefault(command.getId(), 0); builder.setNum(sum); return builder.build(); } + + @Override + @GrpcMethod(value = "clientStreamSum", grpcType = GrpcInvokeTypeEnum.CLIENT_STREAM) + public StreamObserver> clientStreamSum(StreamObserver> observer) { + return new StreamObserver>() { + @Override + public void onNext(GrpcRequest req) { + try { + byte[] bytes = req.readData(); + while (bytes != null && bytes.length != 0) { + ArthasUnittestRequest request = ArthasUnittestRequest.parseFrom(bytes); + sum.addAndGet(request.getNum()); + bytes = req.readData(); +// System.out.println(num.addAndGet(1)); + } + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException(e); + } + } + + @Override + public void onCompleted() { + ArthasUnittestResponse response = ArthasUnittestResponse.newBuilder() + .setNum(sum.get()) + .build(); + GrpcResponse grpcResponse = new GrpcResponse<>(); + //todo 待优化 + grpcResponse.setService("arthas.grpc.unittest.ArthasUnittestService"); + grpcResponse.setMethod("clientStreamSum"); + grpcResponse.writeResponseData(response); + observer.onNext(grpcResponse); + observer.onCompleted(); + } + }; + } } \ No newline at end of file From ed45cdb11864174f70b6f2076c5c4c5a1bac5440 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Sat, 26 Oct 2024 20:11:25 +0800 Subject: [PATCH 46/48] update: Implement data isolation for stream invoke --- .../executor/AbstractGrpcExecutor.java | 6 ++++ .../executor/ClientStreamExecutor.java | 32 ++++++++++++------- .../executor/ServerStreamExecutor.java | 29 +++++++++++++++++ .../src/test/java/unittest/grpc/GrpcTest.java | 2 +- ...ervice.java => ArthasUnittestService.java} | 3 +- ...pl.java => ArthasUnittestServiceImpl.java} | 18 ++++------- 6 files changed, 64 insertions(+), 26 deletions(-) rename arthas-grpc-server/src/test/java/unittest/grpc/service/{ArthasSampleService.java => ArthasUnittestService.java} (91%) rename arthas-grpc-server/src/test/java/unittest/grpc/service/impl/{ArthasSampleServiceImpl.java => ArthasUnittestServiceImpl.java} (87%) diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java index 0fbc01bab9..647f45e368 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java @@ -1,6 +1,10 @@ package com.taobao.arthas.grpc.server.handler.executor; import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; +import com.taobao.arthas.grpc.server.handler.GrpcRequest; +import com.taobao.arthas.grpc.server.handler.StreamObserver; + +import java.util.concurrent.ConcurrentHashMap; /** * @author: FengYe @@ -10,6 +14,8 @@ public abstract class AbstractGrpcExecutor implements GrpcExecutor{ protected GrpcDispatcher dispatcher; + protected ConcurrentHashMap> streamObserverMap = new ConcurrentHashMap<>(); + public AbstractGrpcExecutor(GrpcDispatcher dispatcher) { this.dispatcher = dispatcher; } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java index 95c96a0187..2cfcf16bee 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java @@ -31,19 +31,27 @@ public GrpcInvokeTypeEnum supportGrpcType() { @Override public void execute(GrpcRequest request, Http2DataFrame frame, ChannelHandlerContext context) throws Throwable { - StreamObserver responseObserver = new StreamObserver() { - @Override - public void onNext(GrpcResponse res) { - context.writeAndFlush(new DefaultHttp2HeadersFrame(res.getEndHeader()).stream(frame.stream())); - context.writeAndFlush(new DefaultHttp2DataFrame(res.getResponseData()).stream(frame.stream())); + Integer streamId = request.getStreamId(); + + StreamObserver requestObserver = streamObserverMap.computeIfAbsent(streamId,id->{ + StreamObserver responseObserver = new StreamObserver() { + @Override + public void onNext(GrpcResponse res) { + context.writeAndFlush(new DefaultHttp2HeadersFrame(res.getEndHeader()).stream(frame.stream())); + context.writeAndFlush(new DefaultHttp2DataFrame(res.getResponseData()).stream(frame.stream())); + } + + @Override + public void onCompleted() { + context.writeAndFlush(new DefaultHttp2HeadersFrame(GrpcResponse.getDefaultEndStreamHeader(), true).stream(frame.stream())); + } + }; + try { + return dispatcher.clientStreamExecute(request, responseObserver); + } catch (Throwable e) { + throw new RuntimeException(e); } - - @Override - public void onCompleted() { - context.writeAndFlush(new DefaultHttp2HeadersFrame(GrpcResponse.getDefaultEndStreamHeader(), true).stream(frame.stream())); - } - }; - StreamObserver requestObserver = dispatcher.clientStreamExecute(request, responseObserver); + }); requestObserver.onNext(request); if (frame.isEndStream()) { diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java index 689013dc33..99fc01de7e 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java @@ -2,8 +2,12 @@ import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; import com.taobao.arthas.grpc.server.handler.GrpcRequest; +import com.taobao.arthas.grpc.server.handler.GrpcResponse; +import com.taobao.arthas.grpc.server.handler.StreamObserver; import com.taobao.arthas.grpc.server.handler.constant.GrpcInvokeTypeEnum; import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http2.DefaultHttp2DataFrame; +import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; import io.netty.handler.codec.http2.Http2DataFrame; /** @@ -24,6 +28,31 @@ public GrpcInvokeTypeEnum supportGrpcType() { @Override public void execute(GrpcRequest request, Http2DataFrame frame, ChannelHandlerContext context) throws Throwable { + Integer streamId = request.getStreamId(); + StreamObserver requestObserver = streamObserverMap.computeIfAbsent(streamId, id->{ + StreamObserver responseObserver = new StreamObserver() { + @Override + public void onNext(GrpcResponse res) { + context.writeAndFlush(new DefaultHttp2HeadersFrame(res.getEndHeader()).stream(frame.stream())); + context.writeAndFlush(new DefaultHttp2DataFrame(res.getResponseData()).stream(frame.stream())); + } + + @Override + public void onCompleted() { + context.writeAndFlush(new DefaultHttp2HeadersFrame(GrpcResponse.getDefaultEndStreamHeader(), true).stream(frame.stream())); + } + }; + try { + return dispatcher.clientStreamExecute(request, responseObserver); + } catch (Throwable e) { + throw new RuntimeException(e); + } + }); + + requestObserver.onNext(request); + if (frame.isEndStream()) { + requestObserver.onCompleted(); + } } } diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java index b64ba34a8b..0b5ea72a71 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java @@ -133,7 +133,7 @@ public void testDataIsolation() throws InterruptedException { .build(); ArthasUnittestServiceGrpc.ArthasUnittestServiceStub stub = ArthasUnittestServiceGrpc.newStub(channel); - for (int i = 0; i < 2; i++) { + for (int i = 0; i < 10; i++) { threadPool.submit(() -> { AtomicInteger sum = new AtomicInteger(0); CountDownLatch latch = new CountDownLatch(1); diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasUnittestService.java similarity index 91% rename from arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java rename to arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasUnittestService.java index 99277a2428..37a532bcdb 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasSampleService.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasUnittestService.java @@ -1,6 +1,5 @@ package unittest.grpc.service; -import arthas.grpc.unittest.ArthasUnittest; import arthas.grpc.unittest.ArthasUnittest.ArthasUnittestRequest; import arthas.grpc.unittest.ArthasUnittest.ArthasUnittestResponse; import com.taobao.arthas.grpc.server.handler.GrpcRequest; @@ -12,7 +11,7 @@ * @date: 2024/6/30 下午11:42 * @description: ArthasSampleService */ -public interface ArthasSampleService { +public interface ArthasUnittestService { ArthasUnittestResponse trace(ArthasUnittestRequest command); ArthasUnittestResponse watch(ArthasUnittestRequest command); diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasUnittestServiceImpl.java similarity index 87% rename from arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java rename to arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasUnittestServiceImpl.java index 403867d00a..3dc649f0d7 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasSampleServiceImpl.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasUnittestServiceImpl.java @@ -1,6 +1,5 @@ package unittest.grpc.service.impl; -import arthas.grpc.unittest.ArthasUnittest; import arthas.grpc.unittest.ArthasUnittest.ArthasUnittestRequest; import arthas.grpc.unittest.ArthasUnittest.ArthasUnittestResponse; import com.google.protobuf.InvalidProtocolBufferException; @@ -10,13 +9,8 @@ import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; import com.taobao.arthas.grpc.server.handler.constant.GrpcInvokeTypeEnum; -import com.taobao.arthas.grpc.server.utils.ByteUtil; -import io.netty.handler.codec.http2.DefaultHttp2DataFrame; -import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; -import org.junit.platform.commons.util.CollectionUtils; -import unittest.grpc.service.ArthasSampleService; +import unittest.grpc.service.ArthasUnittestService; -import java.util.Collections; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -26,11 +20,11 @@ * @description: ArthasSampleServiceImpl */ @GrpcService("arthas.grpc.unittest.ArthasUnittestService") -public class ArthasSampleServiceImpl implements ArthasSampleService { +public class ArthasUnittestServiceImpl implements ArthasUnittestService { - private AtomicInteger sum = new AtomicInteger(0); + private AtomicInteger atomicInteger = new AtomicInteger(); - private AtomicInteger num = new AtomicInteger(0); +// private AtomicInteger sum = new AtomicInteger(0); private ConcurrentHashMap map = new ConcurrentHashMap<>(); @@ -78,6 +72,8 @@ public ArthasUnittestResponse unaryGetSum(ArthasUnittestRequest command) { @GrpcMethod(value = "clientStreamSum", grpcType = GrpcInvokeTypeEnum.CLIENT_STREAM) public StreamObserver> clientStreamSum(StreamObserver> observer) { return new StreamObserver>() { + AtomicInteger sum = new AtomicInteger(0); + @Override public void onNext(GrpcRequest req) { try { @@ -86,7 +82,7 @@ public void onNext(GrpcRequest req) { ArthasUnittestRequest request = ArthasUnittestRequest.parseFrom(bytes); sum.addAndGet(request.getNum()); bytes = req.readData(); -// System.out.println(num.addAndGet(1)); +// System.out.println(atomicInteger.addAndGet(1)); } } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); From 06ddcff2f3889435501319b41cb11812bb4e60d7 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Sun, 27 Oct 2024 02:16:00 +0800 Subject: [PATCH 47/48] update: Implement serverStream and biStream --- .../grpc/server/handler/GrpcDispatcher.java | 20 +++- .../grpc/server/handler/GrpcResponse.java | 11 +++ .../grpc/server/handler/Http2Handler.java | 3 +- .../executor/AbstractGrpcExecutor.java | 2 +- .../handler/executor/BiStreamExecutor.java | 48 +++++---- .../executor/ClientStreamExecutor.java | 13 ++- .../executor/ServerStreamExecutor.java | 41 ++++---- .../server/service/ArthasSampleService.java | 14 ++- .../service/impl/ArthasSampleServiceImpl.java | 97 +++++++++++++++---- .../src/main/proto/arthasUnittest.proto | 6 +- .../src/test/java/unittest/grpc/GrpcTest.java | 87 +++++++++++------ .../grpc/service/ArthasUnittestService.java | 8 +- .../impl/ArthasUnittestServiceImpl.java | 69 +++++++++---- .../src/test/proto/arthasUnittest.proto | 16 --- 14 files changed, 294 insertions(+), 141 deletions(-) delete mode 100644 arthas-grpc-server/src/test/proto/arthasUnittest.proto diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java index 9fb93fa1fd..4ad3313f11 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java @@ -7,7 +7,6 @@ import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; import com.taobao.arthas.grpc.server.handler.constant.GrpcInvokeTypeEnum; import com.taobao.arthas.grpc.server.utils.ReflectUtil; -import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -70,9 +69,12 @@ public void loadGrpcService(String grpcServicePackageName) { if (GrpcInvokeTypeEnum.UNARY.equals(grpcMethod.grpcType())) { requestClass = grpcInvoke.type().parameterType(1); responseClass = grpcInvoke.type().returnType(); - } else { + } else if (GrpcInvokeTypeEnum.CLIENT_STREAM.equals(grpcMethod.grpcType()) || GrpcInvokeTypeEnum.BI_STREAM.equals(grpcMethod.grpcType())) { responseClass = getInnerGenericClass(method.getGenericParameterTypes()[0]); requestClass = getInnerGenericClass(method.getGenericReturnType()); + } else if (GrpcInvokeTypeEnum.SERVER_STREAM.equals(grpcMethod.grpcType())) { + requestClass = getInnerGenericClass(method.getGenericParameterTypes()[0]); + responseClass = getInnerGenericClass(method.getGenericParameterTypes()[1]); } MethodHandle requestParseFrom = lookup.findStatic(requestClass, "parseFrom", MethodType.methodType(requestClass, byte[].class)); MethodHandle responseParseFrom = lookup.findStatic(responseClass, "parseFrom", MethodType.methodType(responseClass, byte[].class)); @@ -140,6 +142,17 @@ public StreamObserver clientStreamExecute(GrpcRequest request, Stre return (StreamObserver) methodHandle.invoke(responseObserver); } + public void serverStreamExecute(GrpcRequest request, StreamObserver responseObserver) throws Throwable { + MethodHandle methodHandle = grpcInvokeMap.get(request.getGrpcMethodKey()); + Object req = requestParseFromMap.get(request.getGrpcMethodKey()).invoke(request.readData()); + methodHandle.invoke(req, responseObserver); + } + + public StreamObserver biStreamExecute(GrpcRequest request, StreamObserver responseObserver) throws Throwable { + MethodHandle methodHandle = grpcInvokeMap.get(request.getGrpcMethodKey()); + return (StreamObserver) methodHandle.invoke(responseObserver); + } + /** * 获取指定 service method 对应的入参类型 * @@ -165,6 +178,9 @@ public static void checkGrpcType(GrpcRequest request) { } public static Class getInnerGenericClass(Type type) { + if (type instanceof Class) { + return (Class) type; + } if (type instanceof ParameterizedType) { ParameterizedType paramType = (ParameterizedType) type; Type[] actualTypeArguments = paramType.getActualTypeArguments(); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java index 3ccc52fda3..a275dd0144 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java @@ -2,11 +2,14 @@ import arthas.grpc.common.ArthasGrpc; +import com.taobao.arthas.grpc.server.handler.annotation.GrpcMethod; +import com.taobao.arthas.grpc.server.handler.annotation.GrpcService; import com.taobao.arthas.grpc.server.utils.ByteUtil; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http2.DefaultHttp2Headers; import io.netty.handler.codec.http2.Http2Headers; +import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; @@ -46,6 +49,14 @@ public class GrpcResponse { headers.put("grpc-accept-encoding", "identity,deflate,gzip"); } + public GrpcResponse() { + } + + public GrpcResponse(Method method) { + this.service = method.getDeclaringClass().getAnnotation(GrpcService.class).value(); + this.method = method.getAnnotation(GrpcMethod.class).value(); + } + public Http2Headers getEndHeader() { Http2Headers endHeader = new DefaultHttp2Headers().status("200"); headers.forEach(endHeader::set); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java index e234283afc..d0068b213f 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java @@ -14,6 +14,7 @@ import java.io.*; import java.lang.invoke.MethodHandles; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; /** @@ -102,7 +103,7 @@ private void handleResetStream(Http2ResetFrame resetFrame, ChannelHandlerContext private void processError(ChannelHandlerContext ctx, Throwable e, Http2FrameStream stream) { GrpcResponse response = new GrpcResponse(); ArthasGrpc.ErrorRes.Builder builder = ArthasGrpc.ErrorRes.newBuilder(); - ArthasGrpc.ErrorRes errorRes = builder.setErrorMsg(e.getMessage()).build(); + ArthasGrpc.ErrorRes errorRes = builder.setErrorMsg(Optional.ofNullable(e.getMessage()).orElse("")).build(); response.setClazz(ArthasGrpc.ErrorRes.class); response.writeResponseData(errorRes); ctx.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(stream)); diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java index 647f45e368..d814749dd3 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java @@ -14,7 +14,7 @@ public abstract class AbstractGrpcExecutor implements GrpcExecutor{ protected GrpcDispatcher dispatcher; - protected ConcurrentHashMap> streamObserverMap = new ConcurrentHashMap<>(); + protected ConcurrentHashMap> requestStreamObserverMap = new ConcurrentHashMap<>(); public AbstractGrpcExecutor(GrpcDispatcher dispatcher) { this.dispatcher = dispatcher; diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java index 0d4fd6bb9c..1291463997 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java @@ -3,12 +3,15 @@ import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; import com.taobao.arthas.grpc.server.handler.GrpcRequest; import com.taobao.arthas.grpc.server.handler.GrpcResponse; +import com.taobao.arthas.grpc.server.handler.StreamObserver; import com.taobao.arthas.grpc.server.handler.constant.GrpcInvokeTypeEnum; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http2.DefaultHttp2DataFrame; import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; import io.netty.handler.codec.http2.Http2DataFrame; +import java.util.concurrent.atomic.AtomicBoolean; + /** * @author: FengYe * @date: 2024/10/24 01:52 @@ -27,28 +30,37 @@ public GrpcInvokeTypeEnum supportGrpcType() { @Override public void execute(GrpcRequest request, Http2DataFrame frame, ChannelHandlerContext context) throws Throwable { - // todo 下面是迁移过来的,后面改掉 - // 流式调用,即刻响应 - - GrpcResponse response = new GrpcResponse(); - byte[] bytes = request.readData(); - while (bytes != null) { - response = dispatcher.doUnaryExecute(request.getService(), request.getMethod(), bytes); - - // 针对第一个响应发送 header - if (request.isStreamFirstData()) { - context.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndHeader()).stream(frame.stream())); - request.setStreamFirstData(false); - } - context.writeAndFlush(new DefaultHttp2DataFrame(response.getResponseData()).stream(frame.stream())); + Integer streamId = request.getStreamId(); - bytes = request.readData(); - } + StreamObserver requestObserver = requestStreamObserverMap.computeIfAbsent(streamId, id->{ + StreamObserver responseObserver = new StreamObserver() { + AtomicBoolean sendHeader = new AtomicBoolean(false); - request.clearData(); + @Override + public void onNext(GrpcResponse res) { + // 控制流只能响应一次header + if (!sendHeader.get()) { + sendHeader.compareAndSet(false, true); + context.writeAndFlush(new DefaultHttp2HeadersFrame(res.getEndHeader()).stream(frame.stream())); + } + context.writeAndFlush(new DefaultHttp2DataFrame(res.getResponseData()).stream(frame.stream())); + } + + @Override + public void onCompleted() { + context.writeAndFlush(new DefaultHttp2HeadersFrame(GrpcResponse.getDefaultEndStreamHeader(), true).stream(frame.stream())); + } + }; + try { + return dispatcher.biStreamExecute(request, responseObserver); + } catch (Throwable e) { + throw new RuntimeException(e); + } + }); + requestObserver.onNext(request); if (frame.isEndStream()) { - context.writeAndFlush(new DefaultHttp2HeadersFrame(response.getEndStreamHeader(), true).stream(frame.stream())); + requestObserver.onCompleted(); } } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java index 2cfcf16bee..d58547928a 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java @@ -1,6 +1,5 @@ package com.taobao.arthas.grpc.server.handler.executor; -import arthas.grpc.unittest.ArthasUnittest; import com.taobao.arthas.grpc.server.handler.GrpcDispatcher; import com.taobao.arthas.grpc.server.handler.GrpcRequest; import com.taobao.arthas.grpc.server.handler.GrpcResponse; @@ -11,7 +10,7 @@ import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; import io.netty.handler.codec.http2.Http2DataFrame; -import java.util.Observer; +import java.util.concurrent.atomic.AtomicBoolean; /** * @author: FengYe @@ -33,11 +32,17 @@ public GrpcInvokeTypeEnum supportGrpcType() { public void execute(GrpcRequest request, Http2DataFrame frame, ChannelHandlerContext context) throws Throwable { Integer streamId = request.getStreamId(); - StreamObserver requestObserver = streamObserverMap.computeIfAbsent(streamId,id->{ + StreamObserver requestObserver = requestStreamObserverMap.computeIfAbsent(streamId, id->{ StreamObserver responseObserver = new StreamObserver() { + AtomicBoolean sendHeader = new AtomicBoolean(false); + @Override public void onNext(GrpcResponse res) { - context.writeAndFlush(new DefaultHttp2HeadersFrame(res.getEndHeader()).stream(frame.stream())); + // 控制流只能响应一次header + if (!sendHeader.get()) { + sendHeader.compareAndSet(false, true); + context.writeAndFlush(new DefaultHttp2HeadersFrame(res.getEndHeader()).stream(frame.stream())); + } context.writeAndFlush(new DefaultHttp2DataFrame(res.getResponseData()).stream(frame.stream())); } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java index 99fc01de7e..5192fa41de 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java @@ -10,6 +10,8 @@ import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; import io.netty.handler.codec.http2.Http2DataFrame; +import java.util.concurrent.atomic.AtomicBoolean; + /** * @author: FengYe * @date: 2024/10/24 01:51 @@ -28,31 +30,28 @@ public GrpcInvokeTypeEnum supportGrpcType() { @Override public void execute(GrpcRequest request, Http2DataFrame frame, ChannelHandlerContext context) throws Throwable { - Integer streamId = request.getStreamId(); - - StreamObserver requestObserver = streamObserverMap.computeIfAbsent(streamId, id->{ - StreamObserver responseObserver = new StreamObserver() { - @Override - public void onNext(GrpcResponse res) { + StreamObserver responseObserver = new StreamObserver() { + AtomicBoolean sendHeader = new AtomicBoolean(false); + + @Override + public void onNext(GrpcResponse res) { + // 控制流只能响应一次header + if (!sendHeader.get()) { + sendHeader.compareAndSet(false, true); context.writeAndFlush(new DefaultHttp2HeadersFrame(res.getEndHeader()).stream(frame.stream())); - context.writeAndFlush(new DefaultHttp2DataFrame(res.getResponseData()).stream(frame.stream())); } - - @Override - public void onCompleted() { - context.writeAndFlush(new DefaultHttp2HeadersFrame(GrpcResponse.getDefaultEndStreamHeader(), true).stream(frame.stream())); - } - }; - try { - return dispatcher.clientStreamExecute(request, responseObserver); - } catch (Throwable e) { - throw new RuntimeException(e); + context.writeAndFlush(new DefaultHttp2DataFrame(res.getResponseData()).stream(frame.stream())); } - }); - requestObserver.onNext(request); - if (frame.isEndStream()) { - requestObserver.onCompleted(); + @Override + public void onCompleted() { + context.writeAndFlush(new DefaultHttp2HeadersFrame(GrpcResponse.getDefaultEndStreamHeader(), true).stream(frame.stream())); + } + }; + try { + dispatcher.serverStreamExecute(request, responseObserver); + } catch (Throwable e) { + throw new RuntimeException(e); } } } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java index 6dc8c7e233..f0098f93c5 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java @@ -12,7 +12,15 @@ * @description: ArthasSampleService */ public interface ArthasSampleService { - ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittestRequest request); - ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittestRequest request); - StreamObserver clientStreamSum(StreamObserver observer); + ArthasUnittest.ArthasUnittestResponse unary(ArthasUnittest.ArthasUnittestRequest command); + + ArthasUnittest.ArthasUnittestResponse unaryAddSum(ArthasUnittest.ArthasUnittestRequest command); + + ArthasUnittest.ArthasUnittestResponse unaryGetSum(ArthasUnittest.ArthasUnittestRequest command); + + StreamObserver> clientStreamSum(StreamObserver> observer); + + void serverStream(ArthasUnittest.ArthasUnittestRequest request, StreamObserver> observer); + + StreamObserver> biStream(StreamObserver> observer); } diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java index fb6dc667b7..a3961cb31b 100644 --- a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java +++ b/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java @@ -11,6 +11,7 @@ import com.taobao.arthas.grpc.server.service.ArthasSampleService; import com.taobao.arthas.grpc.server.utils.ByteUtil; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; /** @@ -21,37 +22,50 @@ @GrpcService("arthas.grpc.unittest.ArthasUnittestService") public class ArthasSampleServiceImpl implements ArthasSampleService { - private AtomicInteger sum = new AtomicInteger(0); + private ConcurrentHashMap map = new ConcurrentHashMap<>(); @Override - @GrpcMethod("trace") - public ArthasUnittest.ArthasUnittestResponse trace(ArthasUnittest.ArthasUnittestRequest request) { - try { - Thread.sleep(50000L); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } + @GrpcMethod(value = "unary") + public ArthasUnittest.ArthasUnittestResponse unary(ArthasUnittest.ArthasUnittestRequest command) { ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); - builder.setMessage(request.getMessage()); + builder.setMessage(command.getMessage()); return builder.build(); } @Override - @GrpcMethod(value = "watch", grpcType = GrpcInvokeTypeEnum.BI_STREAM) - public ArthasUnittest.ArthasUnittestResponse watch(ArthasUnittest.ArthasUnittestRequest request) { + @GrpcMethod(value = "unaryAddSum") + public ArthasUnittest.ArthasUnittestResponse unaryAddSum(ArthasUnittest.ArthasUnittestRequest command) { ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); - builder.setMessage(request.getMessage()); + builder.setMessage(command.getMessage()); + map.merge(command.getId(), command.getNum(), Integer::sum); return builder.build(); } @Override - public StreamObserver clientStreamSum(StreamObserver observer) { - return new StreamObserver() { + @GrpcMethod(value = "unaryGetSum") + public ArthasUnittest.ArthasUnittestResponse unaryGetSum(ArthasUnittest.ArthasUnittestRequest command) { + ArthasUnittest.ArthasUnittestResponse.Builder builder = ArthasUnittest.ArthasUnittestResponse.newBuilder(); + builder.setMessage(command.getMessage()); + Integer sum = map.getOrDefault(command.getId(), 0); + builder.setNum(sum); + return builder.build(); + } + + @Override + @GrpcMethod(value = "clientStreamSum", grpcType = GrpcInvokeTypeEnum.CLIENT_STREAM) + public StreamObserver> clientStreamSum(StreamObserver> observer) { + return new StreamObserver>() { + AtomicInteger sum = new AtomicInteger(0); + @Override - public void onNext(GrpcRequest req) { + public void onNext(GrpcRequest req) { try { - ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.parseFrom(ByteUtil.getBytes(req.getByteData())); - sum.addAndGet(request.getNum()); + byte[] bytes = req.readData(); + while (bytes != null && bytes.length != 0) { + ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.parseFrom(bytes); + sum.addAndGet(request.getNum()); + bytes = req.readData(); + } } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); } @@ -62,11 +76,58 @@ public void onCompleted() { ArthasUnittest.ArthasUnittestResponse response = ArthasUnittest.ArthasUnittestResponse.newBuilder() .setNum(sum.get()) .build(); - GrpcResponse grpcResponse = new GrpcResponse(); + GrpcResponse grpcResponse = new GrpcResponse<>(); + grpcResponse.setService("arthas.grpc.unittest.ArthasUnittestService"); + grpcResponse.setMethod("clientStreamSum"); grpcResponse.writeResponseData(response); observer.onNext(grpcResponse); observer.onCompleted(); } }; } + + @Override + @GrpcMethod(value = "serverStream", grpcType = GrpcInvokeTypeEnum.SERVER_STREAM) + public void serverStream(ArthasUnittest.ArthasUnittestRequest request, StreamObserver> observer) { + + for (int i = 0; i < 5; i++) { + ArthasUnittest.ArthasUnittestResponse response = ArthasUnittest.ArthasUnittestResponse.newBuilder() + .setMessage("Server response " + i + " to " + request.getMessage()) + .build(); + GrpcResponse grpcResponse = new GrpcResponse<>(); + grpcResponse.setService("arthas.grpc.unittest.ArthasUnittestService"); + grpcResponse.setMethod("serverStream"); + grpcResponse.writeResponseData(response); + observer.onNext(grpcResponse); + } + observer.onCompleted(); + } + + @Override + @GrpcMethod(value = "biStream", grpcType = GrpcInvokeTypeEnum.BI_STREAM) + public StreamObserver> biStream(StreamObserver> observer) { + return new StreamObserver>() { + @Override + public void onNext(GrpcRequest req) { + try { + byte[] bytes = req.readData(); + while (bytes != null && bytes.length != 0) { + GrpcResponse grpcResponse = new GrpcResponse<>(); + grpcResponse.setService("arthas.grpc.unittest.ArthasUnittestService"); + grpcResponse.setMethod("biStream"); + grpcResponse.writeResponseData(ArthasUnittest.ArthasUnittestResponse.parseFrom(bytes)); + observer.onNext(grpcResponse); + bytes = req.readData(); + } + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException(e); + } + } + + @Override + public void onCompleted() { + observer.onCompleted(); + } + }; + } } \ No newline at end of file diff --git a/arthas-grpc-server/src/main/proto/arthasUnittest.proto b/arthas-grpc-server/src/main/proto/arthasUnittest.proto index 69e02ae6c2..6b925dcce9 100644 --- a/arthas-grpc-server/src/main/proto/arthasUnittest.proto +++ b/arthas-grpc-server/src/main/proto/arthasUnittest.proto @@ -3,12 +3,12 @@ syntax = "proto3"; package arthas.grpc.unittest; service ArthasUnittestService { - rpc trace(ArthasUnittestRequest) returns (ArthasUnittestResponse); - rpc watch(stream ArthasUnittestRequest) returns (stream ArthasUnittestResponse); + rpc unary(ArthasUnittestRequest) returns (ArthasUnittestResponse); rpc unaryAddSum(ArthasUnittestRequest) returns (ArthasUnittestResponse); rpc unaryGetSum(ArthasUnittestRequest) returns (ArthasUnittestResponse); rpc clientStreamSum(stream ArthasUnittestRequest) returns (ArthasUnittestResponse); - rpc serverStreamSum(ArthasUnittestRequest) returns (stream ArthasUnittestResponse); + rpc serverStream(ArthasUnittestRequest) returns (stream ArthasUnittestResponse); + rpc biStream(stream ArthasUnittestRequest) returns (stream ArthasUnittestResponse); } message ArthasUnittestRequest { diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java index 0b5ea72a71..0181a94258 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java @@ -49,10 +49,12 @@ public void testUnary() { .usePlaintext() .build(); - ArthasUnittestServiceGrpc.ArthasUnittestServiceBlockingStub blockingStub = ArthasUnittestServiceGrpc.newBlockingStub(channel); + ArthasUnittestServiceGrpc.ArthasUnittestServiceBlockingStub stub = ArthasUnittestServiceGrpc.newBlockingStub(channel); try { - trace(blockingStub, "trace"); + ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.newBuilder().setMessage("unaryInvoke").build(); + ArthasUnittest.ArthasUnittestResponse res = stub.unary(request); + System.out.println(res.getMessage()); } finally { channel.shutdownNow(); } @@ -108,7 +110,7 @@ public void onError(Throwable t) { @Override public void onCompleted() { - System.out.println("Client streaming completed."); + System.out.println("testClientStreamSum completed."); latch.countDown(); } }); @@ -127,7 +129,6 @@ public void onCompleted() { // 用于测试请求数据隔离性 @Test public void testDataIsolation() throws InterruptedException { - //todo 待完善 ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 9090) .usePlaintext() .build(); @@ -151,7 +152,7 @@ public void onError(Throwable t) { @Override public void onCompleted() { - System.out.println("Client streaming completed."); + System.out.println("testDataIsolation completed."); latch.countDown(); } }); @@ -179,53 +180,79 @@ public void onCompleted() { Thread.sleep(7000L); } - private void trace(ArthasUnittestServiceGrpc.ArthasUnittestServiceBlockingStub stub, String name) { - ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.newBuilder().setMessage(name).build(); - ArthasUnittest.ArthasUnittestResponse res = stub.trace(request); - System.out.println(res.getMessage()); - } + @Test + public void testServerStream() throws InterruptedException { + ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 9090) + .usePlaintext() + .build(); - private void watch(ArthasUnittestServiceGrpc.ArthasUnittestServiceStub stub, String... names) { - // 使用 CountDownLatch 来等待所有响应 - CountDownLatch finishLatch = new CountDownLatch(1); + ArthasUnittestServiceGrpc.ArthasUnittestServiceStub stub = ArthasUnittestServiceGrpc.newStub(channel); + + ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.newBuilder().setMessage("serverStream").build(); - StreamObserver watch = stub.watch(new StreamObserver() { + stub.serverStream(request, new StreamObserver() { @Override public void onNext(ArthasUnittest.ArthasUnittestResponse value) { - System.out.println("watch: " + value.getMessage()); + System.out.println("testServerStream client receive: " + value.getMessage()); } @Override public void onError(Throwable t) { - } @Override public void onCompleted() { - System.out.println("Finished sending watch."); + System.out.println("testServerStream completed"); } }); - try { - for (String name : names) { - ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.newBuilder().setMessage(name).build(); - Thread.sleep(1000L); - watch.onNext(request); - } + Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } finally { - watch.onCompleted(); - finishLatch.countDown(); + channel.shutdown(); } + } - // 等待服务器的响应 - try { - finishLatch.await(); // 等待完成 - } catch (InterruptedException e) { - e.printStackTrace(); + // 用于测试双向流 + @Test + public void testBiStream() throws Throwable { + ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 9090) + .usePlaintext() + .build(); + + ArthasUnittestServiceGrpc.ArthasUnittestServiceStub stub = ArthasUnittestServiceGrpc.newStub(channel); + + CountDownLatch latch = new CountDownLatch(1); + StreamObserver biStreamObserver = stub.biStream(new StreamObserver() { + @Override + public void onNext(ArthasUnittest.ArthasUnittestResponse response) { + System.out.println("testBiStream receive: "+response.getMessage()); + } + + @Override + public void onError(Throwable t) { + System.err.println("Error: " + t); + } + + @Override + public void onCompleted() { + System.out.println("testBiStream completed."); + latch.countDown(); + } + }); + + String[] messages = new String[]{"testBiStream1","testBiStream2","testBiStream3"}; + for (String msg : messages) { + ArthasUnittest.ArthasUnittestRequest request = ArthasUnittest.ArthasUnittestRequest.newBuilder().setMessage(msg).build(); + biStreamObserver.onNext(request); } + + Thread.sleep(2000); + biStreamObserver.onCompleted(); + latch.await(); + channel.shutdown(); } private void addSum(ArthasUnittestServiceGrpc.ArthasUnittestServiceBlockingStub stub, int id, int num) { diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasUnittestService.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasUnittestService.java index 37a532bcdb..6ba3081d21 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasUnittestService.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasUnittestService.java @@ -12,13 +12,15 @@ * @description: ArthasSampleService */ public interface ArthasUnittestService { - ArthasUnittestResponse trace(ArthasUnittestRequest command); - - ArthasUnittestResponse watch(ArthasUnittestRequest command); + ArthasUnittestResponse unary(ArthasUnittestRequest command); ArthasUnittestResponse unaryAddSum(ArthasUnittestRequest command); ArthasUnittestResponse unaryGetSum(ArthasUnittestRequest command); StreamObserver> clientStreamSum(StreamObserver> observer); + + void serverStream(ArthasUnittestRequest request, StreamObserver> observer); + + StreamObserver> biStream(StreamObserver> observer); } diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasUnittestServiceImpl.java b/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasUnittestServiceImpl.java index 3dc649f0d7..aba8faff18 100644 --- a/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasUnittestServiceImpl.java +++ b/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasUnittestServiceImpl.java @@ -1,5 +1,6 @@ package unittest.grpc.service.impl; +import arthas.grpc.unittest.ArthasUnittest; import arthas.grpc.unittest.ArthasUnittest.ArthasUnittestRequest; import arthas.grpc.unittest.ArthasUnittest.ArthasUnittestResponse; import com.google.protobuf.InvalidProtocolBufferException; @@ -22,28 +23,11 @@ @GrpcService("arthas.grpc.unittest.ArthasUnittestService") public class ArthasUnittestServiceImpl implements ArthasUnittestService { - private AtomicInteger atomicInteger = new AtomicInteger(); - -// private AtomicInteger sum = new AtomicInteger(0); - private ConcurrentHashMap map = new ConcurrentHashMap<>(); @Override - @GrpcMethod("trace") - public ArthasUnittestResponse trace(ArthasUnittestRequest command) { - try { - Thread.sleep(5000L); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - ArthasUnittestResponse.Builder builder = ArthasUnittestResponse.newBuilder(); - builder.setMessage(command.getMessage()); - return builder.build(); - } - - @Override -// @GrpcMethod(value = "watch", grpcType = GrpcInvokeTypeEnum.BI_STREAM) - public ArthasUnittestResponse watch(ArthasUnittestRequest command) { + @GrpcMethod(value = "unary") + public ArthasUnittestResponse unary(ArthasUnittestRequest command) { ArthasUnittestResponse.Builder builder = ArthasUnittestResponse.newBuilder(); builder.setMessage(command.getMessage()); return builder.build(); @@ -82,7 +66,6 @@ public void onNext(GrpcRequest req) { ArthasUnittestRequest request = ArthasUnittestRequest.parseFrom(bytes); sum.addAndGet(request.getNum()); bytes = req.readData(); -// System.out.println(atomicInteger.addAndGet(1)); } } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); @@ -95,7 +78,6 @@ public void onCompleted() { .setNum(sum.get()) .build(); GrpcResponse grpcResponse = new GrpcResponse<>(); - //todo 待优化 grpcResponse.setService("arthas.grpc.unittest.ArthasUnittestService"); grpcResponse.setMethod("clientStreamSum"); grpcResponse.writeResponseData(response); @@ -104,4 +86,49 @@ public void onCompleted() { } }; } + + @Override + @GrpcMethod(value = "serverStream", grpcType = GrpcInvokeTypeEnum.SERVER_STREAM) + public void serverStream(ArthasUnittestRequest request, StreamObserver> observer) { + + for (int i = 0; i < 5; i++) { + ArthasUnittest.ArthasUnittestResponse response = ArthasUnittest.ArthasUnittestResponse.newBuilder() + .setMessage("Server response " + i + " to " + request.getMessage()) + .build(); + GrpcResponse grpcResponse = new GrpcResponse<>(); + grpcResponse.setService("arthas.grpc.unittest.ArthasUnittestService"); + grpcResponse.setMethod("serverStream"); + grpcResponse.writeResponseData(response); + observer.onNext(grpcResponse); + } + observer.onCompleted(); + } + + @Override + @GrpcMethod(value = "biStream", grpcType = GrpcInvokeTypeEnum.BI_STREAM) + public StreamObserver> biStream(StreamObserver> observer) { + return new StreamObserver>() { + @Override + public void onNext(GrpcRequest req) { + try { + byte[] bytes = req.readData(); + while (bytes != null && bytes.length != 0) { + GrpcResponse grpcResponse = new GrpcResponse<>(); + grpcResponse.setService("arthas.grpc.unittest.ArthasUnittestService"); + grpcResponse.setMethod("biStream"); + grpcResponse.writeResponseData(ArthasUnittestResponse.parseFrom(bytes)); + observer.onNext(grpcResponse); + bytes = req.readData(); + } + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException(e); + } + } + + @Override + public void onCompleted() { + observer.onCompleted(); + } + }; + } } \ No newline at end of file diff --git a/arthas-grpc-server/src/test/proto/arthasUnittest.proto b/arthas-grpc-server/src/test/proto/arthasUnittest.proto deleted file mode 100644 index b615b056aa..0000000000 --- a/arthas-grpc-server/src/test/proto/arthasUnittest.proto +++ /dev/null @@ -1,16 +0,0 @@ -syntax = "proto3"; - -package arthas.grpc.unittest; - -service ArthasUnittestService { - rpc trace(ArthasUnittestRequest) returns (ArthasUnittestResponse); - rpc watch(stream ArthasUnittestRequest) returns (stream ArthasUnittestResponse); -} - -message ArthasUnittestRequest { - string message = 1; -} - -message ArthasUnittestResponse{ - string message = 1; -} From dbb28611d1b6ef117a5bfe1df1c8c898d294a3d5 Mon Sep 17 00:00:00 2001 From: summer <1129126684@qq.com> Date: Mon, 28 Oct 2024 22:03:15 +0800 Subject: [PATCH 48/48] update: move maven module --- .../src/main/resources/class_template.tpl | 84 ------------------- .../arthas-grpc-server}/README.md | 0 .../arthas-grpc-server}/pom.xml | 2 +- .../grpc/server/ArthasGrpcBootstrap.java | 0 .../arthas/grpc/server/ArthasGrpcServer.java | 0 .../grpc/server/handler/GrpcDispatcher.java | 0 .../grpc/server/handler/GrpcRequest.java | 0 .../grpc/server/handler/GrpcResponse.java | 0 .../server/handler/Http2FrameRequest.java | 0 .../grpc/server/handler/Http2Handler.java | 0 .../grpc/server/handler/StreamObserver.java | 0 .../server/handler/annotation/GrpcMethod.java | 0 .../handler/annotation/GrpcService.java | 0 .../handler/constant/GrpcInvokeTypeEnum.java | 0 .../executor/AbstractGrpcExecutor.java | 0 .../handler/executor/BiStreamExecutor.java | 0 .../executor/ClientStreamExecutor.java | 0 .../server/handler/executor/GrpcExecutor.java | 0 .../handler/executor/GrpcExecutorFactory.java | 0 .../executor/ServerStreamExecutor.java | 0 .../handler/executor/UnaryExecutor.java | 0 .../server/service/ArthasSampleService.java | 0 .../service/impl/ArthasSampleServiceImpl.java | 0 .../arthas/grpc/server/utils/ByteUtil.java | 0 .../arthas/grpc/server/utils/ReflectUtil.java | 0 .../src/main/proto/arthasGrpc.proto | 0 .../src/main/proto/arthasUnittest.proto | 0 .../src/test/java/unittest/grpc/GrpcTest.java | 0 .../grpc/service/ArthasUnittestService.java | 0 .../impl/ArthasUnittestServiceImpl.java | 0 30 files changed, 1 insertion(+), 85 deletions(-) delete mode 100644 arthas-grpc-server/src/main/resources/class_template.tpl rename {arthas-grpc-server => labs/arthas-grpc-server}/README.md (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/pom.xml (99%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcBootstrap.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/handler/Http2FrameRequest.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/handler/StreamObserver.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/handler/constant/GrpcInvokeTypeEnum.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutor.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutorFactory.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/handler/executor/UnaryExecutor.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/java/com/taobao/arthas/grpc/server/utils/ReflectUtil.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/proto/arthasGrpc.proto (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/main/proto/arthasUnittest.proto (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/test/java/unittest/grpc/GrpcTest.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/test/java/unittest/grpc/service/ArthasUnittestService.java (100%) rename {arthas-grpc-server => labs/arthas-grpc-server}/src/test/java/unittest/grpc/service/impl/ArthasUnittestServiceImpl.java (100%) diff --git a/arthas-grpc-server/src/main/resources/class_template.tpl b/arthas-grpc-server/src/main/resources/class_template.tpl deleted file mode 100644 index 89d6e1dd2b..0000000000 --- a/arthas-grpc-server/src/main/resources/class_template.tpl +++ /dev/null @@ -1,84 +0,0 @@ -${package} - -import java.io.Serializable; - -import ${importBlock}; - - -public class ${className} implements ${codecClassName}<${targetProxyClassName}>, Serializable { - public static final long serialVersionUID = 1L; - - public byte[] encode(${targetProxyClassName} target) throws IOException { - CodedOutputStreamCache outputCache = CodedOutputStreamCache.get(); - doWriteTo(target, outputCache.getCodedOutputStream()); - return outputCache.getData(); - } - - public void doWriteTo(${targetProxyClassName} target, CodedOutputStream output) - throws IOException { - - ${dynamicFieldType} ${dynamicFieldName} = null; - if (!ProtoBufUtil.isNull(${dynamicFieldGetter})) { - ${dynamicFieldName} = ${dynamicFieldGetter}; - ${encodeWriteFieldValue} - } - - } - - public ${targetProxyClassName} decode(byte[] bb) throws IOException { - CodedInputStream input = CodedInputStream.newInstance(bb, 0, bb.length); - return readFrom(input); - } - - public int size(${targetProxyClassName} target) throws IOException { - int size = 0; - - ${dynamicFieldType} ${dynamicFieldName} = null; - if (!ProtoBufUtil.isNull(${dynamicFieldGetter})) { - ${dynamicFieldName} = ${dynamicFieldGetter}; - size += ${sizeDynamicString} - } - - return size; - } - - public ${targetProxyClassName} readFrom(CodedInputStream input) throws IOException { - ${targetProxyClassName} target = new ${targetProxyClassName}(); - - ${initListMapFields} - - - ${enumInitialize}; - - try { - boolean done = false; - ProtobufCodec codec = null; - while (!done) { - int tag = input.readTag(); - if (tag == 0) { - break; - } - - if (tag == ${decodeOrder}) { - ${objectDecodeExpress} - ${decodeFieldSetValue} - ${objectDecodeExpressSuffix} - continue; - } - ${objectPackedDecodeExpress} - - - input.skipField(tag); - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e; - } catch (java.io.IOException e) { - throw e; - } - - return target; - - } -} - - \ No newline at end of file diff --git a/arthas-grpc-server/README.md b/labs/arthas-grpc-server/README.md similarity index 100% rename from arthas-grpc-server/README.md rename to labs/arthas-grpc-server/README.md diff --git a/arthas-grpc-server/pom.xml b/labs/arthas-grpc-server/pom.xml similarity index 99% rename from arthas-grpc-server/pom.xml rename to labs/arthas-grpc-server/pom.xml index afcce2de17..0074082ace 100644 --- a/arthas-grpc-server/pom.xml +++ b/labs/arthas-grpc-server/pom.xml @@ -7,7 +7,7 @@ arthas-all com.taobao.arthas ${revision} - ../pom.xml + ../../pom.xml 4.0.0 arthas-grpc-server diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcBootstrap.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcBootstrap.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcBootstrap.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcBootstrap.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/ArthasGrpcServer.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcDispatcher.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcRequest.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/GrpcResponse.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2FrameRequest.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2FrameRequest.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2FrameRequest.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2FrameRequest.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/Http2Handler.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/StreamObserver.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/StreamObserver.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/StreamObserver.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/StreamObserver.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcMethod.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/annotation/GrpcService.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/constant/GrpcInvokeTypeEnum.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/constant/GrpcInvokeTypeEnum.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/constant/GrpcInvokeTypeEnum.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/constant/GrpcInvokeTypeEnum.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/AbstractGrpcExecutor.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/BiStreamExecutor.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ClientStreamExecutor.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutor.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutor.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutor.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutor.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutorFactory.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutorFactory.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutorFactory.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/GrpcExecutorFactory.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/ServerStreamExecutor.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/UnaryExecutor.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/UnaryExecutor.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/UnaryExecutor.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/handler/executor/UnaryExecutor.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/ArthasSampleService.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/service/impl/ArthasSampleServiceImpl.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ByteUtil.java diff --git a/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ReflectUtil.java b/labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ReflectUtil.java similarity index 100% rename from arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ReflectUtil.java rename to labs/arthas-grpc-server/src/main/java/com/taobao/arthas/grpc/server/utils/ReflectUtil.java diff --git a/arthas-grpc-server/src/main/proto/arthasGrpc.proto b/labs/arthas-grpc-server/src/main/proto/arthasGrpc.proto similarity index 100% rename from arthas-grpc-server/src/main/proto/arthasGrpc.proto rename to labs/arthas-grpc-server/src/main/proto/arthasGrpc.proto diff --git a/arthas-grpc-server/src/main/proto/arthasUnittest.proto b/labs/arthas-grpc-server/src/main/proto/arthasUnittest.proto similarity index 100% rename from arthas-grpc-server/src/main/proto/arthasUnittest.proto rename to labs/arthas-grpc-server/src/main/proto/arthasUnittest.proto diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java b/labs/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java similarity index 100% rename from arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java rename to labs/arthas-grpc-server/src/test/java/unittest/grpc/GrpcTest.java diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasUnittestService.java b/labs/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasUnittestService.java similarity index 100% rename from arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasUnittestService.java rename to labs/arthas-grpc-server/src/test/java/unittest/grpc/service/ArthasUnittestService.java diff --git a/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasUnittestServiceImpl.java b/labs/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasUnittestServiceImpl.java similarity index 100% rename from arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasUnittestServiceImpl.java rename to labs/arthas-grpc-server/src/test/java/unittest/grpc/service/impl/ArthasUnittestServiceImpl.java