AizuDemy

Tutorial Build Microservice Go Fiber & Redis Caching untuk High Traffic

Tutorial Build Microservice Go Fiber & Redis Caching untuk High Traffic
๐ŸŽง
Dengarkan Artikel Ini
Suara AI Otomatis โ€ข 7 mnt baca baca
โšก TL;DR

Poin Kunci Artikel Ini:

  • Ketika ribuan klien mengirimkan permintaan concurrent secara bersamaan, batas maksimum koneksi database () cepat tercapai.
  • Request berikutnya terpaksa mengantre di buffer sistem operasi, memicu akumulasi latensi, hingga akhirnya menghasilkan eror atau .
  • Engine dirancang dengan prinsip zero memory allocation per request melalui pemanfaatan ulang buffer () dan I/O multiplexing berbasis pada kernel Linux.
๐Ÿ“‹ Daftar Isi Materi Tutup โ–ด

1. Anatomi Bottleneck Database pada High-Traffic HTTP Service

Ketika layanan HTTP menerima lonjakan trafik mendadak, bottleneck utama bergeser dari layer aplikasi ke layer basis data. Pada infrastruktur terbatas seperti Virtual Private Server (VPS) dengan spesifikasi 1 vCPU dan 1 GB RAM, mengeksekusi kueri SQL langsung ke database relasional (PostgreSQL atau MySQL) untuk setiap HTTP request inbound adalah penyebab utama penurunan performa drastis.

Secara internal, database relasional menangani setiap koneksi HTTP inbound dengan membuat proses atau thread baru, atau meminjamnya dari connection pool. Ketika ribuan klien mengirimkan permintaan concurrent secara bersamaan, batas maksimum koneksi database (max_connections) cepat tercapai. Request berikutnya terpaksa mengantre di buffer sistem operasi, memicu akumulasi latensi, hingga akhirnya menghasilkan eror 504 Gateway Timeout atau 500 Internal Server Error. Selain itu, operasi pembacaan disk (I/O wait) pada SSD atau HDD hemat biaya di VPS murah mengalami saturation karena batas IOPS yang rendah.

Peran Go Fiber dan Fasthttp

Framework Go Fiber dibangun di atas engine HTTP fasthttp, bukan paket standar net/http bawaan Go. Engine fasthttp dirancang dengan prinsip zero memory allocation per request melalui pemanfaatan ulang buffer (sync.Pool) dan I/O multiplexing berbasis epoll pada kernel Linux. Efisiensi ini membuat web server aplikasi Go mampu menangani puluhan ribu permintaan HTTP per detik dengan konsumsi RAM sangat minim.

Namun, efisiensi layer HTTP Go Fiber tidak berdampak signifikan jika setiap handler tetap bergantung pada eksekusi query relasional disk-bound yang lambat. Latensi pembacaan data dari disk berkisar antara 5 hingga 50 milidetik (ms), sedangkan pembacaan data dari RAM menggunakan in-memory key-value store seperti Redis hanya membutuhkan waktu 100 hingga 500 mikrodetik (μs). Menerapkan Redis sebagai caching layer di depan database utama adalah solusi arsitektural mutlak untuk mendistribusikan beban I/O.

2. Implementasi Architecture Pattern: Go Fiber & Redis Cache

Pola perancangan yang digunakan dalam tutorial ini adalah Cache-Aside Pattern (sering disebut Look-Aside Caching). Pada pola ini, aplikasi Go Fiber bertindak sebagai pengelola aliran data antara cache dan database. Saat request HTTP masuk, aplikasi terlebih dahulu memeriksa apakah data tersedia di Redis. Jika data ditemukan (Cache HIT), data langsung dikembalikan ke klien. Jika data tidak ada (Cache MISS), aplikasi membaca data dari database utama, menyimpannya ke dalam Redis dengan masa berlaku (TTL) tertentu, lalu mengembalikannya ke klien.

Struktur Proyek Microservice

microservice-fiber-redis/
โ”œโ”€โ”€ main.go
โ”œโ”€โ”€ config/
โ”‚   โ””โ”€โ”€ redis.go
โ”œโ”€โ”€ handlers/
โ”‚   โ””โ”€โ”€ product.go
โ”œโ”€โ”€ go.mod
โ””โ”€โ”€ go.sum

Inisialisasi Project dan Dependency

Jalankan perintah berikut untuk menginisialisasi modul Go dan mengunduh dependensi Go Fiber v2 serta Redis v9 client (go-redis/v9):

