Hello Stream - Server Streaming RPC
This example builds on 01_hello_world by adding server streaming, where a single request receives multiple responses over time.
What You'll Learn
- Adding a streaming RPC method to a proto service
- Implementing a server streaming handler
- Using
ServerStreamto send multiple responses - Handling client cancellation
Proto Definition
// greeter.proto
syntax = "proto3";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
rpc SayHelloStream (HelloRequest) returns (stream HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}The stream keyword before HelloReply indicates this method returns multiple responses.
Server Implementation
using gRPCServer
include("generated/helloworld/helloworld.jl")
using .helloworld
# Unary handler (same as before)
function say_hello(ctx::ServerContext, request::HelloRequest)::HelloReply
@info "Received request" name=request.name request_id=ctx.request_id
HelloReply("Hello, $(request.name)!")
end
# Server streaming handler
function say_hello_stream(
ctx::ServerContext,
request::HelloRequest,
stream::ServerStream{HelloReply}
)::Nothing
for i in 1:5
if ctx.cancelled
@warn "Stream cancelled by client"
return nothing
end
send!(stream, HelloReply("Hello $(i), $(request.name)!"))
sleep(0.5)
end
return nothing
end
# Service definition
struct GreeterService end
function gRPCServer.service_descriptor(::GreeterService)
ServiceDescriptor(
"helloworld.Greeter",
Dict(
"SayHello" => MethodDescriptor(
"SayHello", MethodType.UNARY,
HelloRequest, HelloReply,
say_hello
),
"SayHelloStream" => MethodDescriptor(
"SayHelloStream", MethodType.SERVER_STREAMING,
HelloRequest, HelloReply,
say_hello_stream
)
),
nothing
)
end
function main()
host = "127.0.0.1"
port = 50051
server = GRPCServer(host, port;
enable_health_check = true,
enable_reflection = true
)
register!(server, GreeterService())
@info "gRPC server starting" host=host port=port
run(server)
end
main()Key Concepts
Streaming Handler Signature
Server streaming handlers receive an additional parameter:
function handler(
ctx::ServerContext,
request::RequestType,
stream::ServerStream{ResponseType}
)::NothingThe handler returns Nothing instead of a response type.
Sending Responses
Use send!(stream, response) to send each response:
for item in items
send!(stream, ResponseType(...))
endCancellation Handling
Check ctx.cancelled to detect client cancellation:
if ctx.cancelled
@warn "Client cancelled the stream"
return nothing
endThis is important for long-running streams to avoid wasting resources.
Method Type
Use MethodType.SERVER_STREAMING in the MethodDescriptor:
MethodDescriptor(
"MethodName", MethodType.SERVER_STREAMING,
RequestType, ResponseType,
handler
)Testing
Run the Server
cd examples/02_hello_stream
julia --project=../.. server.jlCall Unary RPC
grpcurl -plaintext -d '{"name": "Julia"}' localhost:50051 helloworld.Greeter/SayHelloExpected output:
{
"message": "Hello, Julia!"
}Call Streaming RPC
grpcurl -plaintext -d '{"name": "Julia"}' localhost:50051 helloworld.Greeter/SayHelloStreamExpected output (5 messages over ~2.5 seconds):
{
"message": "Hello 1, Julia!"
}
{
"message": "Hello 2, Julia!"
}
{
"message": "Hello 3, Julia!"
}
{
"message": "Hello 4, Julia!"
}
{
"message": "Hello 5, Julia!"
}Other Streaming Patterns
Client Streaming
Single response after receiving multiple requests:
function handler(ctx::ServerContext, stream::ClientStream{RequestType})
for request in stream
# Process each request
end
return ResponseType(...)
endMethod type: MethodType.CLIENT_STREAMING
Bidirectional Streaming
Multiple requests and responses simultaneously:
function handler(ctx::ServerContext, stream::BidiStream{RequestType, ResponseType})
for request in stream
send!(stream, ResponseType(...))
end
close!(stream)
return nothing
endMethod type: MethodType.BIDI_STREAMING
Next Steps
Proceed to Sum Numbers to learn about client streaming, where multiple requests produce a single response.