What Is a Server Streaming RPC?
In server streaming, the client sends a single request message, and the server responds not with one message but with a sequence of messages sent over time on the same HTTP/2 stream, terminated by a status trailer once the server has nothing more to send. The client reads messages from an iterator or callback as they arrive rather than waiting for one complete payload, which lets the server start producing results before the entire result set is ready. This is the natural fit for things like paginated result sets streamed as they're computed, log tailing, or price-update feeds.
Cricket analogy: It is like subscribing to ball-by-ball commentary after asking 'start covering this match': one request, and then a continuous stream of updates — dot ball, four, wicket — arrives until the innings ends.
Backpressure and Flow Control
Because HTTP/2 streams have flow control built in at both the stream and connection level, a server streaming RPC naturally applies backpressure: if the client is slow to read, its receive window shrinks and the server's writes eventually block until the client catches up, preventing unbounded memory growth on either side. Application code should still avoid holding an expensive resource, such as a database cursor, open for the entire duration of a slow-consuming client, and should watch for context cancellation in case the client disconnects mid-stream.
Cricket analogy: It is like a stadium's PA announcer pacing ball updates to how fast the scoreboard operator can key them in — if the operator falls behind, the announcer must wait rather than flooding the board.
Defining and Implementing a Server Streaming RPC
syntax = "proto3";
service StockService {
// Server streaming: one request, a stream of responses.
rpc WatchPrice(WatchPriceRequest) returns (stream PriceUpdate);
}
message WatchPriceRequest {
string symbol = 1;
}
message PriceUpdate {
string symbol = 1;
double price = 2;
int64 timestamp_ms = 3;
}func (s *stockServer) WatchPrice(req *pb.WatchPriceRequest, stream pb.StockService_WatchPriceServer) error {
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-stream.Context().Done():
// Client disconnected or deadline exceeded; stop producing.
return stream.Context().Err()
case <-ticker.C:
price := s.priceFeed.Latest(req.Symbol)
if err := stream.Send(&pb.PriceUpdate{
Symbol: req.Symbol,
Price: price,
TimestampMs: time.Now().UnixMilli(),
}); err != nil {
return err
}
}
}
}A server streaming RPC has no upper bound by default, so a poorly bounded stream can run forever and leak goroutines or threads. Always watch stream.Context().Done() (or the equivalent in your language) so you release resources promptly when the client cancels or disconnects.
Common Use Cases
Server streaming shines whenever a single logical request naturally produces a sequence of results over time: exporting a large table in chunks instead of one giant response, tailing application logs, streaming search results as they're ranked, or pushing live notifications after a client subscribes to a topic. It is also useful for large one-time downloads, since chunking a large file into a stream of byte-range messages avoids allocating one enormous in-memory response and lets the client start processing the first chunk before the rest arrives.
Cricket analogy: It resembles a stadium's video review system streaming replay angles one after another to the third umpire's screen instead of sending a single giant combined video file.
Server streaming responses still share the retry semantics of the underlying call for the initial request, but once messages have started flowing, most gRPC clients will not automatically retry a broken stream — application code typically needs to reconnect and resubscribe from a known offset or checkpoint.
- Server streaming sends one client request but returns a sequence of server messages over one HTTP/2 stream.
- HTTP/2 flow control provides natural backpressure between a fast server and a slow-reading client.
- Handlers must watch for context cancellation to stop producing once a client disconnects.
- The stream is proto-declared with the 'stream' keyword on the response type only.
- Good fits include log tailing, price feeds, paginated exports, and chunked large-file downloads.
- Streams do not auto-retry once started; reconnect-and-resume logic is the application's responsibility.
- Avoid holding expensive resources like DB cursors open for the full duration of a slow client.
Practice what you learned
1. In a server streaming RPC, how many request and response messages are exchanged?
2. What mechanism prevents a fast-producing server from overwhelming a slow-reading client in a server streaming RPC?
3. Where does the 'stream' keyword go in a server streaming RPC's proto definition?
4. What should a server streaming handler do when it detects the client has disconnected mid-stream?
Was this page helpful?
You May Also Like
Unary RPC Explained
The simplest gRPC call pattern: one request in, one response out, modeled as a single HTTP/2 stream that opens and closes in a single round trip.
Client Streaming RPC
A gRPC pattern where the client sends a sequence of messages over one stream and the server replies with a single response once the client finishes.
Bidirectional Streaming RPC
A gRPC pattern where client and server each send an independent, ordered stream of messages over the same connection at the same time.
Choosing the Right RPC Type
A practical decision guide for picking between unary, server-streaming, client-streaming, and bidirectional-streaming gRPC methods.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics