I was curious about how gRPC-Java works, so I tried code reading

Introduction

I tried gRPC Java Tutorial and was curious about how it works, so I researched various things.

--GRPC-How to use Java is described in the tutorial, so I will not explain it in detail. ――It is a summary of what I understood about how to communicate and what the detailed internal implementation is.

Confirmed version

releases: gRPC-Java v1.16.1

--The tutorial used the routeguide package source --Click here for gradle settings

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'

About gRPC

First of all, about gRPC, the points are excerpted from Official Document.

-Use Protocol Buffers to serialize the data to be sent and received and define the RPC interface. --The Protocol Buffers definition file only defines data serialization, but gRPC extends it to define the RPC interface as well. --Most of the code for the data and interfaces to be serialized is automatically generated, so you can concentrate on your business logic. --Communication uses HTTP / 2 --HTTP / 2 format for establishing communication and sending / receiving headers and data --Bidirectional streaming communication is possible with one TCP connection, and the following 4 types of rpc methods can be defined.

type Overview
A simple RPC Returns 1 response for 1 request
A server-side streaming RPC Returns multiple responses for one request
A client-side streaming RPC Returns one response for multiple requests
A bidirectional streaming RPC Exchange multiple requests and multiple responses in both directions

About gRPC-Java

gRPC-Java implementation points.

--On the server side, the communication control part uses Netty. --The client side uses OkHttp for Android, and Netty otherwise. --This time, the processing on the client side has not been investigated much.

--Whether it is Android or not is in ServiceProviders # loadAll Determine using ClassLoader are doing. --For non-Android, ServiceProviders # getCandidatesViaServiceLoader Use java.util.ServiceLoader # load in META-INF / services / io.grpc.ServerProvider I'm getting the written class --I first learned about java.util.ServiceLoader # load. ..

About Netty

Netty used in gRPC-Java is a framework that allows you to create non-blocking I / O (NIO) applications in Java. (Do not use Java Servlet)

The thread that appears in gRPC, the event name of NIO, and the processing of Netty are like this.

--Detect ʻOP_ACCEPT in boss thread and register ʻOP_READ event --The worker thread executes the processing of ʻOP_READ and ʻOP_WRITE --The worker thread reads the client request and creates an executor thread to execute the process when the gRPC method call is needed.

Character role
boss thread
(ServerBootstrap parentGroup)
I of the network used by Netty/A thread that detects O. One is generated by default at startup.
worker thread
(ServerBootstrap childGroup)
I used by Netty/O A thread that processes each event. The default isNumber of processors x 2
executor thread A thread that executes the methods defined in gRPC. Generated for each method call.
I/O event name Overview
OP_ACCEPT Connection from client
OP_READ Network I/Read O
OP_WRITE Network I/Export O

Supplement

For Netty, "JJUG CCC 2018 Spring --I-7 (I) First Netty" is very helpful. Became.

About HTTP / 2

Excerpts from google's Introduction to HTTP / 2 to help you understand gRPC.

In HTTP / 1.x, plain text separated by line breaks is regarded as one request (or response), but in HTTP / 2, this is expressed as one message, and the message is further divided into the following frame units.

--HEADERS frame (HTTP / 1.x header) --DATA frame (HTTP / 1.x body)

In addition, frames can be exchanged in parallel and in both directions with a single TCP connection. In other words, it is not necessary to establish a TCP connection for each request, and multiple responses and server pushes can be made for one request.

gRPC-Java processing overview

