Skip to content
Santekno.com | Tech Tutorials and Trends
EN
📖 0%
01 Jul 2025 · 5 min read ·Article 23 / 110
Go

23 Implementing Client-side Streaming RPC

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

In the world of modern microservices, gRPC has become one of the leading choices for building efficient communication between services, especially when performance and flexibility are top priorities. One of gRPC’s powerful features is its support for streaming RPC. This time, let’s focus on Client-side Streaming RPC, where the client can send many messages sequentially to the server before ultimately receiving a single aggregate response from the server.

This article will cover the concept, usage scenarios, and provide an example implementation of Client-side Streaming RPC using Go (golang). To make things clearer, we’ll include a flowchart diagram and a communication simulation so you get a complete picture of this feature.


What Is Client-side Streaming RPC?

In Client-side Streaming RPC, the communication flow roughly works as follows:

  • The client opens a connection and progressively sends several messages to the server using a stream.
  • Once all the data has been sent, the client closes the stream.
  • The server processes all the data and sends a single response back to the client.

Unlike Unary RPC (one request-one response) or Server Streaming (one request-progressive responses), in client-side streaming the client controls how many messages are sent before requesting a response from the server.

Example Use Cases

Use caseDescription
Progressive file uploadThe client sends parts of a file one by one to the server
IoT batch data transmissionAn IoT device sends sensor data to the server in stages
Client-side loggingThe client sends many log messages then receives a summary

Architecture and Flow

Let’s take a look at the interaction diagram using Mermaid:

MERMAID
sequenceDiagram
    participant Client
    participant Server

    Client->>Server: Buka stream
    loop Satu per satu
      Client->>Server: Kirim message (chunk data)
    end
    Client-->>Server: Tutup stream (Done)
    Server-->>Client: Kirim respon hasil agregasi

Essentially, streaming begins when the client opens a stream, sends all the messages (this can be a loop, for example when a user uploads a 10MB file delivered in 512KB chunks progressively), and finally closes the stream with CloseAndRecv. The server only begins processing after the stream is closed.


Protobuf Schema for Client-streaming

Suppose we want to implement a progressive file upload feature like Google Drive. We can define the gRPC service in an upload.proto file as follows:

proto
 1syntax = "proto3";
 2
 3service FileService {
 4  // UploadFile: Client streaming, server hanya kirim satu hasil.
 5  rpc UploadFile (stream Chunk) returns (UploadStatus);
 6}
 7
 8message Chunk {
 9  bytes content = 1;
10}
11
12message UploadStatus {
13  bool success = 1;
14  string message = 2;
15}

Explanation:

  • Chunk: The message structure sent by the client. It typically contains a portion of the file (bytes).
  • UploadStatus: The final status response from the server.

Implementing Client-side Streaming RPC

1. Server Implementation (Go/gRPC)

On the server, we need to handle the data progressively until the client closes the stream, then reply with a single message.

go
 1// file_service.go
 2type FileServiceServer struct {
 3  pb.UnimplementedFileServiceServer
 4}
 5
 6func (s *FileServiceServer) UploadFile(stream pb.FileService_UploadFileServer) error {
 7    fmt.Println("Start receiving file chunks...")
 8    var totalBytes int64
 9    for {
10        chunk, err := stream.Recv()
11        if err == io.EOF {
12            // Selesai, balas ke client
13            return stream.SendAndClose(&pb.UploadStatus{
14                Success: true,
15                Message: fmt.Sprintf("Upload selesai: %d bytes diterima.", totalBytes),
16            })
17        }
18        if err != nil {
19            return err
20        }
21        // Simulasi write ke disk
22        totalBytes += int64(len(chunk.Content))
23    }
24}

Explanation:

  • stream.Recv(): Retrieves each chunk from the client.
  • When the stream reaches EOF (the client closes the stream), the server replies with the upload status.

2. Client Implementation (Go/gRPC)

The client side opens the stream and then sends the data progressively.

go
 1func uploadFile(client pb.FileServiceClient, filePath string) {
 2    ctx := context.Background()
 3    stream, err := client.UploadFile(ctx)
 4    if err != nil {
 5        log.Fatalf("Gagal membuka stream: %v", err)
 6    }
 7
 8    file, err := os.Open(filePath)
 9    if err != nil {
10        log.Fatalf("Gagal buka file: %v", err)
11    }
12    defer file.Close()
13
14    buf := make([]byte, 512*1024) // Chunk 512KB
15    for {
16        n, err := file.Read(buf)
17        if n > 0 {
18            if err := stream.Send(&pb.Chunk{Content: buf[:n]}); err != nil {
19                log.Fatalf("Gagal kirim chunk: %v", err)
20            }
21        }
22        if err == io.EOF {
23            break
24        } else if err != nil {
25            log.Fatalf("Gagal baca file: %v", err)
26        }
27    }
28
29    status, err := stream.CloseAndRecv()
30    if err != nil {
31        log.Fatalf("Gagal menerima status upload: %v", err)
32    }
33    fmt.Printf("Server reply: %v\n", status.Message)
34}

Communication Simulation

Let’s simulate sending a 2MB file in 512KB chunks:

Chunk OrderSize (KB)Event on the Server
1512Receiving chunk 1 (accumulated: 512KB)
2512Receiving chunk 2 (1MB)
3512Receiving chunk 3 (1.5MB)
4512Receiving chunk 4 (2MB)
EOF-Stream closed, send status

Server response:

Danger
“Upload selesai: 2097152 bytes diterima.”


When Should You Use Client-side Streaming?

Use Client-side Streaming RPC when:

  • The data from the client is large/abundant and you want to avoid a single giant payload.
  • You have realtime data that you want to send progressively before the aggregate result is processed.
  • The client has data in batches, for example updating thousands of records at once.

Comparison (Table)

TechniqueHow It WorksAdvantagesDisadvantages
Unary RPCOne request-one responseSimple, easy to implementLimited payload
Server streamingServer sends a streamContinuous responsesClient can only send one request
Client streamingClient sends a streamClient can batch dataSomewhat complex
Bi-directionalTwo-way streamFull-duplex, very flexibleComplex communication

Conclusion

Client-side Streaming RPC is extremely useful for efficient large-scale or batch data communication without burdening the server or network with a single large payload. With the support of the gRPC protocol, the implementation is relatively simple and scalable. Hopefully this article helps you understand and implement this pattern!

If you’d like to discuss further or see examples in other languages, feel free to leave a comment!


Happy Streaming! 🚀

Related Articles

💬 Comments