gRPC 在 Java 中的入门实例

红太狼 2023-07-02 05:23 191阅读 0赞

一、前言

经过前面三篇 Protobuf 相关文章的学习,相信大家已经对 Protobuf 有所掌握。前文说过, ProtoBuf 很适合做数据存储或 RPC 数据交换格式。可以用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

本节将介绍在 Java 中如何使用 gRPC 和 Protouf。gRpc 也是 Google 推出的 RPC 框架,由于师出同门,Protobuf 和 gRPC 可以非常容易的结合在一起,甚至于使用 Protobuf Maven 插件就可以自动生成 gRPC 代码。

如果你还没有看过前序文章,点击这里查看:

  • Protobuf 学习手册——语法篇
  • Protobuf 学习手册——编码篇
  • Protobuf 在 Java 中的入门实例

在本文中,将为大家实现一个 gRPC 的入门程序,本篇文章的代码源码及更多的 gRPC 示例程序,见下方链接。

源码地址: https://github.com/jitwxs/blog-sample/tree/master/Java/grpc\_demo

二、Protobuf 源文件

本文给大家演示的例子是:根据年龄查询用户列表。首先新建一个 Maven 项目,在 src/main 目录下(和 java 目录同级)创建 proto 文件夹,用于存放 .proto 文件,该目录也是 Protobuf 的 Maven 插件默认扫描的文件夹。

在该文件夹中创建 message.proto 文件,用于表示用户实体。

  1. syntax = "proto3";
  2. option java_package = "jit.wxs.grpc.dto";
  3. option java_outer_classname = "MessageProto";
  4. message User {
  5. int32 age = 1;
  6. string name = 2;
  7. }

简单介绍下:

  • syntax = "proto3";:使用 proto3 编译,否则默认使用 proto2 编译。
  • option java_package:指定当前文件编译成 Java 类后的 Package 包路径。
  • option java_outer_classname:指定当前文件变异成 Java 类后的文件名。

下面创建 grpc_user.proto 文件,用于定义根据年龄查询用户列表的 rpc 接口。

  1. syntax = "proto3";
  2. option java_package = "jit.wxs.grpc.rpc";
  3. option java_outer_classname = "UserRpcProto";
  4. import "message.proto";
  5. message AgeRequest {
  6. int32 age = 1;
  7. }
  8. message UserResponse {
  9. int32 code = 1;
  10. string msg = 2;
  11. repeated User user = 3;
  12. }
  13. service UserRpcService {
  14. rpc listByAge(AgeRequest) returns(UserResponse);
  15. }

在该文件中,定义一个名为 UserRpcService 的 Service 类,在其中定义一个名为 listByAge 的 rpc 接口,该接口的入参为 AgeRequest 和 UserResponse。

三、生成 Java 类

编辑 POM 文件如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <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">
  3. <modelVersion>4.0.0</modelVersion>
  4. <groupId>jit.wxs</groupId>
  5. <artifactId>grpc</artifactId>
  6. <version>1.0-SNAPSHOT</version>
  7. <properties>
  8. <protobuf.version>3.11.0</protobuf.version>
  9. <grpc.version>1.26.0</grpc.version>
  10. </properties>
  11. <dependencies>
  12. <!-- Protobuf Dependency -->
  13. <dependency>
  14. <groupId>com.google.protobuf</groupId>
  15. <artifactId>protobuf-java</artifactId>
  16. <version>${protobuf.version}</version>
  17. </dependency>
  18. <!-- gRPC Dependency Start -->
  19. <dependency>
  20. <groupId>io.grpc</groupId>
  21. <artifactId>grpc-netty-shaded</artifactId>
  22. <version>${grpc.version}</version>
  23. </dependency>
  24. <dependency>
  25. <groupId>io.grpc</groupId>
  26. <artifactId>grpc-protobuf</artifactId>
  27. <version>${grpc.version}</version>
  28. </dependency>
  29. <dependency>
  30. <groupId>io.grpc</groupId>
  31. <artifactId>grpc-stub</artifactId>
  32. <version>${grpc.version}</version>
  33. </dependency>
  34. <!-- gRPC Dependency End -->
  35. <dependency>
  36. <groupId>com.googlecode.protobuf-java-format</groupId>
  37. <artifactId>protobuf-java-format</artifactId>
  38. <version>1.4</version>
  39. </dependency>
  40. <dependency>
  41. <groupId>org.apache.commons</groupId>
  42. <artifactId>commons-lang3</artifactId>
  43. <version>3.10</version>
  44. </dependency>
  45. </dependencies>
  46. <build>
  47. <extensions>
  48. <extension>
  49. <groupId>kr.motd.maven</groupId>
  50. <artifactId>os-maven-plugin</artifactId>
  51. <version>1.6.2</version>
  52. </extension>
  53. </extensions>
  54. <plugins>
  55. <plugin>
  56. <groupId>org.xolstice.maven.plugins</groupId>
  57. <artifactId>protobuf-maven-plugin</artifactId>
  58. <version>0.6.1</version>
  59. <configuration>
  60. <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
  61. <pluginId>grpc-java</pluginId>
  62. <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
  63. </configuration>
  64. <executions>
  65. <execution>
  66. <goals>
  67. <!-- for protobuf -->
  68. <goal>compile</goal>
  69. <!-- for grpc -->
  70. <goal>compile-custom</goal>
  71. </goals>
  72. </execution>
  73. </executions>
  74. </plugin>
  75. </plugins>
  76. </build>
  77. </project>
  1. 引入 1 个 Protobuf 依赖、3 个 gRPC 依赖和 2 个工具包依赖;
  2. 引入 os-maven-plugin 插件,用于获取当前运行系统信息。
  3. 引入 protobuf-maven-plugin 插件,用于生成 Protobuf Java 类和 gRPC Java 类。

    1. <protocArtifact>:Protobuf 执行文件路径。
    2. <pluginId> + <pluginArtifact>:指定 gRPC 插件执行文件路径。
    3. <goal>compile</goal>:生成 Protobuf Java 类命令。
    4. <goal>compile-custom</goal>:生成 gRPC Java 类命令。

