Setup API
gRPCClient.jl exposes two APIs. The one documented in this guide is the current, recommended interface: a reusable gRPCChannel together with the service modules emitted by code generation, where each RPC is called as a function on the channel. The older Legacy API, based on generated *_Client constructors and grpc_async_request / grpc_sync_request, remains available for backwards compatibility and is documented on its own page. Both are generated by default; new code should prefer the API described here.
Package Initialization / Shutdown
gRPCClient.grpc_init — Method
grpc_init([grpc_curl::gRPCCURL])Initializes the gRPCCURL object. The global handle is initialized automatically when the package is loaded. There is no harm in calling this more than once (ie by different packages/dependencies).
Unless specifying a gRPCCURL the global one provided by grpc_global_handle() is used. Each gRPCCURL state has its own connection pool and request semaphore, so sometimes you may want to manage your own like shown below:
The sticky keyword selects the concurrency model used for every task the handle spawns. The default sticky = false uses a multithreading model (Threads.@spawn). Passing sticky = true uses a coroutine model (@async) that is pinned to the spawning thread and is incompatible with multithreading.
grpc_myapp = gRPCCURL()grpc_init(grpc_myapp)client = TestService_TestRPC_Client("172.238.177.88", 8001; grpc=grpc_myapp)# Make some gRPC calls# Only shuts down your gRPC handlegrpc_shutdown(grpc_myapp)gRPCClient.grpc_shutdown — Method
grpc_shutdown([grpc_curl::gRPCCURL])Shuts down the gRPCCURL. This neatly cleans up all active connections and requests. Useful for calling during development with Revise. Unless specifying the gRPCCURL, the global one provided by grpc_global_handle() is shutdown.
gRPCClient.grpc_global_handle — Method
grpc_global_handle()Returns the global gRPCCURL state which contains a libCURL multi handle. By default all gRPC clients use this multi in order to ensure that HTTP/2 multiplexing happens where possible.
gRPCChannel
gRPCClient.gRPCChannel — Type
gRPCChannel(host::AbstractString, port::Integer[; grpc::gRPCCURL = grpc_global_handle(), options...])A lightweight description of the connection to a gRPC server.
The port and hostname are required. By default, a gRPCChannel will handle all communication to the server through its gRPCCURL instance, which defaults to the global default instance. Additional options for the connection may be provided as keyword arguments. Note that these settings only set the defaults when using a gRPCChannel, but may be overridden in each call.
Options
The following options may be used for controlling the communication to a server:
secure: ABoolthat controls whether HTTPS/gRPCS (whentrue) or HTTP/gRPC (whenfalse) is used for the connection. Default:falsedeadline: The gRPC deadline in seconds, covering the entire call from submission: time spent queued client-side waiting for one of the handle'smax_streamsslots counts against it, and the transfer (and thegrpc-timeoutheader sent to the server) gets only the remaining budget. If the call takes longer than this limit it is cancelled and raises an exception. PassInffor no deadline: no timeout is enforced client-side and nogrpc-timeoutheader is sent, so the call runs until it completes orgrpc_cancelends it. Must be non-negative;0expires immediately, and a negative,NaN, or-Infdeadline raisesINVALID_ARGUMENT. Default:10keepalive: The TCP keepalive interval in seconds. This sets bothCURLOPT_TCP_KEEPINTVL(interval between keepalive probes) andCURLOPT_TCP_KEEPIDLE(time before first keepalive probe) to help detect broken connections. Default:60max_send_message_length: The maximum size in bytes for messages sent to the server. Attempting to send messages larger than this will raise an exception. Default:4*1024*1024(4 MiB)max_recieve_message_length: The maximum size in bytes for messages received from the server. Receiving messages larger than this will raise an exception. Default:4*1024*1024(4 MiB)token: Optional bearer token attached to every request as anauthorization: Bearer <token>header.metadata: ADict{String, String}for adding arbitrary fields to the header. For example, the token field can also added by settingmetadata = Dict("authorization" => "Bearer <token>")instead of using thetokenkeyword. Settingtokenand anauthorizationmetadata entry at the same time raisesINVALID_ARGUMENT, since the two are the same header and sending both would leave which one applies up to the server; the check is case-insensitive. To override a client-leveltokenwith per-request metadata, passtoken = nothingalongside it.
Example
In the example below, the setting secure will be overriden in the RPC call.
chan = gRPCChannel("127.0.0.1", 12345, secure = true)response = MyService.MyUnaryRPC(chan, MyMessage(), secure = false)Code generation
When protojl runs with gRPCClient loaded, every service block in the .proto file is expanded into Julia client code, sitting alongside the generated request and response message types. Each service becomes a module whose functions are its RPCs. For example, this single-RPC service definition
syntax = "proto3";
package test;
option go_package = "./;main";
service TestService {
rpc TestRPC(TestRequest) returns (TestResponse) {}
}is expanded to the following service module (the TestRequest and TestResponse message types are generated separately in the same file):
baremodule TestService import gRPCClient import Base # Check compatibility between the loaded version of `gRPCClient` and # the version used to generate this module (1.2.0-rc1). gRPCClient.check_codegen_compat(Base.VersionNumber("1.2.0-rc1")) const TestResponse::DataType = Base.parentmodule(TestService).TestResponse const TestRequest::DataType = Base.parentmodule(TestService).TestRequest # TestService.TestRPC Base.@inline function TestRPC(chan::gRPCClient.gRPCChannel, req::TestRequest, args...; kws...) gRPCClient.grpc_call_unary(chan, typeof(TestRPC), req, args...; kws...) end Base.@inline function TestRPC(chan::gRPCClient.gRPCChannel, req::Base.Vector{UInt8}, args...; kws...) gRPCClient.grpc_call_unary(chan, typeof(TestRPC), req, args...; kws...) end Base.@inline function TestRPC(host::AbstractString, port::Integer, args...; kws...) TestRPC(gRPCClient.gRPCChannel(host, port), args...; kws...) end gRPCClient.rpc_path(::Type{typeof(TestRPC)}) = "/test.TestService/TestRPC" gRPCClient.isstreaming_request(::Type{typeof(TestRPC)}) = false gRPCClient.isstreaming_response(::Type{typeof(TestRPC)}) = false gRPCClient.request_type(::Type{typeof(TestRPC)}) = TestRequest gRPCClient.response_type(::Type{typeof(TestRPC)}) = TestResponse gRPCClient.request_type_displayname(::Type{typeof(TestRPC)}) = "TestRequest" gRPCClient.response_type_displayname(::Type{typeof(TestRPC)}) = "TestResponse" Base.@doc gRPCClient.grpc_generate_rpc_docstring(typeof(TestRPC)) TestRPC export TestRPCendA few things worth noting about the generated module:
- The service is represented as a
baremodule, so that tab-completion and IDE suggestions will show the available RPCs and nothing else. - A check is done for ensuring that the generated code is compatible with the loaded version of
gRPCClient.jl. - The generated methods for the
rpcclarifies which message types the rpc accepts as input. - The generated methods are only thin wrappers around generic implementations residing in the loaded version of
gRPCClient.jl. - A series of traits are generated, describing the request/response types and other information about the
rpc. - An automatic docstring is created.
Which of the two APIs is emitted is controlled by the keyword arguments to grpc_register_service_codegen, both of which default to true:
servicemoduleemits the module-per-service API shown above, the interface documented in this guide.legacyemits the older*_Clientconstructors documented under Legacy API.
protojl picks up whatever was last registered, so call grpc_register_service_codegen before generating if you want to change the defaults. With both enabled the generated file works under either API, and the service-module code is additionally wrapped in a version guard so the file still loads (using the legacy API) on versions of gRPCClient.jl that predate the new one. For new applications, grpc_register_service_codegen(legacy = false) is recommended: it drops the legacy constructors, so less code is generated and IDE suggestions are improved.
gRPCClient.grpc_register_service_codegen — Method
grpc_register_service_codegen(; legacy::Bool = true, servicemodule::Bool = true)Registers gRPCClient for code generation of services in ProtoBuf.jl.
Can generate code either for the legacy API (legacy = true) or for the new API where each service is represented as a module. Both options are enabled by default, but setting legacy = false is recommended for new applications. Setting legacy = false will enable better support for syntax hints. The legacy API may be disable by default or removed in a future version.