当前位置:   article > 正文

SpringBoot整合grpc_springboot grpc

springboot grpc

目录

一、简介

二、下图是通信流程模型 

三、准备

四、环境搭建

1、服务端server

1)pom文件

2)创建proto用于生成proto目标文件

 3)服务端yml配置

4)创建GrpcServerService实现服务端接口

2、客户端client

1)pom文件

2)创建proto用于生成proto目标文件

 3)客户端yml配置

4)创建GrpcClientService客户端类

5)创建SimpleGrpcController

五、启动/测试

1、服务端测试

2、客户端测试 

六、问题

七、源码验证解析

客户端

服务端 


一、简介

之所以会说grpc是高性能框架,默认情况下,gRPC基于Netty进行服务端和客户端互通,使用Protocol Buffers进行传输,这是Google用于序列化结构化数据的成熟开源机制,基于proto3情况下它还是一个跨语言的RPC框架(目前支持Java、c++、Dart、Python、Objective-C、c#、lite-runtime (Android Java)、Ruby和JavaScript(来自协议缓冲区GitHub repo),以及来自golang/protobuf官方包的Go语言生成器) 

如果对grpc不够了解可以参考Introduction to gRPC | gRPC

二、下图是通信流程模型 

  • 客户端(gRPC Stub)调用 A 方法,发起 RPC 调用。
  • 对请求信息使用 Protobuf 进行对象序列化压缩(IDL)。
  • 服务端(gRPC Server)接收到请求后,解码请求体,进行业务逻辑处理并返回。
  • 对响应结果使用 Protobuf 进行对象序列化压缩(IDL)。
  • 客户端接受到服务端响应,解码请求体。回调被调用的 A 方法,唤醒正在等待响应(阻塞)的客户端调用并返回响应结果。

三、准备

1、PROTOC下载及安装

下载地址

2、准备两个springboot项目,作为服务端和客户端

3、下载Apifox软件,用于直连服务端(可选)

下载地址

四、环境搭建

1、服务端server

1)pom文件
  1. <!--部分配置-->
  2. <dependencies>
  3. <dependency>
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot-starter</artifactId>
  6. </dependency>
  7. <dependency>
  8. <groupId>io.grpc</groupId>
  9. <artifactId>grpc-protobuf</artifactId>
  10. <version>${grpc.version}</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>io.grpc</groupId>
  14. <artifactId>grpc-stub</artifactId>
  15. <version>${grpc.version}</version>
  16. </dependency>
  17. <!--gRPC服务端-->
  18. <dependency>
  19. <groupId>net.devh</groupId>
  20. <artifactId>grpc-server-spring-boot-starter</artifactId>
  21. <version>2.13.1.RELEASE</version>
  22. </dependency>
  23. </dependencies>
  24. <properties>
  25. <grpc.version>1.42.1</grpc.version>
  26. <protobuf.version>3.7.1</protobuf.version>
  27. </properties>
  28. <build>
  29. <finalName>${project.artifactId}-${project.version}</finalName>
  30. <extensions>
  31. <extension>
  32. <groupId>kr.motd.maven</groupId>
  33. <artifactId>os-maven-plugin</artifactId>
  34. <version>1.6.2</version>
  35. </extension>
  36. </extensions>
  37. <plugins>
  38. <!-- Grpc coding plug-in-->
  39. <plugin>
  40. <groupId>org.xolstice.maven.plugins</groupId>
  41. <artifactId>protobuf-maven-plugin</artifactId>
  42. <version>0.6.1</version>
  43. <configuration>
  44. <!--suppress UnresolvedMavenProperty -->
  45. <!--&lt;!&ndash; ${os.detected.classifier} 变量由${os.detected.name} 和 ${os.detected.arch} 组成-->
  46. <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
  47. <pluginId>grpc-java</pluginId>
  48. <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
  49. <!-- protoSourceRoot 默认src/main/proto-->
  50. <!-- <protoSourceRoot>src/java/main/proto</protoSourceRoot>-->
  51. </configuration>
  52. <executions>
  53. <execution>
  54. <goals>
  55. <goal>compile</goal>
  56. <goal>compile-custom</goal>
  57. </goals>
  58. </execution>
  59. </executions>
  60. </plugin>
  61. </plugins>
  62. </build>