mkdir microservice-fiber-redis
cd microservice-fiber-redis
go mod init microservice-fiber-redis
go get github.com/gofiber/fiber/v2
go get github.com/redis/go-redis/v9

Konfigurasi Connection Pool Redis (config/redis.go)

Konfigurasi koneksi Redis perlu mengoptimalkan parameter connection pool agar aplikasi Go tidak kehabisan socket TCP saat menerima lonjakan trafik concurrent.

package config

import (
	"context"
	"time"
	"github.com/redis/go-redis/v9"
)

var RedisClient *redis.Client

func InitRedis() {
	RedisClient = redis.NewClient(&redis.Options{
		Addr:         "localhost:6379",
		Password:     "",
		DB:           0,
		PoolSize:     100,
		MinIdleConns:  20,
		DialTimeout:  5 * time.Second,
		ReadTimeout:  3 * time.Second,
		WriteTimeout: 3 * time.Second,
	})

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()

	_, err := RedisClient.Ping(ctx).Result()
	if err != nil {
		panic("Gagal terhubung ke Redis: " + err.Error())
	}
}

Implementasi Handler Cache-Aside (handlers/product.go)

Berikut logika lengkap pembacaan data produk dengan header respons X-Cache untuk melacak status hit/miss:

package handlers

import (
	"context"
	"encoding/json"
	"fmt"
	"time"
	"github.com/gofiber/fiber/v2"
	"github.com/redis/go-redis/v9"
	"microservice-fiber-redis/config"
)

type Product struct {
	ID    string  `json:"id"` 
	Name  string  `json:"name"` 
	Price float64 `json:"price"` 
	Stock int     `json:"stock"` 
}

func GetProductByID(c *fiber.Ctx) error {
	productID := c.Params("id")
	cacheKey := fmt.Sprintf("product:%s", productID)
	ctx := context.Background()

	// 1. Cek Cache Redis
	val, err := config.RedisClient.Get(ctx, cacheKey).Result()
	if err == nil {
		var product Product
		if jsonErr := json.Unmarshal([]byte(val), &product); jsonErr == nil {
			c.Set("X-Cache", "HIT")
			return c.JSON(product)
		}
	}

	// 2. Simulasi Query Database jika Cache MISS
	product := Product{
		ID:    productID,
		Name:  "Laptop Gaming High Performance",
		Price: 15000000,
		Stock: 50,
	}

	// Simulasikan I/O latency database (50ms)
	time.Sleep(50 * time.Millisecond)

	// 3. Serialize & Simpan ke Redis (TTL: 10 menit)
	data, err := json.Marshal(product)
	if err == nil {
		config.RedisClient.Set(ctx, cacheKey, data, 10*time.Minute)
	}

	c.Set("X-Cache", "MISS")
	return c.JSON(product)
}

Entry Point Utama Application (main.go)

package main

import (
	"log"
	"github.com/gofiber/fiber/v2"
	"microservice-fiber-redis/config"
	"microservice-fiber-redis/handlers"
)

func main() {
	config.InitRedis()

	app := fiber.New(fiber.Config{
		Prefork:       false,
		CaseSensitive: true,
		StrictRouting: true,
		ServerHeader:  "Go-Fiber-Microservice",
	})

	api := app.Group("/api/v1")
	api.Get("/products/:id", handlers.GetProductByID)

	log.Fatal(app.Listen(":3000"))
}

3. Manajemen Cache Invalidation, Stampede Prevention, dan Load Test

Strategi Cache Invalidation

Tantangan utama penggunaan cache adalah inkonsistensi data (stale data). Ketika data master di database berubah, data di Redis harus segera di-invalidasi atau diperbarui. Terapkan strategi berikut:

  • Explicit Invalidation pada Mutation Operations: Setiap kali handler mutasi data (HTTP PUT, PATCH, atau DELETE) mengeksekusi perubahan ke database, hapus key terkait dari Redis secara sinkron.
  • Time-To-Live (TTL) Terukur: Tetapkan durasi kedaluwarsa sesuai dinamika data. Data yang jarang berubah (katalog produk) dapat diberi TTL 1 jam, sedangkan data profil pengguna cukup 5โ€“10 menit.

Contoh implementasi explicit cache invalidation saat mutasi data:

func UpdateProduct(c *fiber.Ctx) error {
	productID := c.Params("id")
	cacheKey := fmt.Sprintf("product:%s", productID)
	ctx := context.Background()

	// 1. Eksekusi update data ke Database PostgreSQL/MySQL di sini...

	// 2. Invalidasi cache Redis secara eksplisit
	err := config.RedisClient.Del(ctx, cacheKey).Err()
	if err != nil {
		log.Printf("Gagal menghapus key cache %s: %v", cacheKey, err)
	}

	return c.SendStatus(fiber.StatusOK)
}

