Skip to content
Santekno.com | Level Up Your Engineering Skills
ID
📖 0%
14 Jul 2025 · 4 mnt baca ·Artikel 36 / 110
Go

36 Rate Limiting di Interceptor

IH
Ihsan Arif
Penulis di Santekno · Backend Engineer

Dalam pengembangan microservice modern, kita tak hanya berbicara tentang performa dan skalabilitas, tetapi juga bagaimana melindungi service dari abuse dan lonjakan trafik berlebih. Salah satu mekanisme penting untuk ini adalah rate limiting.

Artikel ini membahas bagaimana menerapkan rate limiting secara elegan di level interceptor pada gRPC dengan bahasa Go. Kita akan membahas konsep, implementasi, dan praktik terbaiknya agar service kamu tetap tangguh dalam menghadapi beban tinggi.


Apa Itu Rate Limiting?

Rate limiting adalah teknik untuk membatasi jumlah permintaan (request) yang dapat dilakukan oleh client dalam jangka waktu tertentu. Misalnya:

  • 10 request per detik per user
  • 1000 request per menit per IP address

Tujuan utamanya:

  • 🚫 Mencegah abuse dan spam
  • 🧱 Melindungi service dari overloading
  • 🧭 Menjaga fairness antara client

Pendekatan Rate Limiting di gRPC

gRPC menyediakan dukungan interceptor di sisi server (dan client). Kita dapat menyisipkan rate limiter di UnaryServerInterceptor agar setiap request terlebih dulu dicek apakah masih dalam batas yang diizinkan.

Untuk implementasi di Go, kita bisa gunakan:

  • golang.org/x/time/rate (leaky bucket)
  • Pustaka open-source seperti uber-go/ratelimit

Contoh Skenario: 5 Request per Detik per Client

Instalasi Dependency

bash
1go get golang.org/x/time/rate

Implementasi Interceptor Rate Limiter

go
 1package middleware
 2
 3import (
 4    "context"
 5    "sync"
 6    "time"
 7
 8    "google.golang.org/grpc"
 9    "google.golang.org/grpc/metadata"
10    "google.golang.org/grpc/status"
11    "google.golang.org/grpc/codes"
12
13    "golang.org/x/time/rate"
14)
15
16// rateLimiter menyimpan limiter per client (misal: per API key atau IP)
17var rateLimiter = struct {
18    sync.RWMutex
19    clients map[string]*rate.Limiter
20}{clients: make(map[string]*rate.Limiter)}
21
22func getClientLimiter(clientID string) *rate.Limiter {
23    rateLimiter.RLock()
24    limiter, exists := rateLimiter.clients[clientID]
25    rateLimiter.RUnlock()
26
27    if !exists {
28        limiter = rate.NewLimiter(5, 10) // 5 req/s, burst max 10
29        rateLimiter.Lock()
30        rateLimiter.clients[clientID] = limiter
31        rateLimiter.Unlock()
32    }
33    return limiter
34}
35
36func RateLimitInterceptor() grpc.UnaryServerInterceptor {
37    return func(
38        ctx context.Context,
39        req interface{},
40        info *grpc.UnaryServerInfo,
41        handler grpc.UnaryHandler,
42    ) (interface{}, error) {
43        md, ok := metadata.FromIncomingContext(ctx)
44        if !ok {
45            return nil, status.Errorf(codes.Unauthenticated, "metadata not found")
46        }
47
48        clientID := "anonymous"
49        if vals := md.Get("x-api-key"); len(vals) > 0 {
50            clientID = vals[0]
51        }
52
53        limiter := getClientLimiter(clientID)
54
55        if !limiter.Allow() {
56            return nil, status.Errorf(codes.ResourceExhausted, "rate limit exceeded")
57        }
58
59        return handler(ctx, req)
60    }
61}

Integrasi dengan Server gRPC

go
1s := grpc.NewServer(
2    grpc.UnaryInterceptor(RateLimitInterceptor()),
3)
4pb.RegisterMyServiceServer(s, &MyService{})

Tabel Simulasi Client

Client IDBatas RequestBurst MaxStatus AwalStatus Setelah 10x Request
client-a5 req/s10✅ Allowed❌ Blocked (Rate Limit)
client-b5 req/s10✅ Allowed✅ Allowed (karena jeda 200ms)

Grafik Simulasi Waktu vs Status Permintaan

MERMAID
gantt
title Simulasi Permintaan Client A (Limit 5 req/s, burst 10)
dateFormat  X
section Status
Request ke-1    :done, 1, 1s
Request ke-2    :done, 2, 1s
Request ke-3    :done, 3, 1s
Request ke-4    :done, 4, 1s
Request ke-5    :done, 5, 1s
Request ke-6    :done, 6, 1s
Request ke-7    :done, 7, 1s
Request ke-8    :done, 8, 1s
Request ke-9    :done, 9, 1s
Request ke-10   :done, 10, 1s
Request ke-11   :crit, 11, 1s
Request ke-12   :crit, 12, 1s

Praktik Terbaik

Gunakan metadata atau JWT claims sebagai key: API key, user ID, atau IP address. ✅ Gunakan burst size bijak: Batasi agar tidak terjadi spike. ✅ Reset map limiter secara periodik: Hindari memory leak dari map yang terus tumbuh. ✅ Logging & metrics: Tambahkan log untuk request yang ditolak karena limit, serta expose metrics (Prometheus).


Bonus: Visual Alur Permintaan

MERMAID
flowchart TD
  Client -->|Request + Metadata| Interceptor
  Interceptor -->|"Check Allow()"| Handler
  Interceptor -->|Reject if Exceeded| ErrorResponse

Studi Kasus: Limit Berdasarkan Role

Jika kamu punya JWT atau metadata yang mengandung role user, kamu bisa membuat rate limit berbeda:

go
1if role == "free" {
2    limiter = rate.NewLimiter(1, 3)
3} else if role == "premium" {
4    limiter = rate.NewLimiter(10, 20)
5}

Dengan begitu, kamu bisa mengatur tingkatan layanan (tiered access) sesuai role atau subscription.


Kesimpulan

Rate limiting adalah lapisan penting dalam membangun API yang aman, adil, dan stabil. Dengan menempatkannya di interceptor gRPC, kamu memastikan bahwa validasi trafik terjadi di entry point sistem.

Gunakan pendekatan ini untuk:

  • 🔐 API publik
  • 🧩 Internal microservice antar tim
  • 🧰 Gateway gRPC sebagai agregator

Artikel Terkait

💬 Komentar