สรุปแบบรวบรัด
ใช้งานการจำกัดอัตราคำขอ API โดยใช้อัลกอริทึม Token Bucket หรือ Sliding Window ส่งคืนส่วนหัวการจำกัดอัตรามาตรฐานของ IETF (RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset) และสถานะ 429 Too Many Requests เมื่อเกินขีดจำกัด Modern PetstoreAPI ใช้การจำกัดอัตราคำขอด้วยโควต้าต่อผู้ใช้ และการตอบสนองข้อผิดพลาดที่ชัดเจน
บทนำ
ไคลเอ็นต์ส่งคำขอ 10,000 ครั้งไปยัง API ของคุณภายในหนึ่งนาที ฐานข้อมูลของคุณล่ม การแจ้งเตือนการตรวจสอบของคุณดังขึ้น ลูกค้ารายอื่นของคุณไม่สามารถเข้าถึง API ได้ คุณกำลังถูกโจมตี—หรืออาจเป็นเพียงการจัดการกับไคลเอ็นต์ที่มีข้อผิดพลาดในวงจรการลองใหม่ (retry loop)
การจำกัดอัตราคำขอช่วยป้องกันสิ่งนี้ มันจะจำกัดจำนวนคำขอที่ไคลเอ็นต์สามารถส่งได้ในช่วงเวลาหนึ่ง เมื่อพวกเขาเกินขีดจำกัด คุณจะส่งคืนสถานะ 429 Too Many Requests ไคลเอ็นต์จะถอยกลับ (backs off) และ API ของคุณยังคงทำงานได้ดี
Swagger Petstore แบบเก่าไม่ได้ใช้การจำกัดอัตราคำขอเลย แต่ Modern PetstoreAPI ใช้การจำกัดอัตราคำขอด้วยส่วนหัว IETF มาตรฐาน โควต้าต่อผู้ใช้ และการตอบสนองข้อผิดพลาดที่ชัดเจน
ในคู่มือนี้ คุณจะได้เรียนรู้อัลกอริทึมการจำกัดอัตราคำขอ ส่วนหัวมาตรฐาน และวิธีที่ Modern PetstoreAPI ใช้การจำกัดอัตราคำขออย่างถูกต้อง
ทำไม API ถึงต้องมีการจำกัดอัตราคำขอ
การจำกัดอัตราคำขอปกป้อง API ของคุณจากการใช้งานในทางที่ผิด และรับประกันการใช้งานที่เป็นธรรม
การป้องกันการใช้งานในทางที่ผิด
1. การโจมตีแบบปฏิเสธการให้บริการ (DoS)
ผู้โจมตีส่งคำขอจำนวนมากไปยัง API ของคุณเพื่อทำให้ไม่สามารถใช้งานได้ การจำกัดอัตราคำขอจะจำกัดผลกระทบของพวกเขา
2. การยัดเยียดข้อมูลประจำตัว (Credential Stuffing)
ผู้โจมตีพยายามใช้การรวมกันของชื่อผู้ใช้/รหัสผ่านนับพันครั้ง การจำกัดอัตราคำขอจะทำให้พวกเขาสามารถทำได้ช้าลง
3. การขูดข้อมูล (Data Scraping)
บอทขูดข้อมูลชุดข้อมูลทั้งหมดของคุณ การจำกัดอัตราคำขอทำให้การขูดข้อมูลทำได้ยาก
4. การควบคุมต้นทุน
หาก API ของคุณเรียกใช้บริการที่มีค่าใช้จ่ายสูง (โมเดล AI, API ของบุคคลที่สาม) การจำกัดอัตราคำขอจะช่วยป้องกันต้นทุนที่เพิ่มขึ้นอย่างควบคุมไม่ได้
การใช้งานที่เป็นธรรม
1. ป้องกันไม่ให้ไคลเอ็นต์รายเดียวผูกขาดทรัพยากร
หากไม่มีการจำกัดอัตราคำขอ ไคลเอ็นต์รายเดียวที่ส่งคำขอ 1,000 ครั้งต่อวินาที สามารถทำให้ไคลเอ็นต์รายอื่นไม่สามารถใช้งานได้
2. ประสิทธิภาพที่คาดการณ์ได้
การจำกัดอัตราคำขอช่วยให้มั่นใจได้ถึงเวลาตอบสนองที่สอดคล้องกันสำหรับไคลเอ็นต์ทั้งหมด
3. การเข้าถึงแบบแบ่งระดับ
ระดับฟรี: 100 คำขอ/ชั่วโมง ระดับชำระเงิน: 10,000 คำขอ/ชั่วโมง การจำกัดอัตราคำขอช่วยบังคับใช้ระดับเหล่านี้
ประโยชน์ในการปฏิบัติงาน
1. การวางแผนขีดความสามารถ
คุณรู้ถึงภาระสูงสุดที่ API ของคุณจะสามารถรองรับได้
2. การคาดการณ์ต้นทุน
ขีดจำกัดอัตราคำขอจำกัดต้นทุนโครงสร้างพื้นฐาน
3. การลดประสิทธิภาพอย่างนุ่มนวล (Graceful Degradation)
ภายใต้ภาระงานที่สูง การจำกัดอัตราคำขอจะช่วยป้องกันความล้มเหลวแบบต่อเนื่อง
อัลกอริทึมการจำกัดอัตราคำขอ
อัลกอริทึมที่แตกต่างกันมีข้อดีข้อเสียที่แตกต่างกัน
1. Fixed Window (หน้าต่างคงที่)
นับจำนวนคำขอในช่วงเวลาที่กำหนดไว้
วิธีการทำงาน:
Window 1 (00:00-00:59): 100 requests allowed
Window 2 (01:00-01:59): 100 requests allowed
การใช้งาน:
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
ข้อดี:
- ติดตั้งใช้งานง่าย
- ใช้หน่วยความจำน้อย
ข้อเสีย:
- ปัญหาการส่งคำขอพร้อมกันจำนวนมาก (Burst problem): ไคลเอ็นต์สามารถส่งคำขอ 100 ครั้งได้ที่ 00:59 และ 100 ครั้งที่ 01:00 (รวม 200 ครั้งภายใน 2 วินาที)
2. Sliding Window (หน้าต่างเลื่อน)
นับจำนวนคำขอในหน้าต่างเวลาที่เคลื่อนที่ (rolling time window)
วิธีการทำงาน:
ที่เวลา 01:30 น. นับจำนวนคำขอตั้งแต่ 00:30 น. ถึง 01:30 น. (60 นาทีที่ผ่านมา)
การใช้งาน:
def is_allowed(user_id):
now = time.time()
window_start = now - 3600 # 1 hour ago
key = f"rate_limit:{user_id}"
# Remove old requests
redis.zremrangebyscore(key, 0, window_start)
# Count requests in window
count = redis.zcard(key)
if count < 100:
redis.zadd(key, {now: now})
redis.expire(key, 3600)
return True
return False
ข้อดี:
- ไม่มีปัญหาการส่งคำขอพร้อมกันจำนวนมาก
- จำกัดอัตราคำขอได้อย่างแม่นยำ
ข้อเสีย:
- ใช้หน่วยความจำสูงกว่า (เก็บ timestamp สำหรับแต่ละคำขอ)
- ซับซ้อนกว่า
3. Token Bucket (ถังโทเค็น)
โทเค็นจะถูกเพิ่มลงในถังด้วยอัตราคงที่ แต่ละคำขอจะใช้โทเค็นหนึ่งอัน
วิธีการทำงาน:
Bucket capacity: 100 tokens
Refill rate: 10 tokens/second
Request: consumes 1 token
การใช้งาน:
def is_allowed(user_id):
now = time.time()
key = f"rate_limit:{user_id}"
# Get current state
data = redis.hgetall(key)
tokens = float(data.get('tokens', 100))
last_refill = float(data.get('last_refill', now))
# Refill tokens
elapsed = now - last_refill
tokens = min(100, tokens + elapsed * 10) # 10 tokens/sec
if tokens >= 1:
tokens -= 1
redis.hset(key, 'tokens', tokens)
redis.hset(key, 'last_refill', now)
redis.expire(key, 3600)
return True
return False
ข้อดี:
- อนุญาตให้มีการส่งคำขอพร้อมกันจำนวนมากได้ (สูงสุดเท่ากับความจุของถัง)
- การจำกัดอัตราคำขอที่เป็นไปอย่างราบรื่น
- มาตรฐานอุตสาหกรรม
ข้อเสีย:
- ซับซ้อนกว่าแบบ Fixed Window
- ต้องจัดเก็บสถานะ
4. Leaky Bucket (ถังรั่ว)
คำขอจะถูกเพิ่มลงในคิวและประมวลผลด้วยอัตราคงที่
วิธีการทำงาน:
Queue capacity: 100 requests
Process rate: 10 requests/second
ข้อดี:
- อัตราการส่งออกที่ราบรื่น
- เหมาะสำหรับการป้องกันบริการดาวน์สตรีม
ข้อเสีย:
- เพิ่มความหน่วง (คำขอต้องรอในคิว)
- ติดตั้งใช้งานซับซ้อน
ควรใช้อัลกอริทึมใด?
สำหรับ API ส่วนใหญ่: Token Bucket
เป็นมาตรฐานอุตสาหกรรม อนุญาตให้มีการส่งคำขอพร้อมกันจำนวนมากได้อย่างสมเหตุสมผล และให้การจำกัดอัตราคำขอที่ราบรื่น
Modern PetstoreAPI ใช้อัลกอริทึม Token Bucket พร้อมโควต้าต่อผู้ใช้
ส่วนหัวการจำกัดอัตราคำขอมาตรฐาน
ใช้ส่วนหัวมาตรฐาน IETF (draft-ietf-httpapi-ratelimit-headers)
ส่วนหัวมาตรฐาน
RateLimit-Limit: จำนวนคำขอสูงสุดที่อนุญาตในช่วงเวลา
RateLimit-Limit: 100
RateLimit-Remaining: จำนวนคำขอที่เหลืออยู่ในช่วงเวลาปัจจุบัน
RateLimit-Remaining: 45
RateLimit-Reset: เวลา (เป็นวินาที) จนกว่าการจำกัดอัตราคำขอจะรีเซ็ต
RateLimit-Reset: 3600
ตัวอย่างการตอบสนอง
GET /pets
200 OK
RateLimit-Limit: 100
RateLimit-Remaining: 99
RateLimit-Reset: 3600
{
"data": [...]
}
ส่วนหัวแบบเก่า (เลิกใช้แล้ว)
API จำนวนมากใช้ส่วนหัวที่ไม่เป็นมาตรฐาน:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
X-RateLimit-Reset: 1710331200
อย่าใช้สิ่งเหล่านี้ คำนำหน้า X- เลิกใช้แล้ว และรูปแบบไม่ได้ถูกกำหนดเป็นมาตรฐาน
Modern PetstoreAPI ใช้วิธีการจำกัดอัตราคำขออย่างไร
Modern PetstoreAPI ใช้การจำกัดอัตราคำขอแบบ Token Bucket พร้อมส่วนหัวมาตรฐาน
การจำกัดอัตราคำขอตามระดับ
ระดับฟรี:
- 100 คำขอ/ชั่วโมง
- 1,000 คำขอ/วัน
ระดับโปร:
- 10,000 คำขอ/ชั่วโมง
- 100,000 คำขอ/วัน
ระดับองค์กร:
- ขีดจำกัดที่กำหนดเอง
การใช้งาน
คำขอที่สำเร็จ:
GET /v1/pets
200 OK
RateLimit-Limit: 100
RateLimit-Remaining: 99
RateLimit-Reset: 3540
{
"data": [...]
}
เกินขีดจำกัดอัตราคำขอ:
GET /v1/pets
429 Too Many Requests
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": "Rate Limit Exceeded",
"status": 429,
"detail": "You have exceeded the rate limit of 100 requests per hour",
"instance": "/v1/pets",
"retryAfter": 120,
"limit": 100,
"window": "1h"
}
ต่อผู้ใช้ (Per-User) เทียบกับ ต่อ IP (Per-IP)
ต่อผู้ใช้ (คำขอที่ผ่านการยืนยันตัวตน):
จำกัดอัตราคำขอด้วย User ID หรือ API Key แม่นยำและเป็นธรรมมากกว่า
user_id = get_authenticated_user()
is_allowed(user_id)
ต่อ IP (คำขอที่ไม่ผ่านการยืนยันตัวตน):
จำกัดอัตราคำขอด้วยที่อยู่ IP แม่นยำน้อยกว่า (IP ที่ใช้ร่วมกัน, VPN) แต่ก็ยังดีกว่าไม่มี
ip_address = request.remote_addr
is_allowed(ip_address)
Modern PetstoreAPI ใช้การจำกัดอัตราคำขอต่อผู้ใช้สำหรับคำขอที่ผ่านการยืนยันตัวตน และจำกัดอัตราคำขอต่อ IP สำหรับปลายทางสาธารณะ
รูปแบบการตอบสนองการจำกัดอัตราคำขอ
เมื่อเกินขีดจำกัดอัตราคำขอ ให้ส่งคืนสถานะ 429 พร้อมรูปแบบข้อผิดพลาด RFC 9457
โครงสร้างการตอบสนอง
{
"type": "https://petstoreapi.com/errors/rate-limit-exceeded",
"title": "Rate Limit Exceeded",
"status": 429,
"detail": "You have exceeded your rate limit. Please try again later.",
"instance": "/v1/pets",
"retryAfter": 120,
"limit": 100,
"remaining": 0,
"reset": 120,
"window": "1h"
}
ส่วนหัว
429 Too Many Requests
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 120
Retry-After: 120
Retry-After: แจ้งไคลเอ็นต์ว่าจะลองใหม่ได้เมื่อใด (เป็นวินาที)
การทดสอบการจำกัดอัตราคำขอด้วย Apidog
Apidog ช่วยให้คุณทดสอบพฤติกรรมการจำกัดอัตราคำขอ
สถานการณ์ทดสอบ
1. การใช้งานปกติ:
Send 50 requests → All succeed
Check RateLimit-Remaining decreases
- ส่ง 50 คำขอ → ทั้งหมดสำเร็จ
- ตรวจสอบว่า RateLimit-Remaining ลดลง
2. เกินขีดจำกัด:
Send 101 requests → 101st returns 429
Verify error response format
Check Retry-After header
- ส่ง 101 คำขอ → คำขอที่ 101 ส่งคืนสถานะ 429
- ตรวจสอบรูปแบบการตอบสนองข้อผิดพลาด
- ตรวจสอบส่วนหัว Retry-After
3. พฤติกรรมการรีเซ็ต:
Exceed limit → Wait for reset → Verify limit restored
- เกินขีดจำกัด → รอการรีเซ็ต → ตรวจสอบว่าขีดจำกัดกลับมาใช้งานได้
4. ระดับที่แตกต่างกัน:
Test free tier (100/hour)
Test pro tier (10,000/hour)
Verify limits are enforced correctly
- ทดสอบระดับฟรี (100/ชั่วโมง)
- ทดสอบระดับโปร (10,000/ชั่วโมง)
- ตรวจสอบว่ามีการบังคับใช้ขีดจำกัดอย่างถูกต้อง
ตัวอย่างการทดสอบด้วย Apidog
// Test rate limit headers
pm.test("Rate limit headers present", () => {
pm.response.to.have.header("RateLimit-Limit");
pm.response.to.have.header("RateLimit-Remaining");
pm.response.to.have.header("RateLimit-Reset");
});
// Test rate limit exceeded
pm.test("Returns 429 when limit exceeded", () => {
// Make 101 requests
for (let i = 0; i < 101; i++) {
pm.sendRequest("GET /v1/pets");
}
pm.response.to.have.status(429);
});
แนวทางปฏิบัติที่ดีที่สุดสำหรับการจำกัดอัตราคำขอ
1. ใช้ส่วนหัวมาตรฐาน
ใช้ส่วนหัวมาตรฐาน IETF ไม่ใช่ส่วนหัว X- ที่กำหนดเอง
2. ส่งคืนสถานะ 429 ไม่ใช่ 403
429 หมายถึง “มีคำขอมากเกินไป” 403 หมายถึง “ต้องห้าม” อย่าสับสนระหว่างสองสถานะนี้
3. รวมส่วนหัว Retry-After
แจ้งไคลเอ็นต์เมื่อพวกเขาสามารถลองใหม่ได้
4. จัดทำเอกสารขีดจำกัดของคุณ
ทำให้ขีดจำกัดอัตราคำขอแสดงอยู่ในเอกสารประกอบ
5. จัดหาหลายระดับ
ระดับฟรี: ขีดจำกัดต่ำ ระดับชำระเงิน: ขีดจำกัดสูงกว่า
6. จำกัดอัตราคำขอตามผู้ใช้ ไม่ใช่ IP
ขีดจำกัดต่อผู้ใช้มีความแม่นยำและเป็นธรรมมากกว่า
7. อนุญาตให้มีการส่งคำขอพร้อมกันจำนวนมาก
Token Bucket อนุญาตให้มีการส่งคำขอพร้อมกันจำนวนมากที่สมเหตุสมผล โดยไม่เป็นการลงโทษการใช้งานปกติ
8. ตรวจสอบการถูกจำกัดอัตราคำขอ
ติดตามความถี่ที่ไคลเอ็นต์ถูกจำกัดอัตราคำขอ อัตราที่สูงบ่งชี้ถึงปัญหา
9. จัดหาปลายทาง (endpoint) สำหรับสถานะการจำกัดอัตราคำขอ
GET /v1/rate-limit
200 OK
{
"limit": 100,
"remaining": 45,
"reset": 3540
}
10. ทดสอบการจำกัดอัตราคำขอ
ใช้ Apidog เพื่อทดสอบพฤติกรรมการจำกัดอัตราคำขอก่อนการนำไปใช้งานจริง
สรุป
การจำกัดอัตราคำขอปกป้อง API ของคุณจากการใช้งานในทางที่ผิด และรับประกันการใช้งานที่เป็นธรรม ใช้อัลกอริทึม Token Bucket พร้อมส่วนหัว IETF มาตรฐาน (RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset) ส่งคืนสถานะ 429 Too Many Requests พร้อมรูปแบบข้อผิดพลาด RFC 9457 เมื่อเกินขีดจำกัด
Modern PetstoreAPI ใช้การจำกัดอัตราคำขออย่างถูกต้องด้วยโควต้าต่อผู้ใช้ ส่วนหัวมาตรฐาน และการตอบสนองข้อผิดพลาดที่ชัดเจน ตรวจสอบ เอกสารประกอบ สำหรับรายละเอียดการใช้งาน
ทดสอบการจำกัดอัตราคำขอของคุณด้วย Apidog เพื่อให้แน่ใจว่าทำงานได้อย่างถูกต้องภายใต้ภาระงานที่สูง และจัดการกับกรณีขอบ (edge cases) ได้อย่างเหมาะสม
คำถามที่พบบ่อย
ฉันควรกำหนดขีดจำกัดอัตราคำขอเท่าใด?
เริ่มต้นอย่างระมัดระวัง: 100 คำขอ/ชั่วโมงสำหรับระดับฟรี, 10,000/ชั่วโมงสำหรับระดับชำระเงิน ปรับเปลี่ยนตามรูปแบบการใช้งานและความสามารถของโครงสร้างพื้นฐาน
ฉันควรจำกัดอัตราคำขอตาม IP หรือผู้ใช้?
จำกัดอัตราคำขอตามผู้ใช้ (API key) สำหรับคำขอที่ผ่านการยืนยันตัวตน ใช้การจำกัดอัตราคำขอตาม IP สำหรับปลายทางสาธารณะเท่านั้น
จะเกิดอะไรขึ้นหากไคลเอ็นต์เกินขีดจำกัดอัตราคำขอ?
ส่งคืนสถานะ 429 Too Many Requests พร้อมส่วนหัว Retry-After อย่าบล็อกไคลเอ็นต์ถาวร—ปล่อยให้พวกเขาลองใหม่หลังจากหน้าต่างรีเซ็ต
ฉันจะจัดการการจำกัดอัตราคำขอสำหรับเว็บฮุคได้อย่างไร?
เว็บฮุคเป็นการสื่อสารแบบเซิร์ฟเวอร์ต่อเซิร์ฟเวอร์ ดังนั้นขีดจำกัดอัตราคำขอจึงควรสูงขึ้น พิจารณาขีดจำกัดที่แยกต่างหากสำหรับเว็บฮุคเทียบกับการเรียกใช้ API
ฉันควรจำกัดอัตราคำขอสำหรับบริการภายในหรือไม่?
ใช่ แต่มีขีดจำกัดที่สูงกว่ามาก การจำกัดอัตราคำขอช่วยป้องกันความล้มเหลวแบบต่อเนื่องแม้ในระบบภายใน
ฉันจะทดสอบการจำกัดอัตราคำขอได้อย่างไร?
ใช้ Apidog เพื่อส่งคำขอหลายรายการและตรวจสอบการตอบสนอง 429 ส่วนหัวการจำกัดอัตราคำขอ และพฤติกรรมการรีเซ็ต
จะเกิดอะไรขึ้นหาก API ของฉันอยู่หลัง CDN?
การแคชของ CDN ช่วยลดภาระงาน แต่คุณยังคงต้องการการจำกัดอัตราคำขอสำหรับการเรียกใช้ที่ไม่ถูกแคช (cache misses) และคำขอประเภท POST/PUT/DELETE
ฉันจะติดตั้งใช้งานการจำกัดอัตราคำขอในเซิร์ฟเวอร์หลายเครื่องได้อย่างไร?
ใช้พื้นที่เก็บข้อมูลที่ใช้ร่วมกัน (Redis, Memcached) เพื่อติดตามการจำกัดอัตราคำขอในทุกเซิร์ฟเวอร์ อย่าใช้หน่วยความจำภายใน—มันจะใช้ไม่ได้ในระบบแบบกระจาย
