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 40 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 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
e594712
update: add more unit test and log
fengye404 Oct 12, 2024
45b6027
update: Migration from jprotobuf serialization to protobuf:protoc; mi…
fengye404 Oct 14, 2024
b2a95f4
update: optimize unit tests
fengye404 Oct 15, 2024
850a9b8
update: optimize unit tests
fengye404 Oct 15, 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
163 changes: 163 additions & 0 deletions arthas-grpc-server/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?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>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.arthas</groupId>
<artifactId>arthas-repackage-logger</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</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,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();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
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;
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.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;

import java.lang.invoke.MethodHandles;

/**
* @author: FengYe
* @date: 2024/7/3 上午12:30
* @description: ArthasGrpcServer
*/
public class ArthasGrpcServer {

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(10);

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

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(Http2FrameCodecBuilder.forServer().build());
ch.pipeline().addLast(new Http2Handler(grpcDispatcher));
}
});
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();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
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.utils.ByteUtil;
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 {

public static final String DEFAULT_GRPC_SERVICE_PACKAGE_NAME = "com.taobao.arthas.grpc.server.service.impl";

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

public static Map<String, MethodHandle> requestParseFromMap = new HashMap<>();
public static Map<String, MethodHandle> requestToByteArrayMap = new HashMap<>();

public static Map<String, MethodHandle> responseParseFromMap = new HashMap<>();
public static Map<String, MethodHandle> responseToByteArrayMap = new HashMap<>();

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

public void loadGrpcService(String grpcServicePackageName) {
List<Class<?>> classes = ReflectUtil.findClasses(Optional.ofNullable(grpcServicePackageName).orElse(DEFAULT_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 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, 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) {
e.printStackTrace();
}
}
}
}

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;
}

public GrpcResponse execute(GrpcRequest request) throws Throwable {
String service = request.getService();
String method = request.getMethod();
return this.execute(service, method, request.readData());
}

/**
* 获取指定 service method 对应的入参类型
*
* @param serviceName
* @param methodName
* @return
*/
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 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)
);
request.setStreamFirstData(true);
}
}
Loading
Loading