gRPC - gRPC 整合 SpringBoot(全代码 + 避坑!)

发布时间:2024年01月06日

目录

一、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 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

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 {

    @Override
    public 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-client

grpc:
  client:
    grpc-server: # 自定义服务名
      address: 'static://127.0.0.1:9000' # 调用 gRPC 的地址
      negotiation-type: plaintext # 明文传输

1.6、演示效果

文章来源:https://blog.csdn.net/CYK_byte/article/details/135427954
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。