点开 IDEA Maven 插件面板,同时选中 protobuf:compileprotobuf:compile-custom 命令,右击 Run Maven Build 运行,即可得到生成的 Java 类,如下图所示。
20200411215225665.png

四、gRPC 服务端与客户端

4.1 gRPC 实现类

创建类 UserRpcServiceImpl,并去实现刚刚 Maven 插件生成的 UserRpcServiceGrpc.UserRpcServiceImplBase 接口:

  1. package jit.wxs.grpc.common;
  2. import io.grpc.stub.StreamObserver;
  3. import jit.wxs.grpc.dto.MessageProto;
  4. import jit.wxs.grpc.rpc.UserRpcProto;
  5. import jit.wxs.grpc.rpc.UserRpcServiceGrpc;
  6. import org.apache.commons.lang3.RandomStringUtils;
  7. import org.apache.commons.lang3.RandomUtils;
  8. import java.util.ArrayList;
  9. import java.util.List;
  10. import java.util.logging.Logger;
  11. import java.util.stream.IntStream;
  12. /** * @author jitwxs * @date 2019年12月20日 0:53 */
  13. public class UserRpcServiceImpl extends UserRpcServiceGrpc.UserRpcServiceImplBase {
  14. private static final Logger logger = Logger.getLogger(UserRpcServiceImpl.class.getName());
  15. @Override
  16. public void listByAge(UserRpcProto.AgeRequest request, StreamObserver<UserRpcProto.UserResponse> responseObserver) {
  17. logger.info("Server Rec listByAge request...");
  18. // 构造响应,模拟业务逻辑
  19. UserRpcProto.UserResponse response = UserRpcProto.UserResponse.newBuilder()
  20. .setCode(0)
  21. .setMsg("success")
  22. .addUser(MessageProto.User.newBuilder()
  23. .setName(RandomStringUtils.randomAlphabetic(5))
  24. .setAge(request.getAge()).build())
  25. .addUser(MessageProto.User.newBuilder()
  26. .setName(RandomStringUtils.randomAlphabetic(5))
  27. .setAge(request.getAge()).build())
  28. .addUser(MessageProto.User.newBuilder()
  29. .setName(RandomStringUtils.randomAlphabetic(5))
  30. .setAge(request.getAge()).build())
  31. .build();
  32. responseObserver.onNext(response);
  33. responseObserver.onCompleted();
  34. }
  35. }

listByAge() 方法中编写当我们接收到 request 请求时的行为,并将结果放入 response 中。这里我模拟了下业务逻辑,返回了三个年龄等于请求参数的用户。

4.2 gRPC 服务端

