gRPC
Table of Contents
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 种:
- 简单 RPC 方式。请求是一个对象,返回也是一个对象;
- 服务端流式 PRC。请求是一个对象,返回流(可从中得到多个对象);
- 客户端流式 RPC。请求是流,返回一个对象;
- 双向流式 RPC。请求和返回都是流。
在 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字段的内容
