gRPC 简介
gRPC是由Google开发并高性能的开源,基于http2和protobuf 序列化的 RPC 框架, 它具有以下特点
- 语言中立
支持C,Java,Go等多种语言来构建RPC服务,这是gRPC被广泛的应用在微服务项目中的重要原因,因为不同的微服务可能用不同的语言构建。 - 基于HTTP/2协议
支持双向流,消息头压缩,单TCP的多路复用,服务端推送等,这些特性使得gRPC更加适用于移动场景下的客户端和服务端之间的通信。 - 基于 IDL 定义服务
编写.proto文件即可生成特定语言的数据结构、服务端接口和客户端Stub。 - 支持Protocol Buffer序列化
Protocol Buffer是由Google开发的一种数据序列化协议(类似于XML、JSON、Hession),平台无关,压缩和传输效率高,语法简单,表达能力强。
gRPC服务的大体架构
协议定义
gRPC 中使用 protocol buffers 做接口定义语言(IDL),同时也作为默认的消息交换格式,在设计上,gRPC 是可以支持 JSON 等其他消息格式的。
一个典型的协议proto
定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| syntax = "proto3"; option java_package = "com.example.grpc"; option java_multiple_files = true; option java_outer_classname = "HelloWorldProto"; message Greeting { string name = 1; }
message HelloResp { string reply = 1; }
service HelloWorld { rpc sayHello (Greeting) returns (HelloResp); }
|
java_package
表示生成java
代码的包名java_multiple_files = true
表示生成多个java文件,若不设置该属性,则只会生成一个java文件java_outer_classname
表示包含message描述的java文件的类名message
用于定义消息格式,是 protocol buffers
消息定义service
用于定义gRPC
服务接口,需要使用插件的方式生成对应的服务代码
引入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>1.30.1</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>1.30.1</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.30.1</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-core</artifactId> <version>1.30.1</version> </dependency>
|
添加 maven 插件
插件的主要作用是把 .proto
文件转换为 Java 代码。
编译默认的输出位置在打包目录的generated-sources/protobuf/
文件夹下,可参考插件官方文档
gRPC 利用proto
文件定义了消息交互格式以及服务,插件配置中goals
中compile
表示编译proto
文件为Message
对象,即消息交互格式,而compile-custom
表示编译.proto
文件为gRPC
服务对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.6.2</version> </extension> </extensions> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.30.1:exe:${os.detected.classifier}</pluginArtifact> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
|
在maven
的配置文件下添加如下配置, 将Message
源文件和gRPC
源文件输出到不同的目录:
1 2 3 4 5 6 7 8
| <properties> <javaOutputDirectory>${project.basedir}/src/main/java-proto</javaOutputDirectory> <protocPluginOutputDirectory> ${project.basedir}/src/main/java-grpc </protocPluginOutputDirectory> </properties>
|
需要注意的一点是,gRPC 服务代码依赖Message
代码。
运行下面的命令,即可生成代码。
可以看到生成的代码如下:
服务端代码实现
1. 实现服务端接口
HelloWorldImplBase
与 Thrift
中 Iface
类似, gRPC 通过扩展这个类,来实现响应的服务逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class HelloWorldRpcService extends HelloWorldGrpc.HelloWorldImplBase { @Override public void sayHello(Greeting request, StreamObserver<HelloResp> responseObserver) { String name = request.getName();
HelloResp resp = HelloResp.newBuilder() .setReply("Hello " + name) .build();
responseObserver.onNext(resp);
responseObserver.onCompleted(); } }
|
2. 创建服务端
我们使用 gRPC
默认的服务来启动即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class GrpcServer { private Server server;
public GrpcServer(int port) { server = ServerBuilder.forPort(port) .addService(new HelloWorldRpcService()) .build(); } public void start() throws IOException { server.start(); } public void awaitTermination() throws InterruptedException { server.awaitTermination(); } public void shutdown() { server.shutdown(); } }
|
3. 服务主程序
设置好端口,启动后,等待服务结束信号。
1 2 3 4 5 6 7
| public class MainServer { public static void main(String[] args) throws IOException, InterruptedException { GrpcServer server = new GrpcServer(8888); server.start(); server.awaitTermination(); } }
|
server.awaitTermination()
调用后才能保证主进程不会退出
客户端实现
1. 创建客户端 Stub
对象
gRPC
通过Stub
对象与服务端通信,本例需要创建 HelloWorldBlockingStub
, 来实现客户端与服务端通信。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class HelloWorldClient { private final HelloWorldGrpc.HelloWorldBlockingStub blockingStub; public HelloWorldClient(String host, int port) { ManagedChannel managedChannel = ManagedChannelBuilder.forAddress(host, port) .usePlaintext() .build(); blockingStub = HelloWorldGrpc.newBlockingStub(managedChannel); }
public String sayHello(String name) { Greeting greeting = Greeting.newBuilder() .setName(name) .build(); HelloResp resp = blockingStub.sayHello(greeting);
return resp.getReply(); } }
|
2. 客户端主程序
1 2 3 4 5 6 7
| public class MainClient { public static void main(String[] args) { HelloWorldClient client = new HelloWorldClient("localhost", 8888); String reply = client.sayHello("HanMeiMei"); System.out.println(reply); } }
|
使用命令行的方式生成代码
使用命令行的方式可以批量处理或自定义协议管理器,从而提升对协议维护管理效率。
1. 下载编译好的 protoc 二进制文件
在 gRPC Github 发布页,下载系统对应的二进制版本。
1
| wget https://github.com/protocolbuffers/protobuf/releases/download/v3.13.0/protoc-3.13.0-osx-x86_64.zip
|
解压到 ~/protoc-3.13.0-osx-x86_64
2. 下载编译好的 protoc-java-gen 插件
在 maven repo 发布目录下 找到对应平台的文件下载
1
| wget https://repo1.maven.org/maven2/io/grpc/protoc-gen-grpc-java/1.32.2/protoc-gen-grpc-java-1.32.2-osx-x86_64.exe
|
把此文件放到 ~/protoc-3.13.0-osx-x86_64/plugin 目录下
3. 命令行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| PROTOC_ROOT=/usr/local/protoc-3.13.0-osx-x86_64
PROTOC_PLUGIN_JAVA="protoc-gen-grpc-java=$PROTOC_ROOT/plugin/protoc-gen-grpc-java-1.32.2-osx-x86_64.exe"
SRC_PROTO_DIR="proto/"
DST_JAVA_DIR="gen-java-proto"
DST_GRPC_DIR="gen-java-grpc"
MAIN_PROTO_FILE="proto/HelloService.proto"
protoc --plugin=$PROTOC_PLUGIN_JAVA -I=$SRC_PROTO_DIR --java_out="gen-java-proto" \ --grpc-java_out="gen-java-proto" "proto/HelloService.proto"
|