2)创建proto用于生成proto目标文件

注意:proto文件需要创建在src/main/proto下,因为pom使用的是protoSourceRoot默认路径

创建Simple.proto 

  1. syntax = "proto3"; // 协议版本
  2. // 选项配置
  3. option java_multiple_files = true;
  4. //生成位置
  5. option java_package = "com.na.model.proto";
  6. option java_outer_classname = "SimpleProto";
  7. service Simple {
  8. // 简单gRPC
  9. rpc OneToOne (MyRequest) returns (MyResponse) {
  10. }
  11. }
  12. message MyRequest {
  13. string name = 1;
  14. int32 value = 2;
  15. }
  16. message MyResponse {
  17. string message = 1;
  18. int64 result = 2;
  19. }

 生成方式有两种一种是通过命令,这里使用maven插件生成compile和compile-custom

 编译后可在target下看到生成的相应的java类

 3)服务端yml配置

这里只贴出关键代码,其他配置根据实际情况来

  1. # gRPC有关的配置,这里只需要配置服务端口号默认9090
  2. grpc:
  3. server:
  4. port: 19898
  5. spring:
  6. application:
  7. name: nacos-grpc

4)创建GrpcServerService实现服务端接口
  1. package com.na.grpc.server;
  2. import com.na.model.proto.MyRequest;
  3. import com.na.model.proto.MyResponse;
  4. import com.na.model.proto.SimpleGrpc;
  5. import io.grpc.stub.StreamObserver;
  6. import lombok.extern.slf4j.Slf4j;
  7. import net.devh.boot.grpc.server.service.GrpcService;
  8. /**
  9. * GrpcServerService.java中有几处需要注意:
  10. *
  11. * 是使用@GrpcService注解,再继承SimpleImplBase,这样就可以借助grpc-server-spring-boot-starter库将oneToOne暴露为gRPC服务;
  12. *
  13. * SimpleImplBase是前文中根据maven compile编译 proto文件自动生成的java代码,
  14. *
  15. * oneToOne方法中处理完毕业务逻辑后,调用responseObserver.onNext方法填入返回内容;
  16. *
  17. * 调用responseObserver.onCompleted方法表示本次gRPC服务完成;
  18. */
  19. @GrpcService
  20. @Slf4j
  21. public class GrpcServerService extends SimpleGrpc.SimpleImplBase {
  22. @Override
  23. public void oneToOne(MyRequest request, StreamObserver<MyResponse> responseObserver) {
  24. log.info("接收客户端数据{}", request);
  25. MyResponse response = MyResponse.newBuilder().setMessage( request.getName()).build();
  26. responseObserver.onNext(response);
  27. responseObserver.onCompleted();
  28. }
  29. }

2、客户端client

1)pom文件
  1. <!--部分配置-->
  2. <dependencies>
  3. <dependency>
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot-starter</artifactId>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.springframework.boot</groupId>
  9. <artifactId>spring-boot-starter-web</artifactId>
  10. </dependency>
  11. <dependency>
  12. <groupId>io.grpc</groupId>
  13. <artifactId>grpc-protobuf</artifactId>
  14. <version>${grpc.version}</version>
  15. </dependency>
  16. <dependency>
  17. <groupId>io.grpc</groupId>
  18. <artifactId>grpc-stub</artifactId>
  19. <version>${grpc.version}</version>
  20. </dependency>
  21. <!--gRPC客户端-->
  22. <dependency>
  23. <groupId>net.devh</groupId>
  24. <artifactId>grpc-client-spring-boot-starter</artifactId>
  25. <version>2.14.0.RELEASE</version>
  26. </dependency>
  27. </dependencies>
  28. <properties>
  29. <grpc.version>1.42.1</grpc.version>
  30. <protobuf.version>3.7.1</protobuf.version>
  31. </properties>
  32. <build>
  33. <finalName>${project.artifactId}-${project.version}</finalName>
  34. <extensions>
  35. <extension>
  36. <groupId>kr.motd.maven</groupId>
  37. <artifactId>os-maven-plugin</artifactId>
  38. <version>1.6.2</version>
  39. </extension>
  40. </extensions>
  41. <plugins>
  42. <!-- Grpc coding plug-in-->
  43. <plugin>
  44. <groupId>org.xolstice.maven.plugins</groupId>
  45. <artifactId>protobuf-maven-plugin</artifactId>
  46. <version>0.6.1</version>
  47. <configuration>
  48. <!--suppress UnresolvedMavenProperty -->
  49. <!--&lt;!&ndash; ${os.detected.classifier} 变量由${os.detected.name} 和 ${os.detected.arch} 组成-->
  50. <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
  51. <pluginId>grpc-java</pluginId>
  52. <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
  53. <!-- protoSourceRoot 默认src/main/proto-->
  54. <!-- <protoSourceRoot>src/java/main/proto</protoSourceRoot>-->
  55. </configuration>
  56. <executions>
  57. <execution>
  58. <goals>
  59. <goal>compile</goal>
  60. <goal>compile-custom</goal>
  61. </goals>
  62. </execution>
  63. </executions>
  64. </plugin>
  65. </plugins>
  66. </build>
