目录
一、gRPC 整合 SpringBoot
1.1、创建项目
1.2、天坑(看前须知)!
1.2.1、天坑背景
1.2.2、解决天坑
1.3、api 开发
1.4、server 开发
1.5、client 开发
1.6、演示效果
一、gRPC 整合 SpringBoot
1.1、创建项目
- api:编写 proto 文件(message 和 service),生成 Java 代码.
- client:引入 api 模块,客户端,通过 stub 代理对 server 进行远程调用.
- server:引入 api 模块,服务端,实现 proto 文件中描述 service,为 client 提供服务.
1.2、天坑(看前须知)!
1.2.1、天坑背景
a)如果你使用 github 最新的依赖,那么 api 中引入的依赖如下(grpc 依赖版本都是 1.60.0):
<dependencies><dependency><groupId>io.grpc</groupId><artifactId>grpc-netty-shaded</artifactId><version>1.60.0</version><scope>runtime</scope></dependency><dependency><groupId>io.grpc</groupId><artifactId>grpc-protobuf</artifactId><version>1.60.0</version></dependency><dependency><groupId>io.grpc</groupId><artifactId>grpc-stub</artifactId><version>1.60.0</version></dependency><dependency> <!-- necessary for Java 9+ --><groupId>org.apache.tomcat</groupId><artifactId>annotations-api</artifactId><version>6.0.53</version><scope>provided</scope></dependency></dependencies>
b)而你在 client 中引入 github 最新依赖(server 中同理),是这样的:
<dependency><groupId>net.devh</groupId><artifactId>grpc-client-spring-boot-starter</artifactId><version>2.14.0.RELEASE</version></dependency>
c)当你写完代码后启动项目是这样的:
2024-01-06 16:20:22.390 ERROR 22720 --- [ main] o.s.boot.SpringApplication : Application run failedorg.springframework.beans.factory.BeanCreationException: Error creating bean with name 'helloController' defined in file [D:\codeRepositories\Gitee\ssm\grpc_boot_demo\client\target\classes\com\cyk\controller\HelloController.class]: Initialization of bean failed; nested exception is java.lang.IllegalStateException: Failed to create channel: grpc-serverat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:628) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.25.jar:5.3.25]at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.25.jar:5.3.25]at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.9.jar:2.7.9]at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) ~[spring-boot-2.7.9.jar:2.7.9]at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.9.jar:2.7.9]at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.7.9.jar:2.7.9]at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.9.jar:2.7.9]at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.9.jar:2.7.9]at com.cyk.ClientApplication.main(ClientApplication.java:10) ~[classes/:na]
Caused by: java.lang.IllegalStateException: Failed to create channel: grpc-serverat net.devh.boot.grpc.client.inject.GrpcClientBeanPostProcessor.processInjectionPoint(GrpcClientBeanPostProcessor.java:218) ~[grpc-client-spring-boot-autoconfigure-2.14.0.RELEASE.jar:2.14.0.RELEASE]at net.devh.boot.grpc.client.inject.GrpcClientBeanPostProcessor.processFields(GrpcClientBeanPostProcessor.java:148) ~[grpc-client-spring-boot-autoconfigure-2.14.0.RELEASE.jar:2.14.0.RELEASE]at net.devh.boot.grpc.client.inject.GrpcClientBeanPostProcessor.postProcessBeforeInitialization(GrpcClientBeanPostProcessor.java:125) ~[grpc-client-spring-boot-autoconfigure-2.14.0.RELEASE.jar:2.14.0.RELEASE]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:440) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1796) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) ~[spring-beans-5.3.25.jar:5.3.25]... 15 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shadedNettyGrpcChannelFactory' defined in class path resource [net/devh/boot/grpc/client/autoconfigure/GrpcClientAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [net.devh.boot.grpc.client.channelfactory.GrpcChannelFactory]: Factory method 'shadedNettyGrpcChannelFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: io/grpc/inprocess/InProcessChannelBuilderat org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:658) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:638) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:233) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1284) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1245) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:494) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:349) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1172) ~[spring-context-5.3.25.jar:5.3.25]at net.devh.boot.grpc.client.inject.GrpcClientBeanPostProcessor.getChannelFactory(GrpcClientBeanPostProcessor.java:239) ~[grpc-client-spring-boot-autoconfigure-2.14.0.RELEASE.jar:2.14.0.RELEASE]at net.devh.boot.grpc.client.inject.GrpcClientBeanPostProcessor.processInjectionPoint(GrpcClientBeanPostProcessor.java:213) ~[grpc-client-spring-boot-autoconfigure-2.14.0.RELEASE.jar:2.14.0.RELEASE]... 20 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [net.devh.boot.grpc.client.channelfactory.GrpcChannelFactory]: Factory method 'shadedNettyGrpcChannelFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: io/grpc/inprocess/InProcessChannelBuilderat org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.3.25.jar:5.3.25]at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-5.3.25.jar:5.3.25]... 37 common frames omitted
Caused by: java.lang.NoClassDefFoundError: io/grpc/inprocess/InProcessChannelBuilderat net.devh.boot.grpc.client.autoconfigure.GrpcClientAutoConfiguration.shadedNettyGrpcChannelFactory(GrpcClientAutoConfiguration.java:161) ~[grpc-client-spring-boot-autoconfigure-2.14.0.RELEASE.jar:2.14.0.RELEASE]at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.25.jar:5.3.25]... 38 common frames omitted
Caused by: java.lang.ClassNotFoundException: io.grpc.inprocess.InProcessChannelBuilderat java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[na:na]at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na]at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) ~[na:na]... 44 common frames omittedProcess finished with exit code 1
1.2.2、解决天坑
a)打开 External Libraries,可以看到同一个依赖引入了两次,并且版本不一样.
b)这是因为我们在 client 模块中引入的依赖 "grpc-client-spring-boot-starter" 已经包含了 api 模块中引入的官方最新依赖,但是 client 中包含的依赖是 1.51.0 版本,而 api 模块中引入的是 1.60.0 版本,因此解决办法就是把 api 模块中引入的对应依赖进行降级.
1.3、api 开发
syntax = "proto3";option java_multiple_files = false;
option java_package = "com.cyk";
option java_outer_classname = "HelloProto";service HelloService {rpc hello(HelloRequest) returns(HelloResponse) {};
}message HelloRequest {string msg = 1;
}message HelloResponse {string result = 1;
}
配置文件如下:
spring:application:name: boot_server# 不启动 Tomcat 容器: server 这边只需要提供gRPC远程调用服务即可# ,Tomcat 这种 Web 服务用不上启动反而浪费额外资源和端口main:web-application-type: none# gRPC 启动端口
grpc:server:port: 9000
1.4、server 开发
a)创建一个类,添加 @GrpcService 注解(注入容器中,表示它是一个 proto 文件中描述的 service 的实现类),让他继承对应的 Base 接口,重写 proto 文件中提供 service 下的方法即可.
@GrpcService
public class HelloService extends HelloServiceGrpc.HelloServiceImplBase {@Overridepublic void hello(HelloProto.HelloRequest request, StreamObserver<HelloProto.HelloResponse> responseObserver) {String msg = request.getMsg();System.out.println("收到客户端请求: " + msg);HelloProto.HelloResponse response = HelloProto.HelloResponse.newBuilder().setResult("ok!").build();responseObserver.onNext(response);responseObserver.onCompleted();}}
1.5、client 开发
通过 @GrpcClient 注解注入我们所需的代理对象(下面以阻塞式为例)
@RestController
public class HelloController {@GrpcClient("grpc-server")private HelloServiceGrpc.HelloServiceBlockingStub stub;@RequestMapping("/hello")public String hello(String msg) {HelloProto.HelloRequest request = HelloProto.HelloRequest.newBuilder().setMsg(msg).build();HelloProto.HelloResponse response = stub.hello(request);return response.getResult();}}
配置文件如下:
spring:application:name: boot-clientgrpc:client:grpc-server: # 自定义服务名address: 'static://127.0.0.1:9000' # 调用 gRPC 的地址negotiation-type: plaintext # 明文传输