Inti Masalah (TL;DR)
Terapkan pembatasan laju API menggunakan algoritma token bucket atau sliding window. Kembalikan header batas laju IETF standar (RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset) dan kode status 429 Too Many Requests ketika batas terlampaui. Modern PetstoreAPI mengimplementasikan pembatasan laju dengan kuota per pengguna dan respons kesalahan yang jelas.
Pendahuluan
Seorang klien membuat 10.000 permintaan ke API Anda dalam satu menit. Basis data Anda macet. Peringatan pemantauan Anda berbunyi. Pelanggan Anda yang lain tidak dapat mengakses API. Anda diserang—atau mungkin hanya berurusan dengan klien yang mengalami bug dalam perulangan percobaan ulang.
Pembatasan laju mencegah hal ini. Ini membatasi berapa banyak permintaan yang dapat dibuat klien dalam jendela waktu. Ketika mereka melebihi batas, Anda mengembalikan 429 Too Many Requests. Klien mundur, dan API Anda tetap sehat.
Swagger Petstore lama tidak menerapkan pembatasan laju sama sekali. Modern PetstoreAPI mengimplementasikan pembatasan laju dengan header IETF standar, kuota per pengguna, dan respons kesalahan yang jelas.
Dalam panduan ini, Anda akan mempelajari algoritma pembatasan laju, header standar, dan bagaimana Modern PetstoreAPI mengimplementasikan pembatasan laju dengan benar.
Mengapa API Membutuhkan Pembatasan Laju
Pembatasan laju melindungi API Anda dari penyalahgunaan dan memastikan penggunaan yang adil.
Perlindungan Terhadap Penyalahgunaan
1. Serangan Denial-of-Service (DoS)
Penyerang membanjiri API Anda dengan permintaan untuk membuatnya tidak tersedia. Pembatasan laju membatasi dampaknya.
2. Credential stuffing
Penyerang mencoba ribuan kombinasi nama pengguna/kata sandi. Pembatasan laju memperlambat mereka.
3. Data scraping
Bot mengikis seluruh kumpulan data Anda. Pembatasan laju membuat scraping tidak praktis.
4. Kontrol biaya
Jika API Anda memanggil layanan yang mahal (model AI, API pihak ketiga), pembatasan laju mencegah biaya yang tidak terkendali.
Penggunaan yang Adil
1. Mencegah satu klien memonopoli sumber daya
Tanpa pembatasan laju, satu klien yang membuat 1000 permintaan/detik dapat menyebabkan kelangkaan bagi klien lain.
2. Performa yang dapat diprediksi
Pembatasan laju memastikan waktu respons yang konsisten untuk semua klien.
3. Akses berjenjang
Tingkat gratis: 100 permintaan/jam. Tingkat berbayar: 10.000 permintaan/jam. Pembatasan laju memberlakukan tingkat-tingkat ini.
Manfaat Operasional
1. Perencanaan kapasitas
Anda tahu beban maksimum yang akan ditangani API Anda.
2. Prediktabilitas biaya
Batas laju membatasi biaya infrastruktur.
3. Degradasi yang anggun
Di bawah beban, pembatasan laju mencegah kegagalan berantai.
Algoritma Pembatasan Laju
Algoritma yang berbeda memiliki trade-off yang berbeda.
1. Fixed Window (Jendela Tetap)
Menghitung permintaan dalam jendela waktu tetap.
Cara kerjanya:
Jendela 1 (00:00-00:59): 100 permintaan diizinkan
Jendela 2 (01:00-01:59): 100 permintaan diizinkan
Implementasi:
def is_allowed(user_id):
current_minute = get_current_minute()
key = f"rate_limit:{user_id}:{current_minute}"
count = redis.incr(key)
redis.expire(key, 60)
return count <= 100
Kelebihan:
- Mudah diimplementasikan
- Penggunaan memori rendah
Kekurangan:
- Masalah burst: Klien dapat membuat 100 permintaan pada 00:59 dan 100 pada 01:00 (200 dalam 2 detik)
2. Sliding Window (Jendela Geser)
Menghitung permintaan dalam jendela waktu yang bergerak.
Cara kerjanya:
Pada 01:30, hitung permintaan dari 00:30 hingga 01:30 (60 menit terakhir).
Implementasi:
def is_allowed(user_id):
now = time.time()
window_start = now - 3600 # 1 jam yang lalu
key = f"rate_limit:{user_id}"
# Hapus permintaan lama
redis.zremrangebyscore(key, 0, window_start)
# Hitung permintaan dalam jendela
count = redis.zcard(key)
if count < 100:
redis.zadd(key, {now: now})
redis.expire(key, 3600)
return True
return False
Kelebihan:
- Tidak ada masalah burst
- Pembatasan laju yang akurat
Kekurangan:
- Penggunaan memori lebih tinggi (menyimpan timestamp untuk setiap permintaan)
- Lebih kompleks
3. Token Bucket (Keranjang Token)
Token ditambahkan ke keranjang dengan laju tetap. Setiap permintaan mengonsumsi satu token.
Cara kerjanya:
Kapasitas keranjang: 100 token
Laju pengisian ulang: 10 token/detik
Permintaan: mengonsumsi 1 token
Implementasi:
def is_allowed(user_id):
now = time.time()
key = f"rate_limit:{user_id}"
# Dapatkan status saat ini
data = redis.hgetall(key)
tokens = float(data.get('tokens', 100))
last_refill = float(data.get('last_refill', now))
# Isi ulang token
elapsed = now - last_refill
tokens = min(100, tokens + elapsed * 10) # 10 token/detik
if tokens >= 1:
tokens -= 1
redis.hset(key, 'tokens', tokens)
redis.hset(key, 'last_refill', now)
redis.expire(key, 3600)
return True
return False
Kelebihan:
- Mengizinkan burst (hingga kapasitas keranjang)
- Pembatasan laju yang mulus
- Standar industri
Kekurangan:
- Lebih kompleks daripada fixed window
- Membutuhkan penyimpanan status
4. Leaky Bucket (Keranjang Bocor)
Permintaan ditambahkan ke antrean dan diproses dengan laju tetap.
Cara kerjanya:
Kapasitas antrean: 100 permintaan
Laju proses: 10 permintaan/detik
Kelebihan:
- Laju keluaran yang mulus
- Baik untuk melindungi layanan hilir
Kekurangan:
- Menambah latensi (permintaan menunggu dalam antrean)
- Kompleks untuk diimplementasikan
Algoritma Mana yang Digunakan?
Untuk sebagian besar API: Token Bucket
Ini adalah standar industri, memungkinkan burst yang wajar, dan menyediakan pembatasan laju yang mulus.
Modern PetstoreAPI menggunakan token bucket dengan kuota per pengguna.
Header Batas Laju Standar
Gunakan header standar IETF (draft-ietf-httpapi-ratelimit-headers).
Header Standar
RateLimit-Limit: Jumlah permintaan maksimum yang diizinkan dalam jendela waktu
RateLimit-Limit: 100
RateLimit-Remaining: Permintaan yang tersisa dalam jendela saat ini
RateLimit-Remaining: 45
RateLimit-Reset: Detik hingga batas laju diatur ulang
RateLimit-Reset: 3600
Contoh Respons
GET /pets
200 OK
RateLimit-Limit: 100
RateLimit-Remaining: 99
RateLimit-Reset: 3600
{
"data": [...]
}
Header Lama (Usang)
Banyak API menggunakan header non-standar:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
X-RateLimit-Reset: 1710331200
Jangan gunakan ini. Prefiks X- sudah usang, dan formatnya tidak distandarisasi.
Bagaimana Modern PetstoreAPI Mengimplementasikan Pembatasan Laju
Modern PetstoreAPI mengimplementasikan pembatasan laju token bucket dengan header standar.
Batas Laju Berdasarkan Tingkat
Tingkat gratis:
- 100 permintaan/jam
- 1.000 permintaan/hari
Tingkat Pro:
- 10.000 permintaan/jam
- 100.000 permintaan/hari
Tingkat Enterprise:
- Batas khusus
Implementasi
Permintaan berhasil:
GET /v1/pets
200 OK
RateLimit-Limit: 100
RateLimit-Remaining: 99
RateLimit-Reset: 3540
{
"data": [...]
}
Batas laju terlampaui:
GET /v1/pets
429 Terlalu Banyak Permintaan
Content-Type: application/problem+json
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 120
Retry-After: 120
{
"type": "https://petstoreapi.com/errors/rate-limit-exceeded",
"title": "Batas Laju Terlampaui",
"status": 429,
"detail": "Anda telah melampaui batas laju 100 permintaan per jam",
"instance": "/v1/pets",
"retryAfter": 120,
"limit": 100,
"window": "1j"
}
Per-Pengguna vs Per-IP
Per-pengguna (permintaan terautentikasi):
Batas laju berdasarkan ID pengguna atau kunci API. Lebih akurat dan adil.
user_id = get_authenticated_user()
is_allowed(user_id)
Per-IP (permintaan tidak terautentikasi):
Batas laju berdasarkan alamat IP. Kurang akurat (IP bersama, VPN) tetapi lebih baik daripada tidak sama sekali.
ip_address = request.remote_addr
is_allowed(ip_address)
Modern PetstoreAPI menggunakan pembatasan laju per-pengguna untuk permintaan terautentikasi dan per-IP untuk endpoint publik.
Format Respons Batas Laju
Ketika batas laju terlampaui, kembalikan 429 dengan format kesalahan RFC 9457.
Struktur Respons
{
"type": "https://petstoreapi.com/errors/rate-limit-exceeded",
"title": "Batas Laju Terlampaui",
"status": 429,
"detail": "Anda telah melampaui batas laju Anda. Silakan coba lagi nanti.",
"instance": "/v1/pets",
"retryAfter": 120,
"limit": 100,
"remaining": 0,
"reset": 120,
"window": "1j"
}
Header
429 Terlalu Banyak Permintaan
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 120
Retry-After: 120
Retry-After: Memberi tahu klien kapan harus mencoba lagi (dalam detik).
Menguji Batas Laju dengan Apidog
Apidog membantu Anda menguji perilaku pembatasan laju.
Skenario Uji
1. Penggunaan normal:
Kirim 50 permintaan → Semua berhasil
Periksa RateLimit-Remaining berkurang
2. Melebihi batas:
Kirim 101 permintaan → yang ke-101 mengembalikan 429
Verifikasi format respons kesalahan
Periksa header Retry-After
3. Perilaku reset:
Melebihi batas → Tunggu reset → Verifikasi batas dipulihkan
4. Tingkat yang berbeda:
Uji tingkat gratis (100/jam)
Uji tingkat pro (10.000/jam)
Verifikasi batas diberlakukan dengan benar
Contoh Uji Apidog
// Uji header batas laju
pm.test("Header batas laju ada", () => {
pm.response.to.have.header("RateLimit-Limit");
pm.response.to.have.header("RateLimit-Remaining");
pm.response.to.have.header("RateLimit-Reset");
});
// Uji batas laju terlampaui
pm.test("Mengembalikan 429 ketika batas terlampaui", () => {
// Buat 101 permintaan
for (let i = 0; i < 101; i++) {
pm.sendRequest("GET /v1/pets");
}
pm.response.to.have.status(429);
});
Praktik Terbaik Pembatasan Laju
1. Gunakan header standar
Gunakan header standar IETF, bukan header X- khusus.
2. Kembalikan 429, bukan 403
429 berarti "terlalu banyak permintaan." 403 berarti "terlarang." Jangan salah mengartikan keduanya.
3. Sertakan Retry-After
Beri tahu klien kapan mereka dapat mencoba lagi.
4. Dokumentasikan batas Anda
Buat batas laju terlihat dalam dokumentasi.
5. Sediakan tingkat yang berbeda
Tingkat gratis: batas rendah. Tingkat berbayar: batas lebih tinggi.
6. Batasi laju berdasarkan pengguna, bukan IP
Batas per pengguna lebih akurat dan adil.
7. Izinkan burst
Token bucket memungkinkan burst yang wajar tanpa menghukum penggunaan normal.
8. Pantau hit batas laju
Lacak seberapa sering klien mencapai batas laju. Tingkat tinggi menunjukkan masalah.
9. Sediakan endpoint status batas laju
GET /v1/rate-limit
200 OK
{
"limit": 100,
"remaining": 45,
"reset": 3540
}
10. Uji pembatasan laju
Gunakan Apidog untuk menguji perilaku batas laju sebelum deployment.
Kesimpulan
Pembatasan laju melindungi API Anda dari penyalahgunaan dan memastikan penggunaan yang adil. Gunakan algoritma token bucket dengan header standar IETF (RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset). Kembalikan 429 Too Many Requests dengan format kesalahan RFC 9457 ketika batas terlampaui.
Modern PetstoreAPI mengimplementasikan pembatasan laju dengan benar dengan kuota per pengguna, header standar, dan respons kesalahan yang jelas. Periksa dokumentasi untuk detail implementasi.
Uji pembatasan laju Anda dengan Apidog untuk memastikan bahwa itu berfungsi dengan benar di bawah beban dan menangani kasus tepi dengan tepat.
FAQ
Batas laju apa yang harus saya tetapkan?
Mulai dengan konservatif: 100 permintaan/jam untuk tingkat gratis, 10.000/jam untuk yang berbayar. Sesuaikan berdasarkan pola penggunaan dan kapasitas infrastruktur.
Haruskah saya membatasi laju berdasarkan IP atau pengguna?
Batasi laju berdasarkan pengguna (kunci API) untuk permintaan terautentikasi. Gunakan pembatasan laju berbasis IP hanya untuk endpoint publik.
Apa yang terjadi jika klien melebihi batas laju?
Kembalikan 429 Too Many Requests dengan header Retry-After. Jangan memblokir klien secara permanen—biarkan mereka mencoba lagi setelah jendela diatur ulang.
Bagaimana cara menangani batas laju untuk webhook?
Webhook adalah komunikasi server-ke-server, jadi batas laju harus lebih tinggi. Pertimbangkan batas terpisah untuk webhook vs panggilan API.
Haruskah saya membatasi laju layanan internal?
Ya, tetapi dengan batas yang jauh lebih tinggi. Pembatasan laju mencegah kegagalan berantai bahkan di sistem internal.
Bagaimana cara menguji pembatasan laju?
Gunakan Apidog untuk mengirim beberapa permintaan dan memverifikasi respons 429, header batas laju, dan perilaku reset.
Bagaimana jika API saya berada di belakang CDN?
Caching CDN mengurangi beban, tetapi Anda masih memerlukan pembatasan laju untuk cache misses dan permintaan POST/PUT/DELETE.
Bagaimana cara mengimplementasikan pembatasan laju di beberapa server?
Gunakan penyimpanan data bersama (Redis, Memcached) untuk melacak batas laju di semua server. Jangan gunakan memori lokal—itu tidak akan berfungsi dalam sistem terdistribusi.
