我很好奇gRPC-Java的工作原理,所以我尝试阅读代码


介绍

我尝试了有关gRPC的Java教程,并对它的工作方式感到好奇,所以我研究了各种东西。

  • 本教程中介绍了如何使用gRPC-Java,因此我将不对其进行详细说明。
  • 这是我对如何进行通信以及详细的内部实现方式了解的一种安排。

确认版本

发行版:gRPC-Java v1.16.1

  • 本教程中使用了routeguide软件包的源代码。
  • 单击此处进行gradle设置
1
2
3
compile 'io.grpc:grpc-netty-shaded:1.16.1'
compile 'io.grpc:grpc-protobuf:1.16.1'
compile 'io.grpc:grpc-stub:1.16.1'

关于gRPC

首先,从官方文档中提取有关gRPC的要点。

  • 使用协议缓冲区序列化要发送和接收的数据,并定义RPC接口

    • 协议缓冲区定义文件仅定义数据的序列化,但gRPC对其进行了扩展以定义RPC接口。
    • 用于序列化的数据和接口的大多数代码是自动生成的,因此您可以专注于业务逻辑。
  • 通讯使用HTTP / 2

    • HTTP / 2格式,用于建立通信以及发送/接收头和数据
    • 一个TCP连接可以进行双向流通信,并且可以定义以下四种rpc方法。

<表格>

类型

概述


<身体>

一个简单的RPC

返回1个请求的1个响应

服务器端流式RPC

返回对一个请求的多个响应

客户端流式RPC

对于多个请求返回一个响应

双向流式RPC

双向交换多个请求和多个响应


关于gRPC-Java

gRPC-Java实现点。

  • 在服务器端,通信控制部分使用Netty
  • 客户端将OkHttp用于Android和Netty,否则

    • 这次,对客户端的处理没有进行太多研究。
  • 是否为

    Android是通过在ServiceProviders#loadAll中使用ClassLoader来确定的。

  • 如果不是Android,请使用java.util.ServiceLoader#在ServiceProviders中加载#getCandidatesViaServiceLoader以获得用META-INF / services / io.grpc.ServerProvider编写的类

    • 我首先了解了java.util.ServiceLoader#加载。 ..

关于Netty

Java中使用的gRPC-Netty是一个框架,允许您在Java中创建非阻塞I / O(NIO)应用程序。 (请勿使用Java Servlet)

gRPC中显示的线程,NIO的事件名称以及Netty的处理均如下所示。

  • 在凸台线程中检测OP_ACCEPT并注册OP_READ事件
  • 工作线程执行OP_READOP_WRITE的处理

  • 当需要gRPC方法调用时,辅助线程读取客户端的请求并创建执行程序线程以执行该过程。

<表格>

字符

角色


<身体>

老板线程(ServerBootstrap parentGroup)

一个线程,用于检测Netty使用的网络上的I / O。默认情况下在启动时生成一个。

工作线程(ServerBootstrap的子组)

处理Netty使用的每个I / O事件的线程。默认值为处理器数量x 2。

执行程序线程

执行gRPC中定义的方法的线程。为每个方法调用生成。


<表格>

I / O事件名称

概述


<身体>

OP_ACCEPT

来自客户端的连接

OP_READ

读取网络I / O

OP_WRITE

导出网络I / O


补品376??9113

关于Netty," JJUG CCC 2018 Spring --I-7(I)我的第一个Netty"非常有帮助。

关于HTTP / 2

摘录自Google的HTTP / 2简介,以了解gRPC。

在HTTP / 1.x中,以换行符分隔的纯文本被视为一个请求(或响应),但是在HTTP / 2中,它被表示为一条消息,并且该消息以以下帧为单位。

  • HEADERS框架(HTTP / 1.x标题)
  • 数据帧(HTTP / 1.x主体)

可以使用一个TCP连接并行和双向交换帧。
换句话说,不必为每个请求建立TCP连接,并且可以针对一个请求进行多个响应和服务器推送。

gRPC-Java处理概述