2)创建proto用于生成proto目标文件

与服务端操作一样,这里省略。。。。。。

 3)客户端yml配置

这里只贴出关键代码,其他配置根据实际情况来

  1. server:
  2. port: 11278
  3. servlet:
  4. context-path: /
  5. # grpc配置
  6. grpc:
  7. # grpc clienT相关配置
  8. client:
  9. # 服务名(不同服务名可对应不同配置)
  10. # nacos-grpc是服务端配置的名字,GrpcClient注解会用到
  11. nacos-grpc:
  12. # gRPC服务端地址
  13. # address: 'dns://127.0.0.1:19898'
  14. address: 'static://127.0.0.1:19898'
  15. # 是否开启保持连接(长连接)
  16. enableKeepAlive: true
  17. # 保持连接时长(默认20s)
  18. keepAliveTimeout: 20s
  19. # 没有RPC调用时是否保持连接(默认false,可禁用避免额外消耗CPU)
  20. keepAliveWithoutCalls: false
  21. # 客户端负载均衡策略(round_robin(默认), pick_first)
  22. defaultLoadBalancingPolicy: round_robin
  23. # 通信类型
  24. # plaintext | plaintext_upgrade | tls
  25. # 明文通信且http/2 | 明文通信且升级http/1.1为http/2 | 使用TLS(ALPN/NPN)通信
  26. negotiationType: plaintext
4)创建GrpcClientService客户端类
  1. package com.na.grpc.client;
  2. import com.na.model.proto.MyRequest;
  3. import com.na.model.proto.MyResponse;
  4. import com.na.model.proto.SimpleGrpc;
  5. import io.grpc.StatusRuntimeException;
  6. import lombok.extern.slf4j.Slf4j;
  7. import net.devh.boot.grpc.client.inject.GrpcClient;
  8. import org.springframework.stereotype.Service;
  9. /**
  10. * GrpcClientService类有几处要注意的地方:
  11. * <p>
  12. * 用@Service将GrpcClientService注册为spring的普通bean实例;
  13. * <p>
  14. * 用@GrpcClient修饰SimpleBlockingStub,这样就可以通过grpc-client-spring-boot-starter库发起gRPC调用,被调用的服务端信息来自名为nacos-grpc服务端配置;
  15. * <p>
  16. * SimpleBlockingStub来自前文中根据helloworld.proto生成的java代码;
  17. * <p>
  18. * SimpleBlockingStub.oneToOne方法会远程调用nacos-grpc应用的gRPC服务;
  19. */
  20. @Service
  21. @Slf4j
  22. public class GrpcClientService {
  23. @GrpcClient("nacos-grpc")
  24. private SimpleGrpc.SimpleBlockingStub simpleStub;
  25. public String oneToOne(final String name) {
  26. try {
  27. final MyResponse response = this.simpleStub.oneToOne(MyRequest.newBuilder().setName(name).build());
  28. return response.getMessage();
  29. } catch (final StatusRuntimeException e) {
  30. log.error("FAILED with " + e.getStatus().getCode().name() + ",and e:{}", e.getMessage());
  31. return "FAILED with " + e.getStatus().getCode().name() + ",and e:" + e.getMessage();
  32. }
  33. }
  34. }
