gRPC is a simple protocol built on top of HTTP/2. The typical way of working with gRPC is to use the protoc
compiler
to generate stubs for each method in a gRPC service. The stubs take pre-parsed protobuf objects and return protobuf
objects and all serialization is handled by the framework. This doesn't work all that well for us.
When we receive a gRPC request for paid queries or for any transaction, we need the raw bytes to send to the hashgraph
platform for consensus. We would like to get the raw bytes from the gRPC request, not pre-parsed objects. We also have
our own highly tuned protobuf parsing library that generates nearly no garbage, and we would like to use it for parsing
rather than the code generated by protoc
. For these reasons, the built-in gRPC frameworks are of little to use to us.
It turns out that gRPC is an extremely simple API built on top of HTTP/2, and we can therefore have our own simple gRPC framework built on top of an HTTP/2 server (like Netty) and get everything we want.
The gRPC package in Hedera App defines GrpcServerManager
interface with a number of lifecycle methods such as
start()
, stop()
and isRunning()
, and an implementation of this interface based on Netty. Hedera App runs at least
one gRPC server on the port specified in the config, and optionally it will also attempt to run additional gRPC servers
on the TLS or node operator ports specified in the same config.
NettyGrpcServerManager
is an implementation of GrpcServerManager
and it uses the Hedera App's services registry in
order to gather the set of RPC endpoints and their corresponding handlers. Given that set and using the
GrpcServiceBuilder
class we produce gRPC ServerServiceDefinition
s which can be directly registered with the Netty
server.
As mentioned earlier, the GrpcServiceBuilder
produces gRPC ServerServiceDefinitions
. These definitions are
configured with the appropriate service handlers and gRPC ServerCall.Listener
methods. At runtime, the Netty server
invokes these listener methods. For instance, the onMessage()
method is where service logic is integrated to handle
transactions or queries using the abstract MethodBase
type.
In this abstract class, the size of incoming requests is validated, and requests that exceed the allowed size are
rejected. Additionally, we track metrics for number of calls and calls per second that were received, failed or
successfully handled. There are two concrete types(QueryMethod
and TransactionMethod
) that extend MethodBase
and implement the handle()
method, which when called will invoke the QueryWorkflow
or IngestWorkflow
respectively.
NEXT: Records