# grpc-examples **Repository Path**: cloudcoder/grpc-examples ## Basic Information - **Project Name**: grpc-examples - **Description**: grpc使用示例 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-02-24 - **Last Updated**: 2023-05-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 1. gRPC 示例 ## 1.1 官方示例 请参考: [官方示例说明](gRPC官方示例说明.md) # 2. rpc框架 ## 2.1 RPC介绍 RPC就是要像调用本地的函数一样去调远程函数,RPC过程如下: ![rpc-flow](images/rpc-flow.jpg) ### 2.1.1 RPC远程调用过程 1. 客户机调用一个称为客户机存根【**client stub**】的本地过程。对于客户机进程来说,这似乎是实际的过程,因为它是一个常规的本地过程。**client stub**将参数打包到远程过程(这可能涉及将它们转换为标准格式),并构建一个或多个网络消息。将参数打包到网络消息中称为封送处理【**marshaling** 】,并要求将所有数据序列化【**serializing**】为一种扁平的字节数组格式。 2. 网络消息由**client stub**发送到远程系统(通过使用*sockets* interfaces对本地内核【local kernel】的系统调用)。 3. 网络消息由内核通过某种协议(无连接或面向连接)传输到远程系统 4. 服务器存根【**server stub**】(有时称为骨架 **skeleton**)接收服务器上的消息。它从网络消息中**unmarshals**出参数,并在必要时将它们从标准网络格式转换为特定于机器的形式。 5. **server stub**调用服务器函数(对客户机来说,服务器函数是远程过程),并将从客户机接收到的参数传递给它 6. 当服务器函数完成时,它将带着返回值返回到**server stub** 7. 如果有必要,**server stub**将返回值转换为一个或多个网络消息发送给**client stub** 8. 消息通过网络发送回**client stub** 9. **client stub**从本地内核读取消息 10. 然后,**client stub**将结果返回给客户机函数,如果需要,将它们从网络消息转换为本地的对象 ### 2.1.2 RPC的好处 ​ RPC的主要好处有两方面。 - 程序员现在可以使用过程调用语义来调用远程函数并获取响应。 - 编写分布式应用程序简化了,因为RPC将所有网络代码隐藏在存根函数中。应用程序不需要担心诸如套接字、端口号以及数据转换和解析等细节。 在[OSI参考模型](https://baike.baidu.com/item/OSI%E6%A8%A1%E5%9E%8B/10119902?fr=aladdin)上,RPC跨越会话层和表示层(第5层和第6层)。 ## 2.2 RPC框架实现功能 - 基于NettyServerBuilder、NettyChannelBuilder,针对RPC的服务端、客户端进行了封装 - 服务端支持Authentication Interceptor - 服务端目前支持心跳服务、简单的hello 服务、client管理服务(提供client实例属性上报、keepalive接口)、消息上报服务 - 客户端使用ServiceManager管理所有BootService, 并在启动时,依次调用所有BootService实例的prepare、startup、onComplete方法,在BootService实例(除GRPCChannelManager)外,将自己作为GRPCChannelListener注册到GRPCChannelManager中 - 客户端使用GRPCChannelManager管理GRPCChannel【针对grpc的ManagedChannel进行封装】,并启动一个线程,根据channel状态检查间隔时间定期检查是否需要重连 - 客户端支持配置多个服务端,如果配置多个,则client随机绑定一个 - 客户端连接成功后,会通知所有注册的服务,标识channel连接成功 - 服务使用channel,调用rpc服务 ### 2.2.1 服务端测试示例 ```java public class ServerMain { public static void main(String[] args) throws Exception { ServiceServer server = new ServiceServer("localhost",7077); server.init(); //通过SPI添加grpc handler HandlerManager.INSTANCE.registerHandlers(server); server.startServer(); server.blockUntilShutdown(); } } ``` ### 2.2.2 客户端测试示例 ```java public class ClientMain { private static final Logger LOGGER = LoggerFactory.getLogger(ClientMain.class); public static void main(String[] args) throws Exception { try { //通过SPI绑定服务 ServiceManager.INSTANCE.boot(); } catch (Exception e) { LOGGER.error("boot failure.", e); } Runtime.getRuntime() .addShutdownHook( new Thread(ServiceManager.INSTANCE::shutdown, "service shutdown thread")); HelloServiceClient helloServiceClient = ServiceManager.INSTANCE.findService(HelloServiceClient.class); for (int i = 0; i < 10; i++) { helloServiceClient.greet("张三" + i); } MsgReportServiceClient msgReportServiceClient = ServiceManager.INSTANCE.findService(MsgReportServiceClient.class); MsgCollection.Builder msgCollection = MsgCollection.newBuilder(); for(int i=0;i<5;i++){ MsgObject.Builder builder = MsgObject.newBuilder(); builder.setId(i+""); builder.setMsg("report msg "+i); builder.setType(i+""); msgCollection.addMsgs(builder); } msgReportServiceClient.reportInSync(msgCollection.build()); List msgs = Lists.newArrayList(); for(int i=10;i<15;i++){ MsgObject.Builder builder = MsgObject.newBuilder(); builder.setId(i+""); builder.setMsg("report msg "+i); builder.setType(i+""); msgs.add(builder.build()); } msgReportServiceClient.reportBySteam(msgs); TimeUnit.SECONDS.sleep(1000); } } ``` #### 客户端启动时序图 客户端启动时序图如下: ![](images/client-sequence.png) #### 服务端启动时序图 ![](images/server-sequence.png) # 3. 定义rpc协议 ## 3.1 定义rcp协议 ​ 在project的src/main/proto 和 src/test/proto,根据需要创建proto文件,如健康检查proto ```protobuf syntax = "proto3"; package grpc.health.v1; message HealthCheckRequest { string service = 1; } message HealthCheckResponse { enum ServingStatus { UNKNOWN = 0; SERVING = 1; NOT_SERVING = 2; } ServingStatus status = 1; } service Health { rpc Check (HealthCheckRequest) returns (HealthCheckResponse); } ``` ## 3.2 根据协议生成代码 ​ 使用的protoc.version版本是: 3.12.0 ​ 在pom.xml文件中增加如下内容,则在compile 或 package时,protobuf-maven-plugin会根据定义的proto文件,生成代码 ​ 默认情况下,生成的代码在: - target/protobuf/grpc-java 存放生成的rpc服务,类以Grpc结尾 - target/protobuf/grpc 存放生成的消息对象, 类名是消息的名称、消息的名称+OrBuilder ```xml kr.motd.maven os-maven-plugin 1.6.2 org.xolstice.maven.plugins protobuf-maven-plugin 0.6.1 com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier} grpc-java io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} compile compile-custom ```