Legacy API
Generated ServiceClient Constructors
When you generate service stubs using ProtoBuf.jl, a constructor method is automatically created for each RPC endpoint. These constructors create gRPCServiceClient instances that are used to make RPC calls.
Constructor Signature
For a service method named TestRPC in service TestService, the generated constructor will have the form:
TestService_TestRPC_Client( host, port; secure=false, grpc=grpc_global_handle(), options...)Parameters
host: The hostname or IP address of the gRPC server (e.g.,"localhost","api.example.com")port: The port number the gRPC server is listening on (e.g.,50051)grpc: The global gRPC handle obtained fromgrpc_global_handle(). This manages the underlying libcurl multi-handle for HTTP/2 multiplexing. Default:grpc_global_handle()options...: Any of the following keyword arguments: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, a token can also be attached 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.
Any of the options mentioned above may also be provided as keyword arguments to grpc_async_request or grpc_async_request, taking priority over options set for the client.
Example
# Create a client for the TestRPC endpointclient = TestService_TestRPC_Client( "localhost", 50051; secure=true, # Use HTTPS/gRPCS deadline=30, # 30 second timeout max_send_message_length=10*1024*1024, # 10 MiB max send max_recieve_message_length=10*1024*1024 # 10 MiB max receive)gRPCClient.gRPCServiceClient — Type
gRPCServiceClient{TRequest,SRequest,TResponse,SResponse}(host, port, path; grpc = grpc_global_handle(), options...)The client-side representation of an RPC. Typically only used from autogenerated code.
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.
RPC
Unary
gRPCClient.grpc_async_request — Method
grpc_async_request(client::gRPCServiceClient{TRequest,false,TResponse,false}, request::TRequest) where {TRequest<:Any,TResponse<:Any}Initiate an asynchronous gRPC request: send the request to the server and then immediately return a gRPCRequest object without waiting for the response. In order to wait on / retrieve the result once its ready, call grpc_async_await. This is ideal when you need to send many requests in parallel and waiting on each response before sending the next request would things down.
using gRPCClient# Step 1: Include generated Protocol Buffer bindingsinclude("test/gen/test/test_pb.jl")# Step 2: Create a client for your RPC method (hostname, port)client = TestService_TestRPC_Client("localhost", 8001)# Step 3: Send all requests without waiting for responsesrequests = Vector{gRPCRequest}()for i in 1:10 push!(requests, grpc_async_request(client, TestRequest(1, zeros(UInt64, 1))))end# Step 4: Wait for and process responsesfor request in requests response = grpc_async_await(client, request) @info responseendgRPCClient.grpc_async_request — Method
grpc_async_request(client::gRPCServiceClient{TRequest,false,TResponse,false}, request::TRequest, channel::Channel{gRPCAsyncChannelResponse{TResponse}}, index::Int64; options...) where {TRequest<:Any,TResponse<:Any}Initiate an asynchronous gRPC request: send the request to the server and then immediately return. When the request is complete a background task will put the response in the provided channel. This has the advantage over the request / await patern in that you can handle responses immediately after they are recieved in any order.
The connection may be configured further by providing a set of keyword arguments. Available options are listed in the docstring of gRPCServiceClient.
using gRPCClient# Step 1: Include generated Protocol Buffer bindingsinclude("test/gen/test/test_pb.jl")# Step 2: Create a clientclient = TestService_TestRPC_Client("localhost", 8001)# Step 3: Create a channel to receive responses (processes responses as they arrive, in any order)N = 10channel = Channel{gRPCAsyncChannelResponse{TestResponse}}(N)# Step 4: Send all requests (the index tracks which response corresponds to which request)for (index, request) in enumerate([TestRequest(i, zeros(UInt64, i)) for i in 1:N]) grpc_async_request(client, request, channel, index)end# Step 5: Process responses as they arrivefor i in 1:N cr = take!(channel) !isnothing(cr.ex) && throw(cr.ex) @assert length(cr.response.data) == cr.indexendgRPCClient.grpc_async_await — Method
grpc_async_await(client::gRPCServiceClient{TRequest,false,TResponse,false}, request::gRPCRequest) where {TRequest<:Any,TResponse<:Any}Wait for the request to complete and return the response when it is ready. Throws any exceptions that were encountered during handling of the request.
gRPCClient.grpc_sync_request — Method
grpc_sync_request(client::gRPCServiceClient{TRequest,false,TResponse,false}, request::TRequest; options...) where {TRequest<:Any,TResponse<:Any}Do a synchronous gRPC request: send the request and wait for the response before returning it. Under the hood this just calls grpc_async_request and grpc_async_await. Use this when you want the simplest possible interface for a single request.
The connection may be configured further by providing a set of keyword arguments. Available options are listed in the docstring of gRPCServiceClient.
using gRPCClient# Step 1: Include generated Protocol Buffer bindingsinclude("test/gen/test/test_pb.jl")# Step 2: Create a clientclient = TestService_TestRPC_Client("localhost", 8001)# Step 3: Make a synchronous request (blocks until response is ready)response = grpc_sync_request(client, TestRequest(1, zeros(UInt64, 1)))@info responseStreaming
gRPCClient.grpc_async_request — Method
grpc_async_request(client::gRPCServiceClient{TRequest,true,TResponse,false}, request::Channel{TRequest}; options...) where {TRequest<:Any,TResponse<:Any}Start a client streaming gRPC request (multiple requests, single response).
The connection may be configured further by providing a set of options as keyword arguments. Available options are listed in the docstring of gRPCServiceClient.
using gRPCClient# Step 1: Include generated Protocol Buffer bindingsinclude("test/gen/test/test_pb.jl")# Step 2: Create a clientclient = TestService_TestClientStreamRPC_Client("localhost", 8001)# Step 3: Create a request channel and send requestsrequest_c = Channel{TestRequest}(16)put!(request_c, TestRequest(1, zeros(UInt64, 1)))# Step 4: Initiate the streaming requestreq = grpc_async_request(client, request_c)# Step 5: Close the channel to signal no more requests will be sent# (the server won't respond until the stream ends)close(request_c)# Step 6: Wait for the single responsetest_response = grpc_async_await(client, req)gRPCClient.grpc_async_request — Method
grpc_async_request(client::gRPCServiceClient{TRequest,false,TResponse,true},request::TRequest,response::Channel{TResponse}; options...) where {TRequest<:Any,TResponse<:Any}Start a server streaming gRPC request (single request, multiple responses).
The connection may be configured further by providing a set of options as keyword arguments. Available options are listed in the docstring of gRPCServiceClient.
using gRPCClient# Step 1: Include generated Protocol Buffer bindingsinclude("test/gen/test/test_pb.jl")# Step 2: Create a clientclient = TestService_TestServerStreamRPC_Client("localhost", 8001)# Step 3: Create a response channel to receive multiple responsesresponse_c = Channel{TestResponse}(16)# Step 4: Send a single request (the server will respond with multiple messages)req = grpc_async_request(client, TestRequest(1, zeros(UInt64, 1)), response_c)# Step 5: Process streaming responses (channel closes when server finishes)for test_response in response_c @info test_responseend# Step 6: Check for exceptionsgrpc_async_await(req)gRPCClient.grpc_async_request — Method
grpc_async_request(client::gRPCServiceClient{TRequest,true,TResponse,true},request::Channel{TRequest},response::Channel{TResponse}; options...) where {TRequest<:Any,TResponse<:Any}Start a bidirectional streaming gRPC request (multiple requests, multiple responses).
The connection may be configured further by providing a set of keyword arguments. Available options are listed in the docstring of gRPCServiceClient.
using gRPCClient# Step 1: Include generated Protocol Buffer bindingsinclude("test/gen/test/test_pb.jl")# Step 2: Create a clientclient = TestService_TestBidirectionalStreamRPC_Client("localhost", 8001)# Step 3: Create request and response channels (streaming in both directions simultaneously)request_c = Channel{TestRequest}(16)response_c = Channel{TestResponse}(16)# Step 4: Initiate the bidirectional streaming requestreq = grpc_async_request(client, request_c, response_c)# Step 5: Send requests and receive responses concurrentlyput!(request_c, TestRequest(1, zeros(UInt64, 1)))for test_response in response_c @info test_response break # Exit after first response for this exampleend# Step 6: Close the request channel to signal no more requests will be sentclose(request_c)# Step 7: Check for exceptionsgrpc_async_await(req)gRPCClient.grpc_async_await — Method
grpc_async_await(client::gRPCServiceClient{TRequest,true,TResponse,false},request::gRPCRequest) where {TRequest<:Any,TResponse<:Any}Raise any exceptions encountered during the streaming request.
Cancellation
Every request is bounded by its deadline even when libcurl stops driving the transfer. libcurl does not process a handle's timeouts while the handle is parked waiting for another request's connection to become multiplexable (CURLOPT_PIPEWAIT), so a connection that never becomes ready (for example a server that accepts TCP but never completes the HTTP/2 handshake) would otherwise wedge every queued request forever. A client-side watchdog guarantees each request resolves with DEADLINE_EXCEEDED shortly after its deadline.
The deadline also covers time spent queued client-side waiting for one of the handle's max_streams slots, so a request submitted while every slot is held by a slow or stuck call still fails at its own deadline rather than waiting indefinitely for a slot.
The exception contract: grpc_async_request throws only for conditions it can detect synchronously at submission (an uninitialized or shut-down handle as UNAVAILABLE, an invalid deadline or a token/authorization-metadata conflict as INVALID_ARGUMENT, an oversized message as RESOURCE_EXHAUSTED). Every failure that depends on time or concurrency (deadline exceeded, including expiry while queued, cancellation, transport errors, server statuses) is raised by grpc_async_await.
Shutting a handle down with grpc_shutdown fails every request still in flight with UNAVAILABLE from grpc_async_await, and a request queued for a max_streams slot with UNAVAILABLE from grpc_async_request. A shut-down handle is usually about to be re-initialized, so these calls can be retried, on this handle once grpc_init has run again or elsewhere.
An in-flight request can also be cancelled explicitly at any time:
gRPCClient.grpc_cancel — Function
grpc_cancel(req::gRPCRequest[, ex::Exception])Gracefully cancel an in-flight request. The easy handle is removed from the libcurl multi, aborting the transfer, all tasks waiting on the request (including streaming channels) are unblocked, and grpc_async_await will throw ex, a CANCELLED gRPCServiceCallException by default.
Safe to call at any time from any task. Returns true when this call performed the cancellation and false when the request had already completed (or the handle was already shut down), in which case nothing changes.
For a long-lived stream you can combine deadline = Inf with grpc_cancel to manage the stream's lifetime yourself: with no deadline the call never times out (client-side or server-side, since the grpc-timeout header is omitted), and cancellation ends it on demand. After cancelling a client or bidirectional stream, close your request channel as usual to release its pump task. Note that with no deadline a request stuck behind a connection that never becomes ready will wait forever, and until it is cancelled it is never cleaned up and leaks its resources; grpc_cancel (or grpc_shutdown) is the only way out, so prefer a finite deadline unless you have an explicit lifecycle for the call.
Raw Encoded Buffers (Partial Decoding)
By default the client encodes a typed message before sending and decodes the response into a typed message before returning it. You can bypass either step and work directly with the raw protobuf payload by declaring the relevant message type parameter as Vector{UInt8}. This is useful when you want to forward bytes you already hold, or partially decode a large response and read only the fields you care about.
A normal protobuf message type is always a generated struct, so Vector{UInt8} is unambiguous as a "raw buffer" marker for the message type parameter. The raw buffer is the serialized protobuf message body only; the 5-byte gRPC framing is still added and stripped by the library, so you never handle it yourself.
Each generated *_Client constructor accepts TRequest and TResponse keyword arguments that default to the proto message types. Override either (or both) with Vector{UInt8} to make that side raw. The request and response sides are independent, so you can make either or both raw.
Send a raw request and receive a raw response:
using ProtoBuf# Build the request bytes yourself (here, by encoding a typed message)io = IOBuffer()encode(ProtoEncoder(io), MyRequest(42, zeros(UInt64, 10)))raw_request = take!(io)client = MyService_MyRPC_Client( "localhost", 50051; TRequest = Vector{UInt8}, TResponse = Vector{UInt8},)raw_response = grpc_sync_request(client, raw_request) # raw_response::Vector{UInt8}# Partially decode only what you need from the response payloadresponse = decode(ProtoDecoder(IOBuffer(raw_response)), MyResponse)Mixed combinations work too. To send a typed request but receive the response as raw bytes, override only TResponse:
client = MyService_MyRPC_Client("localhost", 50051; TResponse = Vector{UInt8})raw_response = grpc_sync_request(client, MyRequest(42, UInt64[]))Raw buffers apply to streaming as well: override the streaming side's type with Vector{UInt8} and use a Channel{Vector{UInt8}}. For example, a server-streaming call that receives each response as raw bytes:
client = MyService_MyServerStreamRPC_Client( "localhost", 50051; TResponse = Vector{UInt8},)response_c = Channel{Vector{UInt8}}(16)req = grpc_async_request(client, raw_request, response_c)for raw in response_c response = decode(ProtoDecoder(IOBuffer(raw)), MyResponse) # process responseendgrpc_async_await(req)If you do not have a generated constructor, the same applies by building a gRPCServiceClient directly with Vector{UInt8} type parameters and the RPC path, for example gRPCClient.gRPCServiceClient{Vector{UInt8}, false, Vector{UInt8}, false}("localhost", 50051, "/foo.MyService/MyRPC").
Exceptions
gRPCClient.gRPCServiceCallException — Type
Exception type that is thrown when something goes wrong while calling an RPC. This can either be triggered by the servers response code or by the client when something fails.
This exception type has two fields:
grpc_status::Int- See here for an indepth explanation of each status.message::String