24 Implementasi Bidirectional Streaming RPC
Bidirectional Streaming RPC adalah salah satu fitur canggih dalam gRPC, framework open-source yang digagas oleh Google untuk kebutuhan komunikasi antar layanan. Dalam skenario aplikasi modern, sering kali kita butuh komunikasi dua arah yang real-time, misal dalam chat aplikasi, transfer file secara potongan, monitoring, atau bahkan collaborative editing. Di artikel ini kita akan menyelami bagaimana implementasi Bidirectional Streaming RPC di gRPC menggunakan bahasa Go—disertai contoh kode, simulasi, dan diagram alur untuk memperjelas bagaimana arsitektur ini berjalan.
Apa itu Bidirectional Streaming di gRPC?
Bidirectional streaming, atau duplex streaming, artinya baik client maupun server dapat mengirim banyak pesan secara bersamaan dan independen sepanjang sesi koneksi terbuka. Ini jelas lebih fleksibel dibandingkan dengan unary RPC (request-response 1:1), server streaming (1:N), atau client streaming (N:1).
Ilustrasi sederhana aliran data pada Bidirectional Streaming:
flowchart LR
Client -- Pesan 1, 2, 3 --> Server
Server -- Respon A, B, C --> Client
Client -- Pesan 4 --> Server
Server -- Respon D --> Client
Pada diagram di atas, tidak ada aturan mutlak siapa yang harus mengirim pesan duluan. Keduanya bisa saling bertukar data secara bersamaan.
Kapan Bidirectional Streaming RPC Dibutuhkan?
Bidirectional streaming berguna di banyak use-case, seperti:
- Chat Real-time: Client & server saling bertukar pesan secara asinkron.
- Sinkronisasi Data: Misal collaborative editing pada dokumen.
- Telemetri/Metrik Monitoring: Client stream data metrik, server bisa streaming feedback langsung.
Contoh Use-case: Sistem Chat Sederhana
Agar mudah dipahami, kita akan implementasikan sistem chat sederhana dengan gRPC Bidirectional Streaming menggunakan Go.
1. Mendefinisikan Protokol gRPC di .proto
1// chat.proto
2syntax = "proto3";
3
4package chat;
5
6service ChatService {
7 rpc Chat(stream ChatMessage) returns (stream ChatMessage);
8}
9
10message ChatMessage {
11 string user = 1;
12 string message = 2;
13 int64 timestamp = 3;
14}Di sini:
ChatServicepunya metodeChatyang bi-directional streaming.ChatMessagemewakili format pesan chat.
2. Generate Kode gRPC
Jalankan berikut untuk generate kode Go:
1protoc --go_out=. --go-grpc_out=. chat.proto3. Implementasi Server
File server.go:
1package main
2
3import (
4 "log"
5 "net"
6 "sync"
7
8 pb "yourrepo/chat"
9 "google.golang.org/grpc"
10)
11
12type chatServer struct {
13 pb.UnimplementedChatServiceServer
14 mu sync.Mutex
15 streams map[string]pb.ChatService_ChatServer
16}
17
18func newServer() *chatServer {
19 return &chatServer{
20 streams: make(map[string]pb.ChatService_ChatServer),
21 }
22}
23
24func (s *chatServer) Chat(stream pb.ChatService_ChatServer) error {
25 var user string
26 for {
27 msg, err := stream.Recv()
28 if err != nil {
29 log.Printf("user %s left: %v", user, err)
30 s.mu.Lock()
31 delete(s.streams, user)
32 s.mu.Unlock()
33 return err
34 }
35 user = msg.User
36
37 // Register stream
38 s.mu.Lock()
39 s.streams[user] = stream
40 s.mu.Unlock()
41
42 // Broadcast ke semua fitur (simulate group chat)
43 s.mu.Lock()
44 for u, sstr := range s.streams {
45 if u != user {
46 sstr.Send(msg)
47 }
48 }
49 s.mu.Unlock()
50 }
51}
52
53func main() {
54 lis, err := net.Listen("tcp", ":50051")
55 if err != nil {
56 log.Fatalf("failed to listen: %v", err)
57 }
58 s := grpc.NewServer()
59 pb.RegisterChatServiceServer(s, newServer())
60 log.Println("Chat server running on :50051")
61 if err := s.Serve(lis); err != nil {
62 log.Fatalf("failed to serve: %v", err)
63 }
64}Penjelasan:
- Setiap user yang connect didaftarkan dalam map
streams. - Ketika receive pesan, server broadcast ke setiap client lain.
- Koneksi streaming open selama client terhubung.
4. Implementasi Client
File client.go:
1package main
2
3import (
4 "bufio"
5 "context"
6 "log"
7 "os"
8 "time"
9
10 pb "yourrepo/chat"
11 "google.golang.org/grpc"
12)
13
14func main() {
15 conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
16 if err != nil { log.Fatalf("fail to dial: %v", err) }
17 defer conn.Close()
18
19 client := pb.NewChatServiceClient(conn)
20 stream, err := client.Chat(context.Background())
21 if err != nil { log.Fatalf("fail to open stream: %v", err) }
22
23 // Goroutine untuk menerima pesan
24 go func() {
25 for {
26 resp, err := stream.Recv()
27 if err != nil {
28 log.Printf("Disconnected: %v", err)
29 return
30 }
31 log.Printf("%s: %s", resp.User, resp.Message)
32 }
33 }()
34
35 // Kirim pesan dari stdin
36 reader := bufio.NewReader(os.Stdin)
37 user := os.Getenv("USER")
38 for {
39 text, _ := reader.ReadString('\n')
40 stream.Send(&pb.ChatMessage{
41 User: user,
42 Message: text,
43 Timestamp: time.Now().Unix(),
44 })
45 }
46}Simulasi Alur Kerja
Misal, ada dua user: Alice dan Bob.
| Step | Alice (client1) | Server | Bob (client2) |
|---|---|---|---|
| 1 | Connect & stream | Stream register | |
| 2 | Connect & stream | ||
| 3 | “Hai Bob!” | Terima, broadcast | Terima broadcast |
| 4 | “Hai Alice!” | ||
| 5 | Terima, broadcast | Terima broadcast |
Sequence Diagram
sequenceDiagram Alice->>Server: Open stream Bob->>Server: Open stream Alice->>Server: Send "Hai Bob!" Server->>Bob: Send "Hai Bob!" (broadcast) Bob->>Server: Send "Hai Alice!" Server->>Alice: Send "Hai Alice!" (broadcast)
Tabel: Tipe RPC pada gRPC
| Jenis RPC | Client Request | Server Response | Kapan Dipakai |
|---|---|---|---|
| Unary RPC | Single | Single | CRUD, standar API |
| Server Streaming | Single | Multiple (stream) | Notifikasi, Download File |
| Client Streaming | Multiple | Single | Upload file potongan, batch insert |
| Bidirectional Streaming | Multiple | Multiple (stream) | Chat, collaborative, telemetri |
Tantangan & Best Practices
Tantangan
- State management pada server lebih ribet, perlu mapping antara user dan stream.
- Harus handling error/koneksi mati dengan baik agar tidak memory leak.
- Sinkronisasi data jika butuh persistensi.
Best Practices
- Gunakan mutex/lock pada akses map stream untuk thread safety.
- Tutup stream ketika client disconnect untuk menghindari resource leak.
- Jangan kirim terlalu banyak pesan besar atau sangat sering tanpa flow control.
- Logging penting untuk memonitor traffic/latency.
Kesimpulan
Implementasi Bidirectional Streaming RPC di gRPC memungkinkan aplikasi yang membutuhkan komunikasi dua arah secara real-time menjadi jauh lebih mudah dikembangkan. Dengan kerangka kerja network yang sudah dioptimasi dan antarmuka deklaratif via proto file, developer ‘hanya’ perlu fokus di logika aplikasi.
Jangan lupa, fitur bi-directional streaming ini harus diimplementasikan dengan memerhatikan resource management dan concurrency agar aplikasi Anda tetap scalable dan robust.
Sumber kode dan demo lengkap bisa cek di repo Github ini .
Terimakasih sudah membaca. Jika ada pertanyaan/saran, silakan kolom komentar atau DM saya di Medium!