From f878771e2bc2186cab0dda3b3b9da101e5eea3a2 Mon Sep 17 00:00:00 2001 From: Carroll Vance Date: Mon, 23 Feb 2026 10:09:42 -0600 Subject: [PATCH] Add __init__ function at package level to reduce boilerplate for using the library / doing codegen --- docs/src/index.md | 3 --- src/Curl.jl | 2 +- src/Streaming.jl | 47 +++++++++++++++++++---------------------------- src/Unary.jl | 33 ++++++++++++--------------------- src/gRPC.jl | 17 +++-------------- src/gRPCClient.jl | 5 +++++ 6 files changed, 40 insertions(+), 67 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index 1f89c94..7c52b25 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -39,9 +39,6 @@ gRPCClient.jl integrates with ProtoBuf.jl to automatically generate Julia client using ProtoBuf using gRPCClient -# Register our service codegen with ProtoBuf.jl -grpc_register_service_codegen() - # Creates Julia bindings for the messages and RPC defined in test.proto protojl("test/proto/test.proto", ".", "test/gen") ``` diff --git a/src/Curl.jl b/src/Curl.jl index fb1664c..a22be52 100644 --- a/src/Curl.jl +++ b/src/Curl.jl @@ -721,7 +721,7 @@ mutable struct gRPCCURL finalizer((x) -> close(x), grpc) # This is used for the global const gRPCCURL handle - # The user is expected to call grpc_init() in order to use it + # grpc_init() is called automatically via __init__() when the package is loaded !running && return grpc open(grpc) diff --git a/src/Streaming.jl b/src/Streaming.jl index d92281d..67a6ec8 100644 --- a/src/Streaming.jl +++ b/src/Streaming.jl @@ -115,27 +115,24 @@ Start a client streaming gRPC request (multiple requests, single response). ```julia using gRPCClient -# Step 1: Initialize gRPC -grpc_init() - -# Step 2: Include generated Protocol Buffer bindings +# Step 1: Include generated Protocol Buffer bindings include("test/gen/test/test_pb.jl") -# Step 3: Create a client +# Step 2: Create a client client = TestService_TestClientStreamRPC_Client("localhost", 8001) -# Step 4: Create a request channel and send requests +# Step 3: Create a request channel and send requests request_c = Channel{TestRequest}(16) put!(request_c, TestRequest(1, zeros(UInt64, 1))) -# Step 5: Initiate the streaming request +# Step 4: Initiate the streaming request req = grpc_async_request(client, request_c) -# Step 6: Close the channel to signal no more requests will be sent +# 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 7: Wait for the single response +# Step 6: Wait for the single response test_response = grpc_async_await(client, req) ``` """ @@ -175,27 +172,24 @@ Start a server streaming gRPC request (single request, multiple responses). ```julia using gRPCClient -# Step 1: Initialize gRPC -grpc_init() - -# Step 2: Include generated Protocol Buffer bindings +# Step 1: Include generated Protocol Buffer bindings include("test/gen/test/test_pb.jl") -# Step 3: Create a client +# Step 2: Create a client client = TestService_TestServerStreamRPC_Client("localhost", 8001) -# Step 4: Create a response channel to receive multiple responses +# Step 3: Create a response channel to receive multiple responses response_c = Channel{TestResponse}(16) -# Step 5: Send a single request (the server will respond with multiple messages) +# 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 6: Process streaming responses (channel closes when server finishes) +# Step 5: Process streaming responses (channel closes when server finishes) for test_response in response_c @info test_response end -# Step 7: Check for exceptions +# Step 6: Check for exceptions grpc_async_await(req) ``` """ @@ -242,33 +236,30 @@ Start a bidirectional streaming gRPC request (multiple requests, multiple respon ```julia using gRPCClient -# Step 1: Initialize gRPC -grpc_init() - -# Step 2: Include generated Protocol Buffer bindings +# Step 1: Include generated Protocol Buffer bindings include("test/gen/test/test_pb.jl") -# Step 3: Create a client +# Step 2: Create a client client = TestService_TestBidirectionalStreamRPC_Client("localhost", 8001) -# Step 4: Create request and response channels (streaming in both directions simultaneously) +# Step 3: Create request and response channels (streaming in both directions simultaneously) request_c = Channel{TestRequest}(16) response_c = Channel{TestResponse}(16) -# Step 5: Initiate the bidirectional streaming request +# Step 4: Initiate the bidirectional streaming request req = grpc_async_request(client, request_c, response_c) -# Step 6: Send requests and receive responses concurrently +# Step 5: Send requests and receive responses concurrently put!(request_c, TestRequest(1, zeros(UInt64, 1))) for test_response in response_c @info test_response break # Exit after first response for this example end -# Step 7: Close the request channel to signal no more requests will be sent +# Step 6: Close the request channel to signal no more requests will be sent close(request_c) -# Step 8: Check for exceptions +# Step 7: Check for exceptions grpc_async_await(req) ``` """ diff --git a/src/Unary.jl b/src/Unary.jl index 111c63b..aadb42a 100644 --- a/src/Unary.jl +++ b/src/Unary.jl @@ -10,22 +10,19 @@ This is ideal when you need to send many requests in parallel and waiting on eac ```julia using gRPCClient -# Step 1: Initialize gRPC (must be called once before making any gRPC requests) -grpc_init() - -# Step 2: Include generated Protocol Buffer bindings +# Step 1: Include generated Protocol Buffer bindings include("test/gen/test/test_pb.jl") -# Step 3: Create a client for your RPC method (hostname, port) +# Step 2: Create a client for your RPC method (hostname, port) client = TestService_TestRPC_Client("localhost", 8001) -# Step 4: Send all requests without waiting for responses +# Step 3: Send all requests without waiting for responses requests = Vector{gRPCRequest}() for i in 1:10 push!(requests, grpc_async_request(client, TestRequest(1, zeros(UInt64, 1)))) end -# Step 5: Wait for and process responses +# Step 4: Wait for and process responses for request in requests response = grpc_async_await(client, request) @info response @@ -79,25 +76,22 @@ This has the advantage over the request / await patern in that you can handle re ```julia using gRPCClient -# Step 1: Initialize gRPC -grpc_init() - -# Step 2: Include generated Protocol Buffer bindings +# Step 1: Include generated Protocol Buffer bindings include("test/gen/test/test_pb.jl") -# Step 3: Create a client +# Step 2: Create a client client = TestService_TestRPC_Client("localhost", 8001) -# Step 4: Create a channel to receive responses (processes responses as they arrive, in any order) +# Step 3: Create a channel to receive responses (processes responses as they arrive, in any order) N = 10 channel = Channel{gRPCAsyncChannelResponse{TestResponse}}(N) -# Step 5: Send all requests (the index tracks which response corresponds to which request) +# 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 6: Process responses as they arrive +# Step 5: Process responses as they arrive for i in 1:N cr = take!(channel) !isnothing(cr.ex) && throw(cr.ex) @@ -169,16 +163,13 @@ Use this when you want the simplest possible interface for a single request. ```julia using gRPCClient -# Step 1: Initialize gRPC -grpc_init() - -# Step 2: Include generated Protocol Buffer bindings +# Step 1: Include generated Protocol Buffer bindings include("test/gen/test/test_pb.jl") -# Step 3: Create a client +# Step 2: Create a client client = TestService_TestRPC_Client("localhost", 8001) -# Step 4: Make a synchronous request (blocks until response is ready) +# Step 3: Make a synchronous request (blocks until response is ready) response = grpc_sync_request(client, TestRequest(1, zeros(UInt64, 1))) @info response ``` diff --git a/src/gRPC.jl b/src/gRPC.jl index 47ab994..6578cb9 100644 --- a/src/gRPC.jl +++ b/src/gRPC.jl @@ -10,28 +10,17 @@ grpc_global_handle() = _grpc """ grpc_init([grpc_curl::gRPCCURL]) -Initializes the `gRPCCURL` object. This should be called once before making gRPC calls. There is no harm in calling this more than once (ie by different packages/dependencies). Typical usage looks like this: - -```julia -grpc_init() - -client = TestService_TestRPC_Client("172.238.177.88", 8001) - -# Make some gRPC calls - -# Shut down the global gRPC handle -grpc_shutdown() -``` +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: -```julia +```julia grpc_myapp = gRPCCURL() grpc_init(grpc_myapp) client = TestService_TestRPC_Client("172.238.177.88", 8001; grpc=grpc_myapp) -# Make some gRPC calls +# Make some gRPC calls # Only shuts down your gRPC handle grpc_shutdown(grpc_myapp) diff --git a/src/gRPCClient.jl b/src/gRPCClient.jl index 9162ae5..d1f6729 100644 --- a/src/gRPCClient.jl +++ b/src/gRPCClient.jl @@ -111,4 +111,9 @@ export gRPCAsyncChannelResponse export gRPCException export gRPCServiceCallException +function __init__() + grpc_init() + grpc_register_service_codegen() +end + end