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 种。
在 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字段的内容