gRPC是Google开源的一个RPC框架,基于HTTP2.0传输数据,数据序列化框架为Google开源的Protocol Buffers
一、先简单介绍下Protocol Buffers
同样作为数据序列化框架,Protocol Buffers比XML更小、更快、更易于编程,它将消息和服务定义在.proto文件中,语言无关,在Java中用它自己的编译器将.proto文件直接编译成Java类
1.定义消息
syntax = "proto3";
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3;
}
指定元素类型:可以指定元素类型为基本类型,也可以指定为enum或者其他message
指定元素代号:在二进制格式的消息中标识元素,是从1开始的数字
指定元素约束:默认情况下元素可以出现0次或1次;repeated:元素可以出现任意次
添加注释:与Java注释相同,//或者/* ……*/
添加reserved元素:弃用某些元素名或元素代号
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
元素基本类型和Java类型的对应关系:double->double, float->float, int32->int, int64->long, bool->boolean, string->String, bytes->BytesString
枚举类型:
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3 [default = 10];
enum Corpus {
//option allow_alias = true;
//OTHER = 6; allow_alias为true可以指定不同枚举元素为相同的值
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4;
}
注意枚举类型的元素代码从0开始,枚举元素也可以弃用某些元素名或元素代码
enum Foo {
reserved 2, 15, 9 to 11, 40 to max;
reserved "FOO", "BAR";
}
默认值:string->empty string, bytes->empty bytes, numeric->0, bool->false, enum->第一个枚举元素
导入其他消息定义:使用public关键字可以传递导入
// old.proto
import public "new.proto";
import "other.proto";
// client.proto
import "old.proto";
// You use definitions from old.proto and new.proto, but not other.proto
消息可以嵌套定义
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}
2.定义服务
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse);
}
二、从实例出发了解gRPC
gRPC的使用流程是这样的:
1.在.proto文件中定义消息体和服务,规定客户端要发送的消息和服务器的通信方式
2.编译.proto文件,生成Java类
3.编写Server和Client端程序,定义通信方法
4.运行服务器程序,监听请求
5.运行客户端程序,发送请求消息,接收返回消息
Protocol Buffers和gRPC都需要自己的编译器,先添加maven依赖
<properties>
<grpc.version>1.13.1</grpc.version>
<protobuf.version>3.5.1</protobuf.version>
<protoc.version>3.5.1-1</protoc.version>
</properties>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>${protobuf.version}</version>
</dependency>
</dependencies>
<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:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
参考gRPC官网,了解到gRPC有四种通信方式
1.客户端发单个消息,服务器发单个消息
2.客户端发单个消息,服务器发消息流
3.客户端发消息流,服务器发单个消息
4.客户端发消息流,服务器发消息流
于是我们在.proto文件中定义四个方法,模拟这四种通信方式
syntax = "proto3";
option java_package = "learn_grpc";
service Service {
rpc SayHello (SayHelloRequest) returns (SayHelloReply) {
}
rpc GetServerPeopleList (GetServerPeopleListRequest) returns (stream
GetServerPeopleListReply) {
}
rpc CountClientPeople (stream CountClientPeopleRequest) returns (CountClientPeopleReply) {
}
rpc Chat (stream ChatContent) returns (stream ChatContent) {
}
}
message SayHelloRequest {
string name = 1;
string helloWords = 2;
}
message SayHelloReply {
string reply = 1;
}
message GetServerPeopleListRequest {
}
message GetServerPeopleListReply {
string name = 1;
}
message CountClientPeopleRequest {
}
message CountClientPeopleReply {
int32 count = 1;
}
message ChatContent {
string content = 1;
}
编译.proto文件,生成了对应的ServiceGrpc.class文件
新建Server和Client文件
public class ServiceServer {
private final Server server;
private final int port;
//初始化server,指定端口号和服务
public ServiceServer(int port) {
this.port = port;
server = ServerBuilder.forPort(port).addService(new Service()).build();
}
public void start() throws IOException, InterruptedException {
server.start();
System.out.println("server started");
blockUntilShutdown();
}
public void stop() {
server.shutdown();
System.out.println("server stopped");
}
//服务器启动后进入等待状态
public void blockUntilShutdown() throws InterruptedException {
System.out.println("server blocked");
server.awaitTermination();
}
//定义服务,实现方法
static class Service extends ServiceGrpc.ServiceImplBase {
//单个消息-单个消息
@Override
public void sayHello(LearnGrpc.SayHelloRequest request,
StreamObserver<LearnGrpc.SayHelloReply> responseObserver) {
String name = request.getName();
//onNext方法里填写要返回的消息
responseObserver.onNext(LearnGrpc.SayHelloReply.newBuilder()
.setReply("Welcome, " + name + "!").build());
responseObserver.onCompleted();
}
//单个消息-消息流
@Override
public void getServerPeopleList(LearnGrpc.GetServerPeopleListRequest request,
StreamObserver<LearnGrpc.GetServerPeopleListReply> responseObserver) {
//多次执行onNext方法,返回消息流
responseObserver
.onNext(LearnGrpc.GetServerPeopleListReply.newBuilder().setName("彭于晏").build());
responseObserver
.onNext(LearnGrpc.GetServerPeopleListReply.newBuilder().setName("张震").build());
responseObserver
.onNext(LearnGrpc.GetServerPeopleListReply.newBuilder().setName("靳东").build());
responseObserver.onCompleted();
}
//消息流-单个消息
@Override
public StreamObserver<LearnGrpc.CountClientPeopleRequest> countClientPeople(
final StreamObserver<LearnGrpc.CountClientPeopleReply> responseObserver) {
//初始化请求流
return new StreamObserver<LearnGrpc.CountClientPeopleRequest>() {
int count = 0;
//编写对请求流中每个请求的操作
public void onNext(LearnGrpc.CountClientPeopleRequest countClientPeopleRequest) {
count++;
}
public void onError(Throwable throwable) {
}
//请求流发送完成
public void onCompleted() {
//onNext方法里填写要返回的消息
responseObserver.onNext(
LearnGrpc.CountClientPeopleReply.newBuilder().setCount(count).build());
responseObserver.onCompleted();
}
};
}
//消息流-消息流
@Override
public StreamObserver<LearnGrpc.ChatContent>
chat(final StreamObserver<LearnGrpc.ChatContent> responseObserver) {
return new StreamObserver<LearnGrpc.ChatContent>() {
//每接受一个请求就返回一个响应
public void onNext(LearnGrpc.ChatContent chatContent) {
System.out.println(chatContent.getContent());
responseObserver.onNext(LearnGrpc.ChatContent.newBuilder()
.setContent("Hi, I'm server!").build());
}
public void onError(Throwable throwable) {
}
public void onCompleted() {
responseObserver.onCompleted();
}
};
}
}
public static void main(String[] args) throws IOException, InterruptedException {
ServiceServer serviceServer = new ServiceServer(50001);
serviceServer.start();
}
}
public class ServiceClient {
private final ManagedChannel channel;
private final ServiceGrpc.ServiceBlockingStub blockingStub;
private final ServiceGrpc.ServiceStub asynStub;
//用host和端口号构造通道,初始化阻塞stub和异步stub,分别用于发送单个消息和消息流
public ServiceClient(String host, int port) {
channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
blockingStub = ServiceGrpc.newBlockingStub(channel);
asynStub = ServiceGrpc.newStub(channel);
}
public void sayHello() {
System.out.println("Method sayHello starts");
LearnGrpc.SayHelloRequest request = LearnGrpc.SayHelloRequest.newBuilder().setName("client")
.setHelloWords("hello").build();
//直接接收单个消息返回值
LearnGrpc.SayHelloReply reply = blockingStub.sayHello(request);
System.out.println("Reply: " + reply.getReply());
System.out.println("Method sayHello ends");
}
public void getServerPeopleList() {
System.out.println("Method getServerPeopleList starts");
System.out.println("People list:");
LearnGrpc.GetServerPeopleListRequest request = LearnGrpc.GetServerPeopleListRequest
.newBuilder().build();
//用迭代器接收消息流
Iterator<LearnGrpc.GetServerPeopleListReply> list = blockingStub
.getServerPeopleList(request);
while (list.hasNext()) {
LearnGrpc.GetServerPeopleListReply reply = list.next();
System.out.println(reply.getName());
}
System.out.println("Method getServerPeopleList ends");
}
public void countClientPeople() throws InterruptedException {
System.out.println("Method countClientPeople starts");
final CountDownLatch finishLatch = new CountDownLatch(1);
LearnGrpc.CountClientPeopleRequest request = LearnGrpc.CountClientPeopleRequest.newBuilder()
.build();
StreamObserver<LearnGrpc.CountClientPeopleReply> responseObserver = new StreamObserver<LearnGrpc.CountClientPeopleReply>() {
//onNext接收返回的单个消息
public void onNext(LearnGrpc.CountClientPeopleReply countClientPeopleReply) {
System.out.println("Client people count:");
System.out.println(countClientPeopleReply.getCount());
}
public void onError(Throwable throwable) {
}
//接收完毕后打开障栅,结束方法
public void onCompleted() {
System.out.println("Method countClientPeople ends");
finishLatch.countDown();
}
};
StreamObserver<LearnGrpc.CountClientPeopleRequest> requestObserver = asynStub
.countClientPeople(responseObserver);
//多次发送消息,构成消息流
requestObserver.onNext(request);
requestObserver.onNext(request);
requestObserver.onNext(request);
requestObserver.onCompleted();
//发送结束后利用障栅进行阻塞,等待服务器返回
finishLatch.await();
}
public void chat() throws InterruptedException {
System.out.println("Method chat starts");
final CountDownLatch finishLatch = new CountDownLatch(1);
LearnGrpc.ChatContent request1 = LearnGrpc.ChatContent.newBuilder()
.setContent("Hello, I'm client!").build();
LearnGrpc.ChatContent request2 = LearnGrpc.ChatContent.newBuilder()
.setContent("Hello, I'm client!").build();
StreamObserver<LearnGrpc.ChatContent> requestObserver = asynStub.chat(new StreamObserver<LearnGrpc.ChatContent>() {
//接收到一个返回消息就调用一次onNext方法,用来接收消息流
public void onNext(LearnGrpc.ChatContent chatContent) {
System.out.println(chatContent.getContent());
}
public void onError(Throwable throwable) {
}
public void onCompleted() {
System.out.println("Method chat ends");
finishLatch.countDown();
}
});
//多次调用onNext方法,发送消息流
requestObserver.onNext(request1);
requestObserver.onNext(request2);
requestObserver.onCompleted();
//发送结束后利用障栅进行阻塞,等待服务器返回
finishLatch.await();
}
public static void main(String[] args) throws InterruptedException {
ServiceClient serviceClient = new ServiceClient("localhost", 50001);
serviceClient.sayHello();
serviceClient.getServerPeopleList();
serviceClient.countClientPeople();
serviceClient.chat();
}
}
先运行服务器,得到以下输出:
server started
server blocked
再运行客户端的四个方法,得到以下输出:
Method sayHello starts
Reply: Welcome, client!
Method sayHello ends
Method getServerPeopleList starts
People list:
彭于晏
张震
靳东
Method getServerPeopleList ends
Method countClientPeople starts
Client people count:
3
Method countClientPeople ends
Method chat starts
Hi, I'm server!
Hi, I'm server!
Method chat ends
通过以上实例,可以了解gRPC的基本使用方法
后续将会对gRPC再进行源码层面的剖析