既然是 RPC 框架,那么就会有服务端和客户端。首先创建服务端:

  1. package jit.wxs.grpc.example1;
  2. import io.grpc.Server;
  3. import io.grpc.ServerBuilder;
  4. import jit.wxs.grpc.common.Constant;
  5. import jit.wxs.grpc.common.UserRpcServiceImpl;
  6. import java.io.IOException;
  7. import java.util.concurrent.TimeUnit;
  8. import java.util.logging.Logger;
  9. /** * Grpc 服务端 * @author jitwxs * @date 2019年12月20日 1:03 */
  10. public class Example1Server {
  11. private static final Logger logger = Logger.getLogger(Example1Server.class.getName());
  12. private Server server;
  13. public static void main(String[] args) throws IOException, InterruptedException {
  14. final Example1Server server = new Example1Server();
  15. server.start();
  16. server.blockUntilShutdown();
  17. }
  18. private void start() throws IOException {
  19. server = ServerBuilder.forPort(Constant.RUNNING_PORT)
  20. .addService(new UserRpcServiceImpl())
  21. .build()
  22. .start();
  23. logger.info("Server started...");
  24. // 程序停止钩子
  25. Runtime.getRuntime().addShutdownHook(new Thread(() -> {
  26. // Use stderr here since the logger may have been reset by its JVM shutdown hook.
  27. System.err.println("*** shutting down gRPC server since JVM is shutting down");
  28. try {
  29. Example1Server.this.stop();
  30. } catch (InterruptedException e) {
  31. e.printStackTrace(System.err);
  32. }
  33. System.err.println("*** server shut down");
  34. }));
  35. }
  36. /** * 停止服务 */
  37. private void stop() throws InterruptedException {
  38. if (server != null) {
  39. server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
  40. }
  41. }
  42. /** * Await termination on the main thread since the grpc library uses daemon threads. */
  43. private void blockUntilShutdown() throws InterruptedException {
  44. if (server != null) {
  45. server.awaitTermination();
  46. }
  47. }
  48. }
  • start() 方法中,Server 服务绑定了 8848 端口,并将 UserRpcServiceImpl 加入到 Service 列表中。
  • 添加一个 ShutdownHook 的回调钩子,当程序终止的前一刻,该钩子会被回调,方法中执行 stop() 方法停止服务端服务。

4.3 gRPC 客户端

最后编写客户端去请求服务端。

  1. package jit.wxs.grpc.example1;
  2. import io.grpc.ManagedChannel;
  3. import io.grpc.ManagedChannelBuilder;
  4. import io.grpc.StatusRuntimeException;
  5. import jit.wxs.grpc.common.Constant;
  6. import jit.wxs.grpc.common.ProtoUtils;
  7. import jit.wxs.grpc.rpc.UserRpcProto;
  8. import jit.wxs.grpc.rpc.UserRpcServiceGrpc;
  9. import java.util.concurrent.TimeUnit;
  10. import java.util.logging.Level;
  11. import java.util.logging.Logger;
  12. /** * Grpc 客户端 * @author jitwxs * @date 2019年12月20日 1:06 */
  13. public class Example1Client {
  14. private static final Logger logger = Logger.getLogger(Example1Client.class.getName());
  15. public static void main(String[] args) throws Exception {
  16. // STEP1 构造 Channel 和 BlockingStub
  17. ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", Constant.RUNNING_PORT)
  18. // Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid needing certificates.
  19. .usePlaintext()
  20. .build();
  21. UserRpcServiceGrpc.UserRpcServiceBlockingStub blockingStub = UserRpcServiceGrpc.newBlockingStub(channel);
  22. int requestAge = 20;
  23. logger.info("Will try to query age = " + requestAge + " ...");
  24. // STEP2 发起 gRPC 请求
  25. UserRpcProto.AgeRequest request = UserRpcProto.AgeRequest.newBuilder().setAge(20).build();
  26. try {
  27. UserRpcProto.UserResponse response = blockingStub.listByAge(request);
  28. logger.info("Response: " + ProtoUtils.toStr(response));
  29. } catch (StatusRuntimeException e) {
  30. logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
  31. } finally {
  32. // STEP3 关闭 Channel
  33. channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
  34. }
  35. }
  36. }
  • 构造方法中,通过 ManagedChannel 创建对服务端的连接。
  • 构造 Request 请求对象,通过 UserRpcServiceBlockingStub 的 listByAge() 接口去请求服务端,并输出返回的 response。

4.4 辅助类

本示例程序还使用了以下辅助类:

  1. package jit.wxs.grpc.common;
  2. public class Constant {
  3. public static int RUNNING_PORT = 8848;
  4. }
  5. package jit.wxs.grpc.common;
  6. import com.google.protobuf.Message;
  7. import com.googlecode.protobuf.format.JsonFormat;
  8. public class ProtoUtils {
  9. private static JsonFormat JSON_FORMAT;
  10. static {
  11. JSON_FORMAT = new JsonFormat();
  12. }
  13. public static String toStr(Message message) {
  14. return JSON_FORMAT.printToString(message);
  15. }
  16. }

五、运行程序

整个项目代码目录结构如下:

20200411220150795.png

首先启动服务端,然后启动客户端。服务端首先接收到客户端请求,输出:

  1. Server Rec listByAge request...

随后客户端接收到服务端响应,输出:

  1. Response: { "msg": "success","user": [{ "age": 20,"name": "Rczby"},{ "age": 20,"name": "KXVdZ"},{ "age": 20,"name": "setgc"}]}

发表评论

表情:
评论列表 (有 0 条评论,191人围观)

还没有评论,来说两句吧...

相关阅读

    相关 gRPC快速入门

    gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grp

    相关 GRPC基础入门

      项目中要使用rpc协议框架来实现两个系统之间的接口调用。A系统调用B系统的相应接口,因为考虑到http请求会包含更多冗余信息,造成请求过大,因此选用了rpc众多框架中的gr