5)创建SimpleGrpcController

定义了两种连接方法getOneToOne和testForAddress

  1. package com.na.controller;
  2. import com.na.base.BaseResponse;
  3. import com.na.grpc.client.GrpcClientService;
  4. import com.na.model.proto.MyRequest;
  5. import com.na.model.proto.SimpleGrpc;
  6. import io.grpc.ManagedChannel;
  7. import io.grpc.ManagedChannelBuilder;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.web.bind.annotation.GetMapping;
  10. import org.springframework.web.bind.annotation.RequestMapping;
  11. import org.springframework.web.bind.annotation.RestController;
  12. /**
  13. * @Description 测试grpc接口
  14. * @Author kele
  15. * @Data 2023/9/6 15:35
  16. */
  17. @RestController
  18. @RequestMapping("grpc")
  19. public class SimpleGrpcController {
  20. @Autowired
  21. private GrpcClientService service;
  22. @GetMapping("getOneToOne")
  23. public BaseResponse getOneToOne() {
  24. return new BaseResponse(service.oneToOne("客户端kele连接"));
  25. }
  26. @GetMapping("testForAddress")
  27. public BaseResponse testForAddress() {
  28. ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 19898)
  29. .usePlaintext()
  30. .build();
  31. MyRequest request = MyRequest.newBuilder().setName("kele的访问").build();
  32. SimpleGrpc.SimpleBlockingStub stub = SimpleGrpc.newBlockingStub(channel);
  33. return new BaseResponse(stub.oneToOne(request));
  34. }
  35. }

五、启动/测试

1、服务端测试

1)先启动服务端启动的netty端口为19898

2)使用Apifox连接服务端

创建grpc操作文档

可以看到,在Apifox中导入了Simple.proto帮我自动生成了客户端连接操作,自己只需要改一下19898端口和请求的参数

2、客户端测试 

1)启动客户端web端口为11278

2)直接通过web访问

这里访问的是getOneToOne方法, 地址http://localhost:11278/grpc/getOneToOne

另外的testForAddress方法可以自己玩一下生产一般不会那样写

六、问题报错

1、出现无法访问com.google.protobuf.GeneratedMessageV3 找不到com.google.protobuf.GeneratedMessageV,在pom中添加以下依赖。

  1. <dependency>
  2. <groupId>com.google.protobuf</groupId>
  3. <artifactId>protobuf-java</artifactId>
  4. <version>${protobuf.version}</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.google.protobuf</groupId>
  8. <artifactId>protobuf-java-util</artifactId>
  9. <version>${protobuf.version}</version>
  10. </dependency>

2、出现:io.grpc.StatusRuntimeException: UNAVAILABLE

1、检查下IP是否能ping通,IP、端口 是否正确

2、Server是否打开

3、连接中如果有证书,证书是否有效

4、无证书的,是否写了明文连接,例如:

5、以上都没问题可以查看服务端是否添加了这个依赖,grpc-netty-shaded包中可能会存在core的冲突

  1. <dependency>
  2. <groupId>io.grpc</groupId>
  3. <artifactId>grpc-netty-shaded</artifactId>
  4. <version>${grpc.version}</version>
  5. </dependency>

七、源码验证解析

客户端

1、客户端序列化,可以查看MyRequest

 2、在客户端调用SimpleGrpc.SimpleBlockingStub.oneToOne方法时,已经将name序列化传输了

3、再往下可以看到客户端ClientCalls中,采用的是异步的方式进行发送二进制数据,等结束执行后再进行判断是否执行结束,

 waitAndDrain()即为等待,直到有一个Runnable,然后执行它和所有在它之后排队的Runnables。一次只能由一个线程调用。poll()方法用于从队列中取出并返回头部的元素,如果队列为空,则返回null。之后又将当前线程赋给了waiter

 用的是线程池,在服务端断点的情况下,后续请求会进入等待队列。

服务端 

待更新。。。。。。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/128698?site
推荐阅读
相关标签
  

闽ICP备14008679号