tutorial

30 Studi Kasus: Streaming Progress Update

Streaming Progress Update Menggunakan Go dan gRPC

Di dunia pengembangan backend modern, pengalaman pengguna yang mulus menjadi perhatian utama. Salah satu tantangan yang sering ditemui adalah bagaimana memberikan update progress secara real-time kepada user, terutama dalam proses backend yang memakan waktu cukup lama seperti parsing, konversi, atau import data besar.

Dalam artikel ini, kita akan membahas studi kasus implementasi Streaming Progress Update menggunakan gRPC Stream di Go (Golang). Kita akan bahas konsep arsitektur, kode implementasi server dan client, dan contoh simulasi dengan tabel serta diagram alur proses.


Permasalahan

Bayangkan sebuah aplikasi desktop atau web yang mengunggah file besar ke backend untuk diproses. Backend memerlukan waktu cukup lama, sementara user tidak diberi tahu progresnya. Hal ini menurunkan UX karena user tidak tahu apakah proses sedang berjalan atau gagal.


Solusi: Streaming Progress via gRPC

gRPC mendukung server-side streaming, memungkinkan server mengirim aliran data berkala ke client dalam satu koneksi gRPC. Ini sangat cocok untuk pengiriman progress update.


Diagram Arsitektur

flowchart LR
    Client -->|StartJob RPC| Server
    Server -->|Stream Progress| Client
    Server -->|Simulasi Proses| Processor

Struktur Proyek

progress-grpc/
├── proto/
│   └── progress.proto
├── server/
│   └── main.go
├── client/
│   └── main.go

1. Definisi Protobuf

proto/progress.proto

syntax = "proto3";

package progress;

service ProgressService {
  rpc StartJob(JobRequest) returns (stream ProgressResponse);
}

message JobRequest {
  string job_id = 1;
}

message ProgressResponse {
  int32 percent = 1;
  string message = 2;
}

Generate dengan:

protoc --go_out=. --go-grpc_out=. proto/progress.proto

2. Implementasi Server

server/main.go

package main

import (
    "context"
    "fmt"
    "log"
    "math/rand"
    "net"
    "time"

    pb "progress-grpc/proto"
    "google.golang.org/grpc"
)

type server struct {
    pb.UnimplementedProgressServiceServer
}

func (s *server) StartJob(req *pb.JobRequest, stream pb.ProgressService_StartJobServer) error {
    progress := 0
    for progress < 100 {
        time.Sleep(1 * time.Second)
        step := rand.Intn(10) + 5
        progress += step
        if progress > 100 {
            progress = 100
        }
        msg := fmt.Sprintf("Progress %d%%", progress)
        stream.Send(&pb.ProgressResponse{
            Percent: int32(progress),
            Message: msg,
        })
    }
    return nil
}

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterProgressServiceServer(s, &server{})
    log.Println("gRPC server running on :50051")
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

3. Implementasi Client

client/main.go

package main

import (
    "context"
    "log"
    "time"

    pb "progress-grpc/proto"
    "google.golang.org/grpc"
)

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

    client := pb.NewProgressServiceClient(conn)
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
    defer cancel()

    stream, err := client.StartJob(ctx, &pb.JobRequest{JobId: "abc123"})
    if err != nil {
        log.Fatalf("error calling StartJob: %v", err)
    }

    for {
        msg, err := stream.Recv()
        if err != nil {
            log.Printf("stream ended: %v", err)
            break
        }
        log.Printf("[%d%%] %s", msg.Percent, msg.Message)
    }
}

Simulasi Output Progress

Berikut simulasi hasil output progress di console client:

[6%] Progress 6%
[13%] Progress 13%
[25%] Progress 25%
[39%] Progress 39%
[52%] Progress 52%
[66%] Progress 66%
[81%] Progress 81%
[95%] Progress 95%
[100%] Progress 100%

Tabel Perbandingan Pendekatan

PendekatanKompatibilitasOverheadResponsifCocok untuk
Polling RESTTinggiTinggiLambatTask ringan
WebsocketMenengahSedangCepatInteraktif 2 arah
SSE (EventStream)Tinggi (HTTP)RendahSedangBrowser, 1 arah
gRPC StreamgRPC onlyRendahCepatInternal RPC/Streaming

Kesimpulan

Dengan memanfaatkan server-side streaming gRPC, kita bisa memberikan update progress secara efisien dan real-time. Pendekatan ini ideal untuk aplikasi internal berbasis microservice, CLI, atau desktop client yang terhubung ke backend berbasis Go.

Studi kasus di atas bisa dikembangkan lebih lanjut:

  • Integrasi dengan Redis untuk status job multi-process.
  • Middleware autentikasi gRPC.
  • Penggunaan UUID atau tracing span untuk jobID.

Semoga studi kasus ini membantu kamu memahami cara menerapkan streaming update yang skalabel dan efektif. 🚀

comments powered by Disqus