Round Robin #
Round robin adalah algoritma load balancing paling klasik dan paling mudah dipahami: setiap request baru diarahkan ke upstream berikutnya dalam urutan melingkar. Request pertama ke upstream-1, kedua ke upstream-2, ketiga ke upstream-3, keempat kembali ke upstream-1, dan seterusnya.
Kesederhanaan inilah yang membuat round robin menjadi pilihan default di hampir semua load balancer termasuk Caddy — ia mudah diprediksi, mudah di-debug, dan bekerja sangat baik untuk mayoritas use case.
Cara Kerja Round Robin #
Caddy menerima 9 request berturut-turut:
Request 1 → backend-1:3000
Request 2 → backend-2:3000
Request 3 → backend-3:3000
Request 4 → backend-1:3000 (kembali ke awal)
Request 5 → backend-2:3000
Request 6 → backend-3:3000
Request 7 → backend-1:3000
Request 8 → backend-2:3000
Request 9 → backend-3:3000
Setiap backend mendapatkan jumlah request yang sama:
backend-1: 3 request (33%)
backend-2: 3 request (33%)
backend-3: 3 request (33%)
Distribusi ini dijamin merata dalam hal jumlah request — bukan dalam hal beban aktual, karena setiap request bisa memiliki durasi dan berat komputasi yang berbeda.
Konfigurasi Round Robin #
example.com {
# Round robin adalah default — tidak perlu menulis lb_policy
reverse_proxy backend-1:3000 backend-2:3000 backend-3:3000
}
# Penulisan eksplisit (sama hasilnya)
example.com {
reverse_proxy backend-1:3000 backend-2:3000 backend-3:3000 {
lb_policy round_robin
}
}
# Dengan health check (direkomendasikan untuk production)
example.com {
reverse_proxy backend-1:3000 backend-2:3000 backend-3:3000 {
lb_policy round_robin
health_uri /health
health_interval 10s
health_timeout 5s
}
}
Round Robin dengan Docker Compose #
Pola yang sangat umum: beberapa container aplikasi di belakang Caddy:
# docker-compose.yml
services:
caddy:
image: caddy:2.8.4
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
networks:
- app-network
app-1:
image: myapp:latest
networks:
- app-network
environment:
- NODE_ENV=production
- INSTANCE=1
app-2:
image: myapp:latest
networks:
- app-network
environment:
- NODE_ENV=production
- INSTANCE=2
app-3:
image: myapp:latest
networks:
- app-network
environment:
- NODE_ENV=production
- INSTANCE=3
networks:
app-network:
driver: bridge
volumes:
caddy_data:
# Caddyfile
example.com {
reverse_proxy app-1:3000 app-2:3000 app-3:3000 {
lb_policy round_robin
health_uri /health
health_interval 15s
health_timeout 5s
}
}
Round Robin dengan Scaling Dinamis #
Dengan Admin API Caddy, kamu bisa menambah atau mengurangi upstream tanpa restart:
# Lihat konfigurasi upstream saat ini
curl -s http://localhost:2019/config/apps/http/servers/srv0/routes \
| jq '.[0].handle[0].routes[0].handle[0].upstreams'
# Tambah upstream baru secara dinamis
curl -X POST http://localhost:2019/config/apps/http/servers/srv0/routes/0/handle/0/routes/0/handle/0/upstreams \
-H "Content-Type: application/json" \
-d '{"dial": "app-4:3000"}'
# Reload config dari file (cara lebih mudah)
sudo systemctl reload caddy
# atau
caddy reload --config /etc/caddy/Caddyfile
Round Robin vs Algoritma Lain: Kapan Memilih Round Robin #
Gunakan Round Robin ketika:
✓ Semua backend memiliki spesifikasi hardware yang sama
✓ Request bersifat homogen — durasi pemrosesan mirip satu sama lain
✓ Aplikasi sepenuhnya stateless (tidak menyimpan state di memori)
✓ Kamu ingin distribusi yang paling mudah diprediksi dan di-debug
✓ Beban per request relatif seragam (misal: API CRUD sederhana)
Pertimbangkan algoritma lain ketika:
✗ Backend memiliki kapasitas berbeda (gunakan weighted)
✗ Durasi request sangat bervariasi (gunakan least_conn)
✗ Aplikasi menyimpan state session di memori (gunakan ip_hash atau cookie)
✗ Kamu ingin cache consistency untuk URI yang sama (gunakan uri_hash)
Simulasi: Perbandingan Round Robin vs Least Connections #
Skenario: 6 request masuk, durasi pemrosesan bervariasi
Backend-1 memproses cepat (50ms per request)
Backend-2 memproses lambat (500ms per request)
Round Robin:
T=0ms Req-1 → backend-1 (50ms)
T=0ms Req-2 → backend-2 (500ms)
T=0ms Req-3 → backend-1 (50ms) backend-1 sudah selesai Req-1
T=0ms Req-4 → backend-2 (500ms) backend-2 MASIH proses Req-2!
T=0ms Req-5 → backend-1 (50ms) backend-1 sudah selesai Req-3
T=0ms Req-6 → backend-2 (500ms) backend-2 MASIH proses Req-2 & 4!
Hasil: backend-2 terbebani dengan antrian, backend-1 idle
Least Connections:
T=0ms Req-1 → backend-1 (50ms) [backend-1: 1 conn, backend-2: 0 conn]
T=0ms Req-2 → backend-2 (500ms) [backend-1: 1 conn, backend-2: 1 conn]
T=50ms Req-3 → backend-1 (50ms) [backend-1: 0 conn → pilih ini]
T=100ms Req-4 → backend-1 (50ms) [backend-1: 0 conn → pilih ini]
T=150ms Req-5 → backend-1 (50ms) [backend-1: 0 conn → pilih ini]
T=200ms Req-6 → backend-1 (50ms) [backend-1: 0 conn → pilih ini]
Hasil: backend-1 digunakan lebih optimal karena lebih cepat
Ini menunjukkan bahwa round robin bisa menyebabkan ketidakseimbangan ketika ada perbedaan kecepatan antar backend. Untuk kasus seperti ini, least_conn lebih optimal.
Health Check dengan Round Robin #
Round robin secara default akan terus mengirim request ke backend yang sedang down sampai kamu mengkonfigurasi health check:
example.com {
reverse_proxy backend-1:3000 backend-2:3000 backend-3:3000 {
lb_policy round_robin
# ── Active Health Check ──────────────────────────────────
# Caddy proaktif cek setiap backend setiap 10 detik
health_uri /health # Endpoint khusus untuk health check
health_interval 10s # Seberapa sering cek
health_timeout 5s # Timeout per cek
health_status 200 # Status code yang dianggap sehat
# ── Passive Health Check ─────────────────────────────────
# Caddy amati error dari request nyata
health_fails 3 # Unhealthy setelah 3 gagal berturut
health_passes 2 # Pulihkan setelah 2 sukses berturut
}
}
Dengan health check, jika backend-2 down:
Request 1 → backend-1:3000 ✓
Request 2 → backend-2:3000 ✗ (gagal — health check catat)
Request 3 → backend-3:3000 ✓
Request 4 → backend-1:3000 ✓
Request 5 → backend-2:3000 ✗ (gagal lagi — health check catat)
Request 6 → backend-3:3000 ✓
[backend-2 gagal 3 kali → tandai unhealthy]
Request 7 → backend-1:3000 ✓ (backend-2 di-skip)
Request 8 → backend-3:3000 ✓ (backend-2 di-skip)
Request 9 → backend-1:3000 ✓ (backend-2 di-skip)
[health check aktif terus cek backend-2 setiap 10s]
[backend-2 pulih → tandai healthy kembali]
Request 10 → backend-2:3000 ✓ (backend-2 kembali dalam rotasi)
Endpoint /health yang Baik
#
Backend harus menyediakan endpoint /health yang melakukan pengecekan yang berarti:
// Contoh Node.js/Express — health endpoint yang baik
app.get('/health', async (req, res) => {
const checks = {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
checks: {}
};
try {
// Cek koneksi database
await db.query('SELECT 1');
checks.checks.database = 'ok';
} catch (err) {
checks.checks.database = 'error';
checks.status = 'degraded';
}
try {
// Cek koneksi Redis
await redis.ping();
checks.checks.redis = 'ok';
} catch (err) {
checks.checks.redis = 'error';
// Redis down tidak selalu critical
}
const statusCode = checks.status === 'ok' ? 200 : 503;
res.status(statusCode).json(checks);
});
Monitoring Round Robin via Admin API #
# Lihat status semua upstream
curl -s http://localhost:2019/reverse_proxy/upstreams/ | jq .
# Output contoh:
# [
# {
# "address": "backend-1:3000",
# "healthy": true,
# "num_requests": 4521,
# "fails": 0
# },
# {
# "address": "backend-2:3000",
# "healthy": false, ← Ini unhealthy!
# "num_requests": 4498,
# "fails": 3
# },
# {
# "address": "backend-3:3000",
# "healthy": true,
# "num_requests": 4539,
# "fails": 1
# }
# ]
# Cek distribusi request dari access log
cat /var/log/caddy/access.log | jq -r '.upstream_latency' | \
awk '{sum+=$1; count++} END {print "avg:", sum/count, "ms"}'
Ringkasan #
- Round robin adalah algoritma default Caddy — setiap request diarahkan ke upstream berikutnya secara bergiliran.
- Round robin bekerja paling baik ketika semua backend homogen (spesifikasi sama) dan request bersifat stateless dengan durasi pemrosesan yang seragam.
- Selalu tambahkan
health_uridanhealth_intervaldi production — tanpanya round robin akan terus kirim request ke backend yang sudah down.- Gunakan
least_connjika durasi request sangat bervariasi — round robin bisa menyebabkan beberapa backend terbebani sementara yang lain idle.- Untuk monitoring distribusi traffic, gunakan Admin API (
/reverse_proxy/upstreams/) untuk melihat jumlah request dan status health per upstream secara real-time.