From this point onward, it will be a story like "what part of the code and what process ~", so before that, I will summarize what I interpreted about the series of processes.

  1. The gRPC server spawns a boss thread for Netty to use at startup
  2. The gRPC client makes a request to the gRPC server
  3. When the boss thread detects the network I / O event ʻOP_ACCEPT, it creates a Netty worker thread (or gets it from the pool) and delegates the processing of ʻOP_READ / ʻOP_WRITE`.
  4. The worker thread is called every time an I / O event such as TCP connection establishment (3-way handshake, etc.), HTTP / 2 HEADERS frame, DATA frame, etc. occurs.
  5. After receiving the HTTP / 2 message (multiple frames) required for the gRPC call from the client, the worker thread spawns an executor thread to execute the gRPC method.
  6. Netty is an event loop mechanism that operates only on the boss thread and the worker thread, but a thread is created from there for each gRPC method call.
  7. Therefore, it seems that Netty threads are not blocked even if blocking processing is executed in the gRPC method.
  8. The executor thread executes the gRPC method and registers the response ʻOP_WRITE`
  9. The worker thread detects ʻOP_WRITE` and returns a response to the client.
  10. If the client still keeps the connection, there is no need to reestablish the TCP connection, so the processes 5. ~ 7. can be executed many times in parallel.
  11. Disconnect TCP if the client disconnects

Code reading on the Server side

TODO I'm writing it for my own memo, so I'll add it later and organize it. (It was difficult to keep track of creating various threads and creating new threads with callbacks, so I'd be happy if you could tell me if I made a mistake.)

Start-up

--Run RouteGuideServer # main --Get an instance of ServerBuilder with ServerBuilder # forPort --In ServiceProviders # loadAll, look at the ClassLoader to determine if it's an Android app or not. --For non-Android, use the ServiceLoader # load mechanism in the java.util package to META-INF / services / io.grpc.ServerProvider From .16.1 / netty / src / main / resources / META-INF / services / io.grpc.ServerProvider) to [NettyServerProvider.java](https://github.com/grpc/grpc-java/blob/v1.16.1/ I'm getting an instance of netty / src / main / java / io / grpc / netty / NettyServerProvider.java) --In NettyServerBuilder # addService, pass the class that inherits the automatically generated xxxGrpc.xxxImplBase --Here, the methods defined in Protocol Buffers are bound. -AbstractServerImplBuilder.java # L146 Because the automatically generated code bindService is called

Receive request

--When the worker thread accepts a gRPC method call request, NettyServerHandler is ServerImpl $ ServerTransportListenerImpl # streamCreated /io/grpc/netty/NettyServerHandler.java#L437) --ServerImpl $ ServerTransportListenerImpl # streamCreated [pass an instance of StreamCreated that inherits ContextRunnable to the SerializingExecutor that wraps ThreadPoolExecutor](https://github.com/grpc/grpc-java/blob/v1.16.1/core/src/ main / java / io / grpc / internal / ServerImpl.java # L495) --This executor runs the following Runnable task in ThreadFactoryBuilder $ ThreadFactory with the thread name grpc-default-executor-% d --StreamCreated # run-> StreamCreated # runInContext is executed --And ServerImpl # JumpToApplicationThreadServerStreamListener $ MessagesAvailable # runInContext is called --Furthermore, ServerImpl \ $ JumpToApplicationThreadServerStreamListener $ 1HalfClosed # runInContextHalfClosedgRPC method is executed. --When the getFeature defined in gRPC is called, it looks like this

getFeature:130, RouteGuideServer$RouteGuideService (grpc.routeguide)
invoke:462, RouteGuideGrpc$MethodHandlers (grpc.routeguide)
onHalfClose:171, ServerCalls$UnaryServerCallHandler$UnaryServerCallListener (io.grpc.stub)
halfClosed:283, ServerCallImpl$ServerStreamListenerImpl (io.grpc.internal)
runInContext:710, ServerImpl$JumpToApplicationThreadServerStreamListener$1HalfClosed (io.grpc.internal)
run:37, ContextRunnable (io.grpc.internal)
run:123, SerializingExecutor (io.grpc.internal)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:748, Thread (java.lang)

Recommended Posts

I was curious about how gRPC-Java works, so I tried code reading
Java9 was included, so I tried jshell.
I was curious about how to use Optional orElse () and orElseGet () properly.
I was curious about all_month and read ActiveSupport DateAndTime :: Calculations
I tried source code analysis
About the matter that I was addicted to how to use hashmap