gRPC

Table of Contents

1. gRPC 简介

gRPC 是由 Google 开源的 RPC 框架,它使用 HTTP/2 协议并使用 ProtoBuf 作为序列化工具。gRPC 支持各种主流语言。图 1 是 gRPC 示意图。

grpc.png

Figure 1: gRPC 示意图

2. Java 实例

下面使用 Java 编程语言来说明 gRPC 的基本使用。

参考:
gRPC Basics - Java
The Java gRPC implementation. HTTP/2 based RPC

2.1. gRPC 的 maven 依赖库

gRPC 的 maven 依赖库如下:

<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-netty</artifactId>
  <version>1.11.0</version>
</dependency>
<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-protobuf</artifactId>
  <version>1.11.0</version>
</dependency>
<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-stub</artifactId>
  <version>1.11.0</version>
</dependency>

2.2. 服务器和客户端 4 种数据传递方式

在 gRPC 中,服务器和客户端之间的数据传递方式有 4 种。

在 ProtoBuf 定义中指定 service 时需要指定采用哪种方式。下面是指定 4 种传递方式的例子:

// file helloworld.proto
syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.mycompany.app.helloworld";
option java_outer_classname = "HelloWorldProto";

package helloworld;

service Greeter {
  // 方式1:简单RPC方式。请求是一个对象,返回也是一个对象。这是最简单的方式,类似于函数调用
  rpc SayHello (HelloRequest) returns (HelloReply) {}

  // 方式2:服务端流式PRC。请求是一个对象,返回流(可从中得到多个对象)
  rpc SayHelloReplyStreaming (HelloRequest) returns (stream HelloReply) {}

  // 方式3:客户端流式RPC。请求是流,返回一个对象
  rpc SayHelloRequestStreaming (stream HelloRequest) returns (HelloReply) {}

  // 方式4:双向流式RPC。请求和返回都是流
  rpc SayHelloBiStreaming (stream HelloRequest) returns (stream HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

2.2.1. 编译 ProtoBuf 定义文件(protobuf-maven-plugin)

在 maven 工程中,ProtoBuf 定义文件的默认存放目录是 src/main/proto/ 。为了方便地编译 ProtoBuf 定义文件为 Java 辅助类文件,推荐使用 maven 插件 protobuf-maven-plugin ,需要在 pom.xml 中进行下面配置:

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.5.0.Final</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.5.1</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.11.0:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

以上面介绍的 helloworld.proto 文件为例,执行 mvn protobuf:compile protobuf:compile-custom 后可得到下面这些文件:

./target/generated-sources/protobuf/grpc-java/com/mycompany/app/helloworld/GreeterGrpc.java
./target/generated-sources/protobuf/java/com/mycompany/app/helloworld/HelloReply.java
./target/generated-sources/protobuf/java/com/mycompany/app/helloworld/HelloReplyOrBuilder.java
./target/generated-sources/protobuf/java/com/mycompany/app/helloworld/HelloRequest.java
./target/generated-sources/protobuf/java/com/mycompany/app/helloworld/HelloRequestOrBuilder.java
./target/generated-sources/protobuf/java/com/mycompany/app/helloworld/HelloWorldProto.java

注 1:它们是从.proto 文件生成的 Server 和 Client 都需要的 Java 辅助文件。
注 2:一般地,我们无需手动执行 mvn protobuf:compile protobuf:compile-custom ,在执行 mvn compile 或者 mvn package 时会自动执行它。

2.3. 简单 RPC 实例

下面以简单 RPC 方式(不介绍另外 3 种方式)来介绍如何实现服务器和客户端代码。这里 有完整可运行代码。

2.3.1. 实现服务端

实现服务端的关键代码一:
要实现服务端,实现 service 中定义相应 rpc 方法即可(即类 GreeterGrpc.GreeterImplBase 中的 sayHello 方法)。下面是一个例子:

class GreeterImpl extends GreeterGrpc.GreeterImplBase {

    @Override
    public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
        // 实现的功能为:
        // 从客户端传过来的消息中取name这个字段,在前面加上"Hello "后,放入到返回给客户端的消息中的message字段中。
        HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
        responseObserver.onNext(reply);
        responseObserver.onCompleted();
    }
}

GreeterGrpc.GreeterImplBase 的定义位于由 proto 生成的文件 GreeterGrpc.java 中:

// 下面代码片断摘自文件 ./target/generated-sources/protobuf/grpc-java/com/mycompany/app/helloworld/GreeterGrpc.java
  public static abstract class GreeterImplBase implements io.grpc.BindableService {

    public void sayHello(com.mycompany.app.helloworld.HelloRequest request,
        io.grpc.stub.StreamObserver<com.mycompany.app.helloworld.HelloReply> responseObserver) {
    }

  }

实现服务端的关键代码二:
除了实现对应的 rpc 方法外,要启动服务器,我们还需要指定端口、增加 service 到服务器等工作,这可通过 io.grpc.ServerBuilder 来实现,下面是一个例子:

io.grpc.Server server = io.grpc.ServerBuilder.forPort(port).addService((BindableService) new GreeterImpl()).build();
server.start();

2.3.2. 实现客户端

要实现客户端,首先需要创建 stub,

实现客户端关键代码一:创建 stub

io.grpc.ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build(); // usePlaintext()表示不使用tls
GreeterGrpc.GreeterBlockingStub blockingStub = GreeterGrpc.newBlockingStub(channel);

实现客户端关键代码二:调用服务

HelloRequest request = HelloRequest.newBuilder().setName("world").build();

HelloReply response = blockingStub.sayHello(request); // 调用rpc服务

System.out.println(response.getMessage());  // 打印服务器返回消息中message字段的内容

Author: cig01

Created: <2018-03-30 Fri>

Last updated: <2018-04-28 Sat>

Creator: Emacs 27.1 (Org mode 9.4)