从这一点开始,它将类似于"代码的哪一部分以及什么样的处理?",因此在此之前,我将总结一下我对一系列处理的解释。

  • gRPC服务器在启动时产生Netty使用的老板线程
  • gRPC客户端向gRPC服务器发出请求
  • 当老板线程检测到网络I / O事件OP_ACCEPT时,它将创建一个Netty工作线程(或从池中获取它)并委派OP_READ / OP_WRITE的处理。
  • 每当发生I / O事件(例如TCP连接建立(3向握手等),HTTP / 2 HEADERS框架,DATA框架等)时,都会调用工作线程。
  • 在从客户端接收到gRPC调用所需的HTTP / 2消息(多个帧)后,工作线程生成了一个执行程序线程来执行gRPC方法。

  • Netty是一种仅在老板线程和工作线程上运行的事件循环机制,但是从那里为每个gRPC方法调用创建一个线程。
  • 因此,即使在gRPC方法中执行了阻塞处理,Netty线程也似乎未被阻塞。
  • 执行程序线程执行gRPC方法并注册响应OP_WRITE
  • 工作线程检测到OP_WRITE并将响应返回给客户端
  • 如果客户端仍处于连接状态,则无需重新建立TCP连接,因此5. ~ 7.处理可以并行执行多次。
  • 如果客户端断开连接,请断开TCP连接
  • 在服务器端读取代码

    待办事项我正在为备忘录编写它,因此以后将添加它。 (很难跟踪创建各种线程以及使用回调创建新线程的过程,因此,如果您能告诉我是否犯了错误,我将非常高兴。)

    开始

    • 运行RouteGuideServer#main
    • 使用ServerBuilder#forPort获取ServerBuilder的实例

      • 查看ServiceProviders中的ClassLoader#loadAll以确定它是否是Android应用程序
      • 如果您不是Android用户,则使用java.util包的ServiceLoader#加载机制从META-INF / services / io.grpc.ServerProvider获取NettyServerProvider.java的实例。
    • 在NettyServerBuilder#addService中,传递继承自动生成的xxxGrpc.xxxImplBase的类。

      • 此处绑定了协议缓冲区中定义的方法
      • 因为调用了由AbstractServerImplBuilder.java#L146自动生成的代码的bindService。

    • NettyServerBuilder#构建

      • 创建并返回一个ServerImpl实例
      • 这时,AbstractServerImplBuilder#buildTransportServer创建NettyServer的实例,将其作为参数传递,并转到ServerImpl具有的称为InternalServer transportServer的字段。
    • 执行NettyServer#在ServerImpl中启动#从Builder获得的start

      • 使用NettyServer创建Netty所需的父级(老板)和子级(工人)的NioEventLoopGroup实例#allocateSharedGroups
      • 新服务器引导程序启动Netty服务器
      • 实现ChannelInitializer并将其传递给ServerBootstrap#childHandler
      • io.netty.bootstrap.AbstractBootstrap#bind
      • 使用initAndRegister启动老板线程

        • io.netty.channel.nio.NioEventLoop.run开始侦听OP_ACCEPT事件
    • 将带有ThreadFactory(ThreadFactoryBuilder $ ThreadFactory)的ThreadPoolExecutor传递给执行者,该线程创建名为grpc-default-executor-%d的线程

      • 每次调用gRPC方法时,都会从此处生成并执行线程
    • 启动NettyServer时,主线程使用Runtime.getRuntime()。AddShutdownHook()在最后定义处理。
    • 另外,主线程调用RouteGuideServer.blockUntilShutdown进入WAIT状态。

    收到请求

    • 当工作线程接受gRPC方法调用请求时,NettyServerHandler将执行ServerImpl $ ServerTransportListenerImpl#streamCreated
    • ServerImpl $ ServerTransportListenerImpl#streamCreated将继承ContextRunnable的StreamCreated实例传递给包装ThreadPoolExecutor的SerializingExecutor。

    • 该执行程序在ThreadFactoryBuilder $ ThreadFactory中以线程名称grpc-default-executor-%d执行以下Runnable任务。

      • StreamCreated#运行