一. 摘要

  1. 此篇文章将用实例介绍grpc四种服务类型中的最普通的单项 rpc

二. 实践

整体项目如下:
image.png
其中cloud-grpc-java为maven项目,cloud-grpc-protos为定义接口项目。

1.通过protobuf定义接口和数据类型

在cloud-grpc-protos文件夹下创建hello.proto,内容如下:

  1. syntax = "proto3";
  2. option go_package = "pbfs/hello";
  3. option java_multiple_files = true;
  4. option java_package = "com.cloud.grpc.hello";
  5. option java_outer_classname = "HelloProto";
  6. option objc_class_prefix = "HL";
  7. package hello;
  8. service Hello {
  9. rpc SayHello (HelloRequest) returns (HelloResponse) {}
  10. }
  11. // The request message containing the user's name.
  12. message HelloRequest {
  13. string name = 1;
  14. }
  15. // The response message containing the greetings
  16. message HelloResponse {
  17. string message = 1;
  18. }

以上,一个 简单 RPC , 客户端使用存根发送请求到服务器并等待响应返回,就像平常的函数调用一样。定义一个SayHello rpc服务,入参:HelloRequest,返参:HelloResponse

2.maven 配置

创建一个如上图(cloud-grpc-java)的maven项目,pom.xml加入grpc开发相关配置,如下:

  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"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.cloud.grpc</groupId>
  6. <artifactId>java</artifactId>
  7. <version>1.0.0.0</version>
  8. <name>java</name>
  9. <description>Demo project for grpc for java</description>
  10. <properties>
  11. <java.version>11</java.version>
  12. <grpc.version>1.29.0</grpc.version>
  13. <protobuf.version>3.11.0</protobuf.version>
  14. </properties>
  15. <dependencies>
  16. <dependency>
  17. <groupId>io.grpc</groupId>
  18. <artifactId>grpc-netty-shaded</artifactId>
  19. <version>${grpc.version}</version>
  20. </dependency>
  21. <dependency>
  22. <groupId>io.grpc</groupId>
  23. <artifactId>grpc-protobuf</artifactId>
  24. <version>${grpc.version}</version>
  25. </dependency>
  26. <dependency>
  27. <groupId>io.grpc</groupId>
  28. <artifactId>grpc-stub</artifactId>
  29. <version>${grpc.version}</version>
  30. </dependency>
  31. <dependency> <!-- necessary for Java 9+ -->
  32. <groupId>org.apache.tomcat</groupId>
  33. <artifactId>annotations-api</artifactId>
  34. <version>6.0.53</version>
  35. <scope>provided</scope>
  36. </dependency>
  37. </dependencies>
  38. <build>
  39. <extensions>
  40. <extension>
  41. <groupId>kr.motd.maven</groupId>
  42. <artifactId>os-maven-plugin</artifactId>
  43. <version>1.5.0.Final</version>
  44. </extension>
  45. </extensions>
  46. <plugins>
  47. <plugin>
  48. <groupId>org.xolstice.maven.plugins</groupId>
  49. <artifactId>protobuf-maven-plugin</artifactId>
  50. <version>0.5.1</version>
  51. <configuration>
  52. <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
  53. <pluginId>grpc-java</pluginId>
  54. <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
  55. <protoSourceRoot>../cloud-grpc-protos</protoSourceRoot>
  56. </configuration>
  57. <executions>
  58. <execution>
  59. <goals>
  60. <goal>compile</goal>
  61. <goal>compile-custom</goal>
  62. </goals>
  63. </execution>
  64. </executions>
  65. </plugin>
  66. </plugins>
  67. </build>
  68. </project>

说明: ../cloud-grpc-protos 红色标注部分需要根据pom.xml与上面创建proto所在路径相匹配,这样protobuf 插件才会根据此目录找到定义的proto文件生成相关代码。

3.生成grpc,protobuf相关类

  1. mvn protobuf:compile
  2. mvn protobuf:compile-custom

同步pom.xml,即可在target下生成如下源码:
image.png

4.编写服务端,并运行

