gRPC Streaming in Spring Boot: Server, Client, and Bi-Directional Examples

Efficient communication is fundamental when building distributed systems and microservices. gRPC, a high-performance framework, takes this a step further with its support for streaming, enabling real-time communication between systems. Whether you’re building a live chat service, real-time notifications, or dashboards, gRPC streaming is an ideal solution for these use cases.

This article explores the core concepts of gRPC streaming, how to implement server-side and client-side streaming in Spring Boot, and the benefits of bi-directional streaming. Step-by-step examples, common pitfalls, and use case scenarios will ensure you gain a practical understanding of gRPC streaming.

Table of Contents

  1. What is gRPC Streaming?
  2. How to Define Streaming RPCs in .proto
  3. Implementing Server-Streaming Service
  4. Implementing Client-Streaming Service
  5. Implementing Bi-Directional Streaming
  6. Handling Streaming on the Client Side (Async)
  7. Common Pitfalls and How to Avoid Them
  8. Use Cases for gRPC Streaming
  9. Testing Streaming Services with grpcui
  10. Dockerized Example Repo
  11. FAQs
  12. Summary

What is gRPC Streaming?

Streaming in gRPC extends traditional request-response communication by enabling continuous data exchange between client and server. Unlike REST APIs, which rely on discrete requests for communication, gRPC streaming handles data transmission in real-time using HTTP/2.

There are three types of gRPC streaming:

  1. Server-Streaming: Server sends a stream of responses to the client.
  2. Client-Streaming: Client sends a stream of requests to the server.
  3. Bi-Directional Streaming: Both client and server send streams of data in parallel.

This makes gRPC streaming a powerful choice for building scalable, responsive applications requiring real-time communication.

Official gRPC Documentation on Streaming


How to Define Streaming RPCs in .proto

The .proto file is the core of gRPC, where you define the structure of your services and messages. To define streaming services, use the stream keyword.

Example .proto File

Create a file named streaming.proto in src/main/proto/:

   syntax = "proto3";

   package streaming;

   service StreamingService {
       // Server-streaming RPC
       rpc ServerStream (StreamRequest) returns (stream StreamResponse);

       // Client-streaming RPC
       rpc ClientStream (stream StreamRequest) returns (StreamResponse);

       // Bi-directional streaming RPC
       rpc BiDirectionalStream (stream StreamRequest) returns (stream StreamResponse);
   }

   message StreamRequest {
       string message = 1;
   }

   message StreamResponse {
       string response = 1;
   }

The above example:

  • Defines three types of gRPC streaming services.
  • Uses the stream keyword to indicate streaming capabilities for inputs and/or outputs.

Learn Protobuf Syntax


Implementing Server-Streaming Service

The server-streaming pattern allows the server to send multiple responses for a single client request.

gRPC Service

Here’s how to implement server-streaming in Spring Boot:

   @GrpcService
   public class StreamingServerService extends StreamingServiceGrpc.StreamingServiceImplBase {

       @Override
       public void serverStream(StreamRequest request, StreamObserver<StreamResponse> responseObserver) {
           for (int i = 0; i < 5; i++) {
               StreamResponse response = StreamResponse.newBuilder()
                   .setResponse("Chunk " + i)
                   .build();
               responseObserver.onNext(response);
           }
           responseObserver.onCompleted();
       }
   }

The server sends a stream of responses using responseObserver.onNext() followed by onCompleted().

Read About @GrpcService Annotation


Implementing Client-Streaming Service

The client-streaming pattern enables continuous data from the client to the server.

Service Implementation

   @GrpcService
   public class StreamingClientService extends StreamingServiceGrpc.StreamingServiceImplBase {

       @Override
       public StreamObserver<StreamRequest> clientStream(StreamObserver<StreamResponse> responseObserver) {
           return new StreamObserver<StreamRequest>() {
               private StringBuilder messageLog = new StringBuilder();

               @Override
               public void onNext(StreamRequest request) {
                   messageLog.append(request.getMessage()).append("\n");
               }

               @Override
               public void onError(Throwable t) {
                   responseObserver.onError(t);
               }

               @Override
               public void onCompleted() {
                   StreamResponse response = StreamResponse.newBuilder()
                       .setResponse("Received:\n" + messageLog.toString())
                       .build();
                   responseObserver.onNext(response);
                   responseObserver.onCompleted();
               }
           };
       }
   }