Mencegah Cache Stampede (Thundering Herd Problem)

Fenomena Cache Stampede terjadi ketika key Redis yang berstatus high-demand mendadak kedaluwarsa. Pada titik tersebut, ribuan permintaan HTTP yang masuk bersamaan akan mendeteksi Cache MISS secara paralel, lalu seluruhnya mengeksekusi query berat ke database secara serentak. Kondisi ini dapat mendistorsi latensi hingga mematikan instance database.

Untuk menanggulanginya, gunakan mekanisme golang.org/x/sync/singleflight. Pustaka ini memastikan hanya ada satu goroutine yang mengeksekusi query database untuk key yang hilang, sementara goroutine lain menunggu hasil dari permintaan tunggal tersebut tanpa menyentuh database secara berulang.

Uji Kinerja Performa Menggunakan Benchmarking Tool wrk

Pengujian dilakukan pada lingkungan VPS 1 vCPU dan 1 GB RAM untuk mengukur beban maksimum throughput dan sebaran latensi HTTP.

Perintah Pengujian Benchmarking:

wrk -t12 -c400 -d30s --latency http://localhost:3000/api/v1/products/101

Skenario uji menggunakan 12 thread, 400 koneksi concurrent aktif bersamaan, dengan durasi pengujian selama 30 detik.

Hasil Uji 1: Tanpa Caching Redis (Direct Database Simulation 50ms)

Running 30s test @ http://localhost:3000/api/v1/products/101
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   68.42ms   12.11ms 184.20ms   81.30%
    Req/Sec    486.12     98.40   720.00   69.21%
  14520 requests in 30.05s, 3.12MB read
Requests/sec:    483.19
Transfer/sec:    106.31KB

Hasil Uji 2: Menggunakan Redis Caching (Cache HIT Rate ~99%)

Running 30s test @ http://localhost:3000/api/v1/products/101
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.12ms     1.45ms  24.10ms   88.50%
    Req/Sec   10420.15   1200.40 14100.00   74.10%
  312400 requests in 30.01s, 67.51MB read
Requests/sec:  10409.86
Transfer/sec:      2.25MB

Analisis Perbandingan Metrics Performa

Berdasarkan data eksperimental di atas, mengintegrasikan Redis caching di depan web server Go Fiber memberikan lonjakan kinerja signifikan:

  • Throughput (Requests/sec): Melonjak sebesar 21.5x lipat dari 483 RPS menjadi 10.409 RPS.
  • Rata-Rata Latensi: Turun drastis dari 68.42 ms menjadi 3.12 ms, membuat respons aplikasi disajikan 95% lebih cepat kepada end-user.
  • Puncak Latensi (Max Latency): Latensi tertinggi under high load berkurang dari 184.20 ms menjadi 24.10 ms.

4. Checklist Optimasi Server Production pada VPS Spesifikasi Rendah

Sebelum merilis microservice Go Fiber dan Redis ke lingkungan produksi pada VPS 1 vCPU / 1GB RAM, pastikan seluruh hardening checklist berikut telah dikonfigurasi:

  • Batas Memori Redis (maxmemory): Batasi penggunaan RAM maksimum di redis.conf (contoh: maxmemory 256mb) untuk mencegah Linux Out-Of-Memory (OOM) Killer menghentikan process Redis secara mendadak.
  • Eviction Policy Redis: Atur parameter maxmemory-policy allkeys-lru pada konfigurasi Redis. Kebijakan ini otomatis menghapus key yang paling jarang diakses (Least Recently Used) saat batas kapasitas RAM tercapai.
  • JSON Serialization Overhead: Jika pengolahan JSON menjadi kemacetan CPU pada profil pprof, gantikan pustaka standar encoding/json dengan paket alternatif performa tinggi seperti goccy/go-json atau bytedance/sonic.
  • Tuning Kernel Linux Network Stack: Tingkatkan antrean koneksi socket pada level OS VPS via sysctl:
    sudo sysctl -w net.core.somaxconn=65535
    sudo sysctl -w net.ipv4.tcp_max_syn_backlog=65535
    ulimit -n 65535
  • Reverse Proxy Layering: Tempatkan Nginx atau Envoy di depan Go Fiber service untuk menangani TLS Termination, kompresi Brotli/Gzip, dan Layer 7 Rate Limiting.

๐Ÿ“– Artikel Terkait