编写HelloServer.java如下

  1. package com.cloud.grpc.java.standard.server;
  2. import com.cloud.grpc.hello.HelloGrpc;
  3. import com.cloud.grpc.hello.HelloRequest;
  4. import com.cloud.grpc.hello.HelloResponse;
  5. import io.grpc.Server;
  6. import io.grpc.ServerBuilder;
  7. import io.grpc.stub.StreamObserver;
  8. import java.io.IOException;
  9. import java.util.concurrent.TimeUnit;
  10. public class HelloServer {
  11. private Server server;
  12. private void start() throws IOException {
  13. /* The port on which the server should run */
  14. int port = 50051;
  15. server = ServerBuilder.forPort(port)
  16. .addService(new HelloIml()) //这里可以添加多个模块
  17. .build()
  18. .start();
  19. System.out.println("Server started, listening on " + port);
  20. Runtime.getRuntime().addShutdownHook(new Thread() {
  21. @Override
  22. public void run() {
  23. // Use stderr here since the logger may have been reset by its JVM shutdown hook.
  24. System.err.println("*** shutting down gRPC server since JVM is shutting down");
  25. try {
  26. HelloServer.this.stop();
  27. } catch (InterruptedException e) {
  28. e.printStackTrace(System.err);
  29. }
  30. System.err.println("*** server shut down");
  31. }
  32. });
  33. }
  34. private void stop() throws InterruptedException {
  35. if (server != null) {
  36. server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
  37. }
  38. }
  39. private void blockUntilShutdown() throws InterruptedException {
  40. if (server != null) {
  41. server.awaitTermination();
  42. }
  43. }
  44. public static void main(String[] args) throws IOException, InterruptedException {
  45. final HelloServer server = new HelloServer();
  46. server.start();
  47. server.blockUntilShutdown();
  48. }
  49. private static class HelloIml extends HelloGrpc.HelloImplBase{
  50. @Override
  51. public void sayHello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
  52. // super.sayHello(request, responseObserver);
  53. HelloResponse helloResponse=HelloResponse.newBuilder().setMessage("Hello "+request.getName()+", I'm Java grpc Server").build();
  54. responseObserver.onNext(helloResponse);
  55. responseObserver.onCompleted();
  56. }
  57. }
  58. }

通过main函数启动:结果如下:
image.png

5.编写,运行客户端

编写HelloClient.java,如下:

  1. package com.cloud.grpc.java.standard.client;
  2. import com.cloud.grpc.hello.HelloGrpc;
  3. import com.cloud.grpc.hello.HelloRequest;
  4. import com.cloud.grpc.hello.HelloResponse;
  5. import io.grpc.ManagedChannel;
  6. import io.grpc.ManagedChannelBuilder;
  7. import java.util.concurrent.TimeUnit;
  8. public class HelloClient {
  9. //远程连接管理器,管理连接的生命周期
  10. private final ManagedChannel channel;
  11. private final HelloGrpc.HelloBlockingStub blockingStub;
  12. public HelloClient(String host, int port) {
  13. //初始化连接
  14. channel = ManagedChannelBuilder.forAddress(host, port)
  15. .usePlaintext()
  16. .build();
  17. //初始化远程服务Stub
  18. blockingStub = HelloGrpc.newBlockingStub(channel);
  19. }
  20. public void shutdown() throws InterruptedException {
  21. //关闭连接
  22. channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
  23. }
  24. public String sayHello(String name) {
  25. //构造服务调用参数对象
  26. HelloRequest request = HelloRequest.newBuilder().setName(name).build();
  27. //调用远程服务方法
  28. HelloResponse response = blockingStub.sayHello(request);
  29. //返回值
  30. return response.getMessage();
  31. }
  32. public static void main(String[] args) throws InterruptedException {
  33. HelloClient client = new HelloClient("127.0.0.1", 50051);
  34. //服务调用
  35. String content = client.sayHello("Java client");
  36. //打印调用结果
  37. System.out.println(content);
  38. //关闭连接
  39. client.shutdown();
  40. }
  41. }

执行main函数,结果如下:
image.png
可见已从服务端获取响应。