Skip to content

liuyazong/grpc-in-action

Repository files navigation

GRpc实战及原理分析

  • grpc-client/grpc-client-demo:客户端示例程序
  • grpc-server/grpc-server-demo:服务端示例程序
  • grpc-service:grpc服务定义
  • grpc-client-spring-boot:客户端集成Spring Boot
  • grpc-server-spring-boot:服务端集成Spring Boot

消息及服务定义

  1. message定义

     message InputMessage {
         double numA = 1;
         double numB = 2;
     }
     
     message OutputMessage {
         double result = 1;
     }
    
  2. 数据类型

     proto type      java type       default
     double          double          0
     float           float           0
     int32           int             0
     int64           long            0
     bool            bool            false
     string          String          ""
     bytes           ByteString      
    
  3. service定义

     service ComputeService {
         //Simple RPC。客户端发送一个并等待服务端的响应
         rpc add (InputMessage) returns (OutputMessage) {
     
         }
     
         //Client-side streaming RPC。客户端写消息序列到流并把他们通过流发送到服务端,发送完成后等待服务器端返回结果
         rpc subtract (stream InputMessage) returns (OutputMessage) {
     
         }
     
         //Server-side streaming RPC。客户端发送一个请求,服务端返回一个流给客户端,客户端从流中读取消息序列直到读取完
         rpc multiply (InputMessage) returns (stream OutputMessage) {
     
         }
     
         //Bidirectional streaming RPC。客户端和服务端都可以通过流来发送消息序列,客户端和服务端读写的顺序是任意的
         rpc divide (stream InputMessage) returns (stream OutputMessage) {
     
         }
     }
    
  4. options

     option java_multiple_files = true; //是否为每个message生成单独的java文件
     option java_package = "yz.grpc.proto.service"; //生成文件的包名
     option java_outer_classname = "ComputeServiceProto";
    
  5. 其他

    其他消息类型,如map类型、嵌套、引用其他proto文件定义,见官方教程

API

服务端API

  1. io.grpc.Server

    监听和分发客户端请求。实现类:io.grpc.internal.ServerImpl

  2. io.grpc.netty.NettyServerBuilder

    提供一系列静态方法用于构建io.grpc.Server实例

  3. io.grpc.BindableService

    绑定service实例到server的方法。service实例继承xxxGrpc.xxxImplBase(proto生成的类),而xxxGrpc.xxxImplBase实现了io.grpc.BindableService接口。调用ServerBuilder的public abstract T addService(BindableService bindableService);方法即可将service绑定到server对外提供服务。

  4. io.grpc.ServerInterceptor

    服务端拦截器,在处理请求之前或之后做一些工作,如认证、日志、将请求转发到其他server

客户端API

  1. io.grpc.stub.AbstractStub

    用于调用服务端提供的服务方法,由proto根据service定义生成其实现类

  2. io.grpc.netty.NettyChannelBuilder

    提供一系列静态方法用于构建io.grpc.ManagedChannel实例

  3. io.grpc.ClientInterceptor

    客户端拦截器,在客户端发送请求或接收响应时做一些工作,如认证、日志、request/response重写

原理分析

服务端

  1. server端在使用io.grpc.netty.NettyServerBuilder构建io.grpc.Server实例时,通过调用addService(BindableService bindableService);方法将service实例解析为io.grpc.ServerServiceDefinition对象,最终以ServerServiceDefinition.getServiceDescriptor().getName()为key,以io.grpc.ServerServiceDefinition实例为value存储在io.grpc.internal.InternalHandlerRegistry.Builder属性内的java.util.LinkedHashMap实例中。

  2. 接下来调用io.grpc.netty.NettyServerBuilderbuild方法,该方法调用构造器ServerImpl(AbstractServerImplBuilder<?> builder,InternalServer transportServer,Context rootContext)返回io.grpc.Server实例。该构造函数调用builder参数的build方法将java.util.LinkedHashMap中方key、value封装为io.grpc.internal.InternalHandlerRegistry实例并赋值给io.grpc.internal.ServerImplio.grpc.internal.InternalHandlerRegistry属性。参数transportServerio.grpc.netty.NettyServerBuilderio.grpc.netty.NettyServerBuilder.buildTransportServer方法构建,实际上它是一个io.grpc.netty.NettyServer的实例。

  3. 接下来看io.grpc.internal.ServerImplstart方法,其内部调用了io.grpc.netty.NettyServerstart方法,这里才开始netty server的配置及启动。这里重点关注io.netty.bootstrap.ServerBootstrap.childHandler(io.netty.channel.ChannelHandler)方法,在io.grpc.netty.NettyServerTransportstart方法内对io.grpc.netty.NettyServerHandler进行实例化并把该实例添加到netty的channel链中。

客户端

客户端实例化ManagedChannel并实例化对stub,像调用本地方法一样调用远程方法就行了。

那么,服务端是如何直到客户端调用的哪个方法的呢?接着看。

io.grpc.netty.NettyClientStream.Sink.writeHeadersio.grpc.netty.NettyServerHandler.onHeadersRead

  1. 客户端writeHeaders

    从stub的远程方法调用入口跟进去,会发现,实际调用的是io.grpc.stub.ClientCalls的几个公有静态方法。在调用这些方法时,stub首先会把方法相关的返回值类型、入参类型、方法全名(service全名+/+方法名)封装为io.grpc.MethodDescriptor实例。然后会构造io.grpc.internal.ClientCallImpl实例并调用其start方法,在这个方法内构造了io.grpc.netty.NettyClientStream实例并调用其start方法,这里就到了writeHeaders的地方:以:path为key、以/+方法全名为value写入io.netty.handler.codec.http2.Http2Headers实例中。

  2. 服务端onHeadersRead

    服务端在读取完客户端请求头后会调用io.grpc.netty.NettyServerHandleronHeadersRead方法。该方法从请求头中取出path值,然后调用io.grpc.internal.InternalHandlerRegistry(io.grpc.internal.ServerImpl实例的一个属性,服务端启动时已初始化并把service信息放入其中)的lookupMethod方法更具path值找到对应的处理方法。

整体流程

  1. 服务端将service实例添加到server中,并将其构造为io.grpc.internal.InternalHandlerRegistry实例。
  2. 客户端请求时将所要调用的方法信息以path、value的方式放在请求头中。
  3. 服务端在接到请求头后取出path对应的值并到io.grpc.internal.InternalHandlerRegistry实例中找到对应的io.grpc.ServerCallHandler来完成其请求的处理。

GRpc官方文档

官方教程

Releases

No releases published

Packages

No packages published

Languages