tutorial

28 Studi Kasus: Streaming Chat Sederhana

Chat menjadi salah satu fitur yang wajib ada pada banyak aplikasi modern saat ini. Entah itu website toko daring, aplikasi edukasi, atau platform komunitas—semua membutuhkan interaksi real-time agar pengguna merasa lebih terlibat. Pada artikel kali ini, saya akan membahas studi kasus sederhana: bagaimana merancang dan mengimplementasikan sistem chat streaming menggunakan teknologi gRPC Streaming yang populer di lingkungan backend modern.

Studi Kasus: Persyaratan & Arsitektur

Persyaratan Fitur

  1. Pengguna dapat mengirim pesan chat.
  2. Semua pengguna yang terhubung dapat menerima pesan chat terbaru secara real-time.
  3. Setiap pengguna dapat melihat histori sesi chat ketika mereka masuk.
  4. Sistem cukup sederhana untuk dijalankan secara lokal.
  5. Toleransi sederhana terhadap multiple client.

Diagram Arsitektur

flowchart TD
    Client1 -->|Bidirectional Streaming| gRPCServer
    Client2 -->|Bidirectional Streaming| gRPCServer
    gRPCServer -->|Store Chat| DB[(Database)]
  • Klien membuka koneksi bidirectional streaming dengan server gRPC.
  • Ketika pesan dikirim, server menyimpannya ke database (atau memori), lalu melemparkannya ke semua klien aktif.

1. Persiapan: Stack Teknologi

  • Bahasa: Go
  • gRPC Framework: google.golang.org/grpc
  • Protobuf Compiler: protoc
  • Database: In-memory (slice/array) atau SQLite/Redis (opsional untuk persistensi)

2. Definisi Protobuf

chat.proto

syntax = "proto3";

package chat;

message ChatMessage {
  string username = 1;
  string message = 2;
  string timestamp = 3;
}

service ChatService {
  rpc ChatStream(stream ChatMessage) returns (stream ChatMessage);
}

3. Implementasi Server gRPC di Go

package main

import (
	"context"
	"fmt"
	"log"
	"net"
	"sync"
	"time"

	pb "yourmodule/chat"
	"google.golang.org/grpc"
)

type chatServer struct {
	pb.UnimplementedChatServiceServer
	mu      sync.Mutex
	clients map[string]pb.ChatService_ChatStreamServer
	msgs    []pb.ChatMessage
}

func (s *chatServer) ChatStream(stream pb.ChatService_ChatStreamServer) error {
	id := fmt.Sprintf("client-%d", time.Now().UnixNano())
	s.mu.Lock()
	s.clients[id] = stream
	for _, msg := range s.msgs {
		stream.Send(&msg)
	}
	s.mu.Unlock()

	defer func() {
		s.mu.Lock()
		delete(s.clients, id)
		s.mu.Unlock()
	}()

	for {
		msg, err := stream.Recv()
		if err != nil {
			log.Printf("client %s disconnected", id)
			return err
		}

		msg.Timestamp = time.Now().Format(time.RFC3339)
		s.mu.Lock()
		s.msgs = append(s.msgs, *msg)
		for _, c := range s.clients {
			if err := c.Send(msg); err != nil {
				log.Println("send error:", err)
			}
		}
		s.mu.Unlock()
	}
}

func main() {
	lis, err := net.Listen("tcp", ":50051")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	server := grpc.NewServer()
	pb.RegisterChatServiceServer(server, &chatServer{
		clients: make(map[string]pb.ChatService_ChatStreamServer),
	})
	log.Println("gRPC chat server started on :50051")
	server.Serve(lis)
}

4. Simulasi Client gRPC

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	pb "yourmodule/chat"
	"google.golang.org/grpc"
)

func main() {
	conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("fail connect: %v", err)
	}
	defer conn.Close()

	client := pb.NewChatServiceClient(conn)
	stream, err := client.ChatStream(context.Background())
	if err != nil {
		log.Fatalf("fail stream: %v", err)
	}

	// Receive stream
	go func() {
		for {
			msg, err := stream.Recv()
			if err != nil {
				log.Fatal(err)
			}
			fmt.Printf("[%s] %s: %s\n", msg.Timestamp, msg.Username, msg.Message)
		}
	}()

	// Send messages
	for {
		var text string
		fmt.Print("> ")
		fmt.Scanln(&text)
		stream.Send(&pb.ChatMessage{
			Username: "ihsan",
			Message:  text,
		})
	}
}

5. Tabel Komponen dan Fungsinya

KomponenFungsi
ChatService.ChatStreamEndpoint utama gRPC untuk bidirectional streaming chat
ChatMessageStruktur data pesan yang dikirim dan diterima
map[string]streamDaftar koneksi klien aktif untuk broadcast
[]ChatMessageRiwayat chat untuk dikirim ulang saat klien terhubung

6. Diagram Sekuensial Proses

sequenceDiagram
  participant Client as Klien
  participant Server as gRPC Server

  Client->>Server: Buka Stream
  Server-->>Client: Kirim histori pesan
  loop Chat
    Client->>Server: Kirim ChatMessage
    Server->>Server: Simpan pesan
    Server->>Client: Broadcast ke semua klien
  end

7. Kesimpulan

Dengan memanfaatkan gRPC bidirectional streaming, kita bisa membangun sistem chat real-time yang ringan, cepat, dan efisien untuk banyak klien. Pendekatan ini cocok untuk berbagai kebutuhan backend modern mulai dari fitur live support hingga aplikasi kolaborasi.

Untuk pengembangan lebih lanjut, Anda bisa menambahkan autentikasi, menyimpan pesan ke database sungguhan, atau mengintegrasikan sistem pub/sub seperti Redis agar bisa dijalankan secara horizontal.

Selamat mencoba dan semoga bermanfaat! 🚀

comments powered by Disqus