Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for gRPC #2914

Open
wants to merge 36 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
61cdb9a
init
fengye404 Jun 30, 2024
c1a9ea8
init
fengye404 Jun 30, 2024
610d121
init
fengye404 Jul 3, 2024
871b0d7
init
fengye404 Jul 7, 2024
f5732a6
init
fengye404 Jul 9, 2024
96dcd06
update: add arthasSample
fengye404 Jul 14, 2024
e3ec9e2
update: add MiniTemplator
fengye404 Jul 21, 2024
8761959
init
fengye404 Jul 23, 2024
0116fcd
update: add protobuf codec
fengye404 Jul 24, 2024
29eb16f
update: add protobuf codec
fengye404 Jul 28, 2024
faff33f
update: add protobuf codec
fengye404 Jul 29, 2024
900c89f
update: add protobuf codec
fengye404 Jul 31, 2024
8dd4c42
update: add protobuf codec
fengye404 Aug 1, 2024
bc81e98
update: add protobuf codec
fengye404 Aug 3, 2024
1640f19
update: add protobuf codec
fengye404 Aug 3, 2024
4d960ae
update: add protobuf codec
fengye404 Aug 4, 2024
f720222
update: add protobuf codec
fengye404 Aug 5, 2024
be1d85b
update: add protobuf codec
fengye404 Aug 7, 2024
5871052
update: add protobuf codec
fengye404 Aug 8, 2024
500e64a
update: add protobuf codec
fengye404 Aug 9, 2024
2248532
update: add grpc handler
fengye404 Aug 11, 2024
77abc18
update: add grpc handler
fengye404 Aug 12, 2024
ec65cb3
update: add grpc handler
fengye404 Aug 19, 2024
f55e872
update: add grpc handler
fengye404 Aug 27, 2024
5732edd
update: add grpc handler
fengye404 Sep 4, 2024
c8f9310
update: add grpc handler
fengye404 Sep 5, 2024
526cdd9
update: add grpc dispatcher
fengye404 Sep 9, 2024
cb669af
update: add grpc dispatcher
fengye404 Sep 12, 2024
bfee0f1
update: complete grpc dispatcher
fengye404 Sep 13, 2024
96251c5
update: add stream handler
fengye404 Sep 15, 2024
b2c289f
update: add stream handler
fengye404 Sep 16, 2024
a3426b7
update: Optimize the situation where multiple grpc bodies exist in th…
fengye404 Sep 18, 2024
893f7f3
update: add unit test
fengye404 Sep 19, 2024
f3050ab
update: formatter
fengye404 Sep 22, 2024
7aa63aa
update: add unit test and error process
fengye404 Sep 23, 2024
2061dea
update: add maven profiles
fengye404 Oct 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions arthas-grpc-server/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<parent>
<artifactId>arthas-all</artifactId>
<groupId>com.taobao.arthas</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>arthas-grpc-server</artifactId>
<name>arthas-grpc-server</name>
<url>https://github.com/alibaba/arthas</url>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<grpc.version>1.46.0</grpc.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-bom</artifactId>
<version>${grpc.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>

<!-- https://mvnrepository.com/artifact/io.netty/netty-codec-http2 -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http2</artifactId>
<version>4.1.72.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.19.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.12</version>
</dependency>

<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.0</version>
</dependency>




<!-- 测试用 -->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http2</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-services</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>


<build>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protoSourceRoot>${basedir}/src/main/proto</protoSourceRoot>
<protocArtifact>com.google.protobuf:protoc:3.11.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.28.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.4.1.Final</version>
</extension>
</extensions>
</build>

<profiles>
<profile>
<id>mac</id>
<activation>
<os>
<family>mac</family>
</os>
</activation>
<properties>
<os.detected.classifier>osx-x86_64</os.detected.classifier>
</properties>
</profile>
</profiles>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.taobao.arthas.grpc.server;

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.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 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
* @description: ArthasGrpcServer
*/
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());

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();

GrpcDispatcher grpcDispatcher = new GrpcDispatcher();
grpcDispatcher.loadGrpcService();

try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@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();
} 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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
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.ReflectUtil;

import java.lang.invoke.MethodHandle;
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;
import java.util.Optional;

/**
* @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";

private Map<String, MethodHandle> grpcMethodInvokeMap = new HashMap<>();

private Map<String, Boolean> grpcMethodStreamMap = new HashMap<>();

public void loadGrpcService() {
List<Class<?>> 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();

// 处理 method
MethodHandles.Lookup lookup = MethodHandles.lookup();
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method : declaredMethods) {
if (method.isAnnotationPresent(GrpcMethod.class)) {
GrpcMethod grpcMethod = method.getAnnotation(GrpcMethod.class);
MethodHandle methodHandle = lookup.unreflect(method);
String grpcMethodKey = generateGrpcMethodKey(grpcService.value(), grpcMethod.value());
grpcMethodInvokeMap.put(grpcMethodKey, methodHandle.bindTo(instance));
grpcMethodStreamMap.put(grpcMethodKey, grpcMethod.stream());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

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);
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();
// 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);
}

/**
* 获取指定 service method 对应的入参类型
*
* @param serviceName
* @param methodName
* @return
*/
public 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) {
request.setStream(
Optional.ofNullable(grpcMethodStreamMap.get(generateGrpcMethodKey(request.getService(), request.getMethod())))
.orElse(false)
);
request.setStreamFirstData(true);
}
}
Loading
Loading