Here, the onNext method processes each client message, while onCompleted sends aggregated responses back.


Implementing Bi-Directional Streaming

Bi-directional streaming allows the client and server to send messages independently, which is useful for real-time applications like chat systems.

Bi-Directional Service Implementation

   @GrpcService
   public class BiDirectionalService extends StreamingServiceGrpc.StreamingServiceImplBase {

       @Override
       public StreamObserver<StreamRequest> biDirectionalStream(StreamObserver<StreamResponse> responseObserver) {
           return new StreamObserver<StreamRequest>() {
               @Override
               public void onNext(StreamRequest request) {
                   String responseMessage = "Echoing back: " + request.getMessage();
                   StreamResponse response = StreamResponse.newBuilder()
                       .setResponse(responseMessage)
                       .build();
                   responseObserver.onNext(response);
               }

               @Override
               public void onError(Throwable t) {
                   responseObserver.onError(t);
               }

               @Override
               public void onCompleted() {
                   responseObserver.onCompleted();
               }
           };
       }
   }

This service enables live, parallel data exchange, offering the most flexibility out of all the patterns.

Explore Bi-Directional Streaming in gRPC


Handling Streaming on the Client Side (Async)

Handling streams on the client side involves iterating through responses or managing callbacks for asynchronous communication.

Example Client Consumption

   public void consumeServerStream() {
       StreamObserver<StreamResponse> responseObserver = new StreamObserver<StreamResponse>() {
           @Override
           public void onNext(StreamResponse response) {
               System.out.println("Received response: " + response.getResponse());
           }
           @Override
           public void onError(Throwable t) {
               System.err.println("Stream error occurred");
           }
           @Override
           public void onCompleted() {
               System.out.println("Stream completed");
           }
       };

       StreamRequest request = StreamRequest.newBuilder().setMessage("Client Stream Request").build();
       streamingStub.serverStream(request, responseObserver);
   }

Learn More About Async RPC


Common Pitfalls and How to Avoid Them

  1. Backpressure: Use flow control mechanisms to avoid buffer overflows.
  2. Timeouts: Always configure proper timeouts to avoid infinite waits.
  3. Error Handling: Implement fallback logic for broken streams.

Use Cases for gRPC Streaming

  • Real-Time Dashboards: Stream updates as they occur.
  • Live Chat: Handle instant message exchanges.
  • Streaming Logs: Transmit log data in real-time to monitoring tools.

Testing Streaming Services with grpcui

Install grpcui for an interactive web interface to test gRPC streaming.

Command to launch grpcui:

   grpcui -plaintext localhost:<port>

Dockerized Example Repo

Access the complete example with Docker configuration on GitHub – gRPC Streaming Examples.


FAQs

What is the difference between REST and gRPC streaming?

REST does not support real-time streaming natively, while gRPC offers server, client, and bi-directional streaming.

Is gRPC streaming suitable for real-time applications?

Yes, it’s particularly well-suited for applications requiring instantaneous updates, such as chat or live metrics.

Can I test gRPC streaming with Postman?

No, Postman doesn’t support gRPC. Use tools like grpcui or grpcurl.

How does HTTP/2 enable efficient streaming?

HTTP/2 leverages multiplexing, allowing multiple streams over the same connection, reducing latency.


Summary

gRPC streaming is a transformative addition to your toolkit for building real-time, scalable applications in Spring Boot. By implementing server-streaming, client-streaming, and bi-directional streaming, you can unlock new possibilities for handling dynamic, continuous data flows.

Explore the GitHub repo for a working implementation and start your streaming journey today!

Similar Posts