
My Desktop
概述
gRPC是Google开源的通用高性能RPC框架,它支持的是使用Protocol Buffers来编写Service定义,支持较多语言扩平台并且拥有强大的二进制序列化工具集。与文章《RPC框架实践之:Apache Thrift》 一文中实践的另一种通用RPC框架 Thrift 能通过Generator自动生成对应语言的Service接口类似,gRPC也能 自动地生成 Server和Client的 Service存根(Stub),我们只需要 一个命令 就能快速搭建起RPC运行环境。
下面实践一下gRPC框架,做的事情就是:Client端通过远程RPC调用Server的获取时间的接口,从而将服务器时间获取到本地并显示。
类似于之前对于 RPC框架: Thrift 的实践步骤,下面一一阐述。
注: 本文首发于 My 公众号 CodeSheep ,可 长按 或 扫描 下面的 小心心 来订阅 ↓ ↓ ↓

开发gRPC-API
-
首先创建一个基于Maven的项目: GrpcAPI
-
pom中加入grpc相关的依赖
1 <dependency> 2 <groupId>io.grpc</groupId> 3 <artifactId>grpc-all</artifactId> 4 <version>1.12.0</version> 5 </dependency>
这个grpc-all包含了很多grpc相关的组件:grpc-netty 、 grpc-protobuf 、grpc-stub 等等

- pom中加入grpc相关的 build插件
这里添加两个Maven插件,目的是后面需要用这些插件来执行Protocol Buffers命令,从而自动生成相关的Stub代码:
os-maven-plugin:生成平台无关的属性 protobuf-maven-plugin:执行Protocol Buffers命令并生成Stub代码库
1 <build> 2 <extensions> 3 <extension> 4 <groupId>kr.motd.maven</groupId> 5 <artifactId>os-maven-plugin</artifactId> 6 <version>1.4.1.Final</version> 7 </extension> 8 </extensions> 9 <plugins> 10 <plugin> 11 <groupId>org.xolstice.maven.plugins</groupId> 12 <artifactId>protobuf-maven-plugin</artifactId> 13 <version>0.5.0</version> 14 <configuration> 15 <pluginId>grpc-java</pluginId> 16 <protocArtifact>com.google.protobuf:protoc:3.0.2:exe:${os.detected.classifier}</protocArtifact> 17 <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.2.0:exe:${os.detected.classifier}</pluginArtifact> 18 </configuration> 19 <executions> 20 <execution> 21 <goals> 22 <goal>compile</goal> 23 <goal>compile-custom</goal> 24 </goals> 25 </execution> 26 </executions> 27 </plugin> 28 </plugins> 29 </build>
- 编写.proto的服务定义文件
这里.proto文件的作用和写法就和我的前一篇文章《RPC框架实践之:Apache Thrift》 一文中Thrift所要求的.thrift文件编写一样,是有其自己的语法要求的!
1syntax = "proto3”; // 语法版本 2 3// stub选项 4option java_package = "com.hansonwang99.grpc.api”; 5option java_outer_classname = “RPCDateServiceApi”; 6option java_multiple_files = true; 7 8// 定义包名,类似于我的文章《RPC框架实践之:Apache Thrift》中的Thrift的namespace 9package com.hansonwang99.grpc.api; 10 11// 服务接口定义,服务端和客户端都要遵守该接口进行通信 12service RPCDateService { 13 rpc getDate (RPCDateRequest) returns (RPCDateResponse) {} 14} 15 16// 定义消息(请求) 17message RPCDateRequest { 18 string userName = 1; 19} 20 21// 定义消息(响应) 22message RPCDateResponse { 23 string serverDate = 1; 24}
- 执行
mvn compile命令来自动生成代码Stub
mvn编译完成以后,在target/generated-sources目录下就能看到根据上面.proto文件自动转化生成的Java代码Stub

代码生成结果如下所示

好了,既然gRPC-API已经有了,下面可以分别编写服务端和客户端
开发gRPC服务端
-
创建基于Maven的项目:Server
-
pom中添加 GrpcAPI 依赖
1 <dependency> 2 <groupId>com.hansonwang99</groupId> 3 <artifactId>GrpcAPI</artifactId> 4 <version>1.0-SNAPSHOT</version> 5 <scope>compile</scope> 6 </dependency>
接下来一步比较关键
-
实现gRPC服务接口
public class RPCDateServiceImpl extends RPCDateServiceGrpc.RPCDateServiceImplBase{ @Override public void getDate(RPCDateRequest request, StreamObserver<RPCDateResponse> responseObserver) { RPCDateResponse rpcDateResponse = null; Date now=new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("今天是"+"yyyy年MM月dd日 E kk点mm分”); String nowTime = simpleDateFormat.format( now ); try { rpcDateResponse = RPCDateResponse .newBuilder() .setServerDate( "Welcome " + request.getUserName() + ", " + nowTime ) .build(); } catch (Exception e) { responseObserver.onError(e); } finally { responseObserver.onNext( rpcDateResponse ); } responseObserver.onCompleted(); } }
我想此处重写的getDate()方法并不陌生吧,这正是上文 .proto 文件中定义的Service接口。 此处逻辑比较简单:获取当前时间,并且将其与请求RPCDateRequest中提取出的userName字段进行拼接,然后返回给调用端!形成一个闭环
-
创建gRPC服务端启动类
public class GRPCServer { private static final int port = 9999; public static void main( String[] args ) throws Exception { Server server = ServerBuilder. forPort(port) .addService( new RPCDateServiceImpl() ) .build().start(); System.out.println( "grpc服务端启动成功, 端口=" + port ); server.awaitTermination(); } }
端口自定义的9999,也就是在该端口监听。现在可以立即运行GRPCServer,来启动服务端

开发gRPC客户端
-
创建基于Maven的项目:Client
-
pom中依然需要添加 GrpcAPI 依赖
1 <dependency> 2 <groupId>com.hansonwang99</groupId> 3 <artifactId>GrpcAPI</artifactId> 4 <version>1.0-SNAPSHOT</version> 5 <scope>compile</scope> 6 </dependency> -
创建gRPC客户端启动类
public class GRPCClient { private static final String host = “localhost”; private static final int serverPort = 9999;
1public static void main( String[] args ) throws Exception { 2 ManagedChannel managedChannel = ManagedChannelBuilder.forAddress( host, serverPort ).usePlaintext().build(); 3 try { 4 RPCDateServiceGrpc.RPCDateServiceBlockingStub rpcDateService = RPCDateServiceGrpc.newBlockingStub( managedChannel ); 5 RPCDateRequest rpcDateRequest = RPCDateRequest 6 .newBuilder() 7 .setUserName(“hansonwang99”) 8 .build(); 9 RPCDateResponse rpcDateResponse = rpcDateService.getDate( rpcDateRequest ); 10 System.out.println( rpcDateResponse.getServerDate() ); 11 } finally { 12 managedChannel.shutdown(); 13 } 14}}
现在立即启动 GRPCClient!
C-S通信实验
还记得我们的目标吗?
RPC完成的即是远程的过程调用,在本实验中那就是客户端可以远程调用服务端的getDate()过程,并将结果取到客户端来显示!

后记
本文实验代码在码云 → 需要自取
作者更多的原创文章:在开源中国
作者其他一些RPC框架的实践如下:
作者一些关于容器化、微服务化方面的文章如下: