目录
一、gRPC 整合 SpringBoot
1.1、创建项目
1.2、天坑(看前须知)!
1.2.1、天坑背景
1.2.2、解决天坑
1.3、api 开发
1.4、server 开发
1.5、client 开发
1.6、演示效果
a)如果你使用 github 最新的依赖,那么 api 中引入的依赖如下(grpc 依赖版本都是 1.60.0):
io.grpc
grpc-netty-shaded
1.60.0
runtime
io.grpc
grpc-protobuf
1.60.0
io.grpc
grpc-stub
1.60.0
org.apache.tomcat
annotations-api
6.0.53
provided
b)而你在 client 中引入 github 最新依赖(server 中同理),是这样的:
net.devh
grpc-client-spring-boot-starter
2.14.0.RELEASE
c)当你写完代码后启动项目是这样的:
2024-01-06 16:20:22.390 ERROR 22720 --- [ main] o.s.boot.SpringApplication : Application run failed
org.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-server
at 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-server
at 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/InProcessChannelBuilder
at 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/InProcessChannelBuilder
at 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/InProcessChannelBuilder
at 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.InProcessChannelBuilder
at 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 omitted
Process finished with exit code 1
a)打开 External Libraries,可以看到同一个依赖引入了两次,并且版本不一样.
b)这是因为我们在 client 模块中引入的依赖 "grpc-client-spring-boot-starter" 已经包含了 api 模块中引入的官方最新依赖,但是 client 中包含的依赖是 1.51.0 版本,而 api 模块中引入的是 1.60.0 版本,因此解决办法就是把 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
a)创建一个类,添加 @GrpcService 注解(注入容器中,表示它是一个 proto 文件中描述的 service 的实现类),让他继承对应的 Base 接口,重写 proto 文件中提供 service 下的方法即可.
@GrpcService
public class HelloService extends HelloServiceGrpc.HelloServiceImplBase {
@Override
public void hello(HelloProto.HelloRequest request, StreamObserver responseObserver) {
String msg = request.getMsg();
System.out.println("收到客户端请求: " + msg);
HelloProto.HelloResponse response = HelloProto.HelloResponse
.newBuilder()
.setResult("ok!")
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
通过 @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-client
grpc:
client:
grpc-server: # 自定义服务名
address: 'static://127.0.0.1:9000' # 调用 gRPC 的地址
negotiation-type: plaintext # 明文传输