ข้ามไปยังเนื้อหาหลัก
เวอร์ชัน: ล่าสุด

การจำกัดอัตรา

อยู่ภายในขีดจำกัดอัตราของ Omise API และสร้างการรวมที่มีประสิทธิภาพ เรียนรู้เกี่ยวกับเฮดเดอร์การจำกัดอัตรา จัดการข้อผิดพลาด 429 อย่างสง่างาม และเพิ่มประสิทธิภาพรูปแบบคำขอของคุณ

ภาพรวม

เพื่อให้แน่ใจว่าบริการเชื่อถือได้สำหรับผู้ค้าทุกราย Omise ใช้การจำกัดอัตราบนคำขอ API การจำกัดอัตราป้องกันไม่ให้การรวมเดียวครอบงำ API และรับประกันการจัดสรรทรัพยากรอย่างเป็นธรรม การทำความเข้าใจและเคารพขีดจำกัดเหล่านี้เป็นสิ่งสำคัญสำหรับการสร้างการรวมการชำระเงินที่แข็งแกร่ง

เริ่มต้นอย่างรวดเร็ว
  • ขีดจำกัดเริ่มต้น: 1,000 คำขอต่อนาทีต่อคีย์ API
  • ตรวจสอบเฮดเดอร์ X-RateLimit-* ในการตอบกลับ
  • จัดการ HTTP 429 ด้วยการชะลอแบบทวีคูณ
  • ใช้งานคิวคำขอสำหรับการดำเนินการปริมาณสูง
  • แคชการตอบกลับเมื่อเหมาะสม

รายละเอียดการจำกัดอัตรา

ขีดจำกัดปัจจุบัน

ประเภทขีดจำกัดค่าขอบเขต
ขีดจำกัดอัตรามาตรฐาน1,000 คำขอ/นาทีต่อคีย์ API
การอนุญาตระเบิด~100 คำขออนุญาตการระเบิดสั้นๆ
ระยะเวลารีเซ็ต60 วินาทีหน้าต่างแบบเลื่อน

สิ่งที่นับเข้าสู่ขีดจำกัด

นับ:

  • คำขอ API ทั้งหมด (GET, POST, PATCH, DELETE)
  • คำขอที่สำเร็จ (การตอบกลับ 2xx)
  • คำขอที่ล้มเหลว (การตอบกลับ 4xx, 5xx)
  • การยืนยันตัวตนที่ล้มเหลว

ไม่นับ:

  • คำขอที่ถูกบล็อกก่อนถึง API (URL ที่ไม่ถูกต้อง)
  • คำขอสินทรัพย์คงที่
  • การเข้าถึงแดชบอร์ด
  • การส่ง webhook จาก Omise

เฮดเดอร์การจำกัดอัตรา

การตอบกลับ API ทุกครั้งรวมข้อมูลการจำกัดอัตราในเฮดเดอร์:

เฮดเดอร์การตอบกลับ

HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 995
X-RateLimit-Reset: 1612137600

คำอธิบายเฮดเดอร์

เฮดเดอร์คำอธิบายตัวอย่าง
X-RateLimit-Limitคำขอสูงสุดที่อนุญาตในหน้าต่าง1000
X-RateLimit-Remainingคำขอที่เหลือในหน้าต่างปัจจุบัน995
X-RateLimit-ResetUnix timestamp เมื่อขีดจำกัดรีเซ็ต1612137600

การอ่านเฮดเดอร์ในโค้ด

# Ruby - ตรวจสอบเฮดเดอร์การจำกัดอัตรา
require 'omise'

Omise.api_key = ENV['OMISE_SECRET_KEY']

response = Omise::Charge.retrieve('chrg_test_...')

# เข้าถึงเฮดเดอร์
limit = response.http_headers['X-RateLimit-Limit']
remaining = response.http_headers['X-RateLimit-Remaining']
reset = response.http_headers['X-RateLimit-Reset']

puts "ขีดจำกัดอัตรา: #{remaining}/#{limit}"
puts "รีเซ็ตที่: #{Time.at(reset.to_i)}"
# Python - ตรวจสอบเฮดเดอร์การจำกัดอัตรา
import omise
from datetime import datetime

omise.api_secret = os.environ['OMISE_SECRET_KEY']

charge = omise.Charge.retrieve('chrg_test_...')

# เข้าถึงเฮดเดอร์ (เฉพาะไลบรารี)
headers = charge.response_headers

limit = headers.get('X-RateLimit-Limit')
remaining = headers.get('X-RateLimit-Remaining')
reset_timestamp = int(headers.get('X-RateLimit-Reset', 0))

print(f"ขีดจำกัดอัตรา: {remaining}/{limit}")
print(f"รีเซ็ตที่: {datetime.fromtimestamp(reset_timestamp)}")
// Node.js - ตรวจสอบเฮดเดอร์การจำกัดอัตรา
const omise = require('omise')({
secretKey: process.env.OMISE_SECRET_KEY
});

try {
const charge = await omise.charges.retrieve('chrg_test_...');

// เฮดเดอร์ที่มีอยู่ในการตอบกลับ
const headers = charge._response.headers;

const limit = headers['x-ratelimit-limit'];
const remaining = headers['x-ratelimit-remaining'];
const reset = headers['x-ratelimit-reset'];

console.log(`ขีดจำกัดอัตรา: ${remaining}/${limit}`);
console.log(`รีเซ็ตที่: ${new Date(reset * 1000)}`);

} catch (error) {
console.error('คำขอล้มเหลว:', error);
}

HTTP 429 การตอบกลับ

เมื่อคุณเกินขีดจำกัดอัตรา API จะส่งคืน HTTP 429 Too Many Requests:

429 รูปแบบการตอบกลับ

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1612137660
Retry-After: 60

{
"object": "error",
"location": "https://www.omise.co/api-errors#rate-limit-exceeded",
"code": "rate_limit_exceeded",
"message": "too many requests, please try again later"
}

ฟิลด์การตอบกลับ

ฟิลด์คำอธิบาย
code"rate_limit_exceeded"
messageข้อความข้อผิดพลาดที่อ่านได้
Retry-Afterวินาทีที่จะรอก่อนลองใหม่

การจัดการการจำกัดอัตรา

กลยุทธ์ 1: การชะลอแบบทวีคูณ (แนะนำ)

ลองใหม่ด้วยความล่าช้าที่เพิ่มขึ้น:

# Ruby - การชะลอแบบทวีคูณ
require 'omise'

def create_charge_with_backoff(params, max_attempts: 5)
attempt = 0

begin
attempt += 1
Omise::Charge.create(params)

rescue Omise::Error => e
if e.code == 'rate_limit_exceeded' && attempt < max_attempts
# คำนวณความล่าช้าในการชะลอ: 1 วินาที, 2 วินาที, 4 วินาที, 8 วินาที, 16 วินาที
delay = 2 ** (attempt - 1)

# เพิ่มการสั่นไหว (ความสุ่ม) เพื่อป้องกันการรุมกัน
jitter = rand(0..delay * 0.1)
sleep(delay + jitter)

retry
else
raise
end
end
end

# การใช้งาน
charge = create_charge_with_backoff(
amount: 100000,
currency: 'thb',
card: token
)
# Python - การชะลอแบบทวีคูณด้วย decorator
import time
import random
from functools import wraps

def exponential_backoff(max_attempts=5, base_delay=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)

except omise.errors.BaseError as e:
if e.code != 'rate_limit_exceeded':
raise

if attempt == max_attempts - 1:
raise

# คำนวณความล่าช้าพร้อมการสั่นไหว
delay = base_delay * (2 ** attempt)
jitter = random.uniform(0, delay * 0.1)
total_delay = delay + jitter

print(f"ถูกจำกัดอัตรา กำลังลองใหม่ใน {total_delay:.2f} วินาที...")
time.sleep(total_delay)

raise Exception("เกินจำนวนครั้งการลองใหม่สูงสุด")

return wrapper
return decorator

@exponential_backoff(max_attempts=5)
def create_charge(amount, currency, card):
return omise.Charge.create(
amount=amount,
currency=currency,
card=card
)

# การใช้งาน
charge = create_charge(100000, 'thb', token)
// Node.js - การชะลอแบบทวีคูณ
async function createChargeWithBackoff(chargeData, maxAttempts = 5) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
return await omise.charges.create(chargeData);

} catch (error) {
if (error.code !== 'rate_limit_exceeded' || attempt === maxAttempts - 1) {
throw error;
}

// คำนวณความล่าช้าพร้อมการสั่นไหว
const baseDelay = Math.pow(2, attempt) * 1000;
const jitter = Math.random() * baseDelay * 0.1;
const delay = baseDelay + jitter;

console.log(`ถูกจำกัดอัตรา กำลังลองใหม่ใน ${(delay / 1000).toFixed(2)} วินาที...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}

// การใช้งาน
const charge = await createChargeWithBackoff({
amount: 100000,
currency: 'thb',
card: token
});

กลยุทธ์ 2: เคารพเฮดเดอร์ลองใหม่หลัง

ใช้เวลาลองใหม่ที่เซิร์ฟเวอร์แนะนำ:

<?php
function createChargeWithRetryAfter($params, $maxAttempts = 5) {
$attempt = 0;

while ($attempt < $maxAttempts) {
try {
$attempt++;
return OmiseCharge::create($params);

} catch (Exception $e) {
if ($e->getCode() !== 'rate_limit_exceeded' || $attempt >= $maxAttempts) {
throw $e;
}

// รับเฮดเดอร์ลองใหม่หลังจากการตอบกลับ
$retryAfter = $e->getResponse()->getHeader('Retry-After');
$delay = $retryAfter ? (int)$retryAfter : 60;

echo "ถูกจำกัดอัตรา กำลังรอ {$delay} วินาที...\n";
sleep($delay);
}
}

throw new Exception('เกินจำนวนครั้งการลองใหม่สูงสุด');
}

// การใช้งาน
$charge = createChargeWithRetryAfter([
'amount' => 100000,
'currency' => 'thb',
'card' => $token
]);

กลยุทธ์ 3: คิวคำขอ

จัดคิวคำขอเพื่อควบคุมอัตรา:

// Node.js - คิวคำขอพร้อมการจำกัดอัตรา
class RateLimitedQueue {
constructor(requestsPerMinute = 1000) {
this.queue = [];
this.requestsPerMinute = requestsPerMinute;
this.requestsThisMinute = 0;
this.windowStart = Date.now();
}

async enqueue(requestFn) {
return new Promise((resolve, reject) => {
this.queue.push({ requestFn, resolve, reject });
this.processQueue();
});
}

async processQueue() {
if (this.queue.length === 0) return;

// รีเซ็ตหน้าต่างหากผ่านไปแล้ว 1 นาที
const now = Date.now();
if (now - this.windowStart >= 60000) {
this.requestsThisMinute = 0;
this.windowStart = now;
}

// ตรวจสอบว่าสามารถทำคำขอได้หรือไม่
if (this.requestsThisMinute >= this.requestsPerMinute) {
// รอจนกว่าจะถึงหน้าต่างถัดไป
const waitTime = 60000 - (now - this.windowStart);
setTimeout(() => this.processQueue(), waitTime);
return;
}

// ประมวลผลคำขอถัดไป
const { requestFn, resolve, reject } = this.queue.shift();
this.requestsThisMinute++;

try {
const result = await requestFn();
resolve(result);
} catch (error) {
if (error.code === 'rate_limit_exceeded') {
// จัดคิวคำขอใหม่
this.queue.unshift({ requestFn, resolve, reject });
// รอก่อนประมวลผล
setTimeout(() => this.processQueue(), 5000);
} else {
reject(error);
}
}

// ประมวลผลถัดไปในคิว
if (this.queue.length > 0) {
// หน่วงเวลาเล็กน้อยระหว่างคำขอ
setTimeout(() => this.processQueue(), 100);
}
}
}

// การใช้งาน
const queue = new RateLimitedQueue(1000);

async function createCharge(data) {
return queue.enqueue(() => omise.charges.create(data));
}

// คำขอหลายรายการถูกจัดคิวโดยอัตโนมัติ
const charge1 = await createCharge({ amount: 100000, currency: 'thb', card: token1 });
const charge2 = await createCharge({ amount: 50000, currency: 'thb', card: token2 });

กลยุทธ์ 4: การดำเนินการเป็นชุด

ลดคำขอด้วยการทำเป็นชุด:

# Python - การดึงการชำระเงินเป็นชุด
def get_charges_batch(charge_ids, batch_size=100):
"""ดึงการชำระเงินหลายรายการอย่างมีประสิทธิภาพ"""
charges = []

# ใช้ endpoint รายการแทนการดึงข้อมูลแต่ละรายการ
for i in range(0, len(charge_ids), batch_size):
batch_ids = charge_ids[i:i+batch_size]

# คำขอรายการเดียวแทนคำขอดึงข้อมูล 100 ครั้ง
page = omise.Charge.list(limit=batch_size)

# กรองเป็น ID ที่ร้องขอ
batch_charges = [c for c in page['data'] if c.id in batch_ids]
charges.extend(batch_charges)

# พิจารณาขีดจำกัดอัตรา
time.sleep(0.1)

return charges

# ไม่ดี: 1000 คำขอ
for charge_id in charge_ids:
charge = omise.Charge.retrieve(charge_id) # 1 คำขอแต่ละรายการ

# ดี: 10 คำขอ
charges = get_charges_batch(charge_ids, batch_size=100)

การตรวจสอบการจำกัดอัตรา

ติดตามการใช้งานแบบเรียลไทม์

# Ruby - ตัวตรวจสอบขีดจำกัดอัตรา
class RateLimitMonitor
def initialize
@limit = nil
@remaining = nil
@reset_at = nil
end

def track_response(response)
headers = response.http_headers

@limit = headers['X-RateLimit-Limit'].to_i
@remaining = headers['X-RateLimit-Remaining'].to_i
@reset_at = Time.at(headers['X-RateLimit-Reset'].to_i)

# บันทึกหากใกล้ถึงขีดจำกัด
usage_percent = ((@limit - @remaining).to_f / @limit * 100).round(2)

if usage_percent > 80
Rails.logger.warn(
"ขีดจำกัดอัตรา: ใช้ไป #{usage_percent}% (เหลือ #{@remaining}/#{@limit})"
)
end

# แจ้งเตือนหากใกล้มาก
if usage_percent > 95
alert_high_rate_limit_usage(usage_percent)
end
end

def alert_high_rate_limit_usage(percent)
# ส่งการแจ้งเตือน (อีเมล, Slack, PagerDuty, ฯลฯ)
AlertService.notify(
"⚠️ การใช้งานขีดจำกัดอัตรา: #{percent}%",
"เหลือเพียง #{@remaining} คำขอจนถึง #{@reset_at}"
)
end
end

# การใช้งานใน wrapper คำขอ
monitor = RateLimitMonitor.new

def make_request(&block)
response = block.call
monitor.track_response(response)
response
end

charge = make_request { Omise::Charge.retrieve('chrg_test_...') }

เมตริกแดชบอร์ด

// Node.js - บันทึกเมตริกไปยังบริการตรวจสอบ
class MetricsCollector {
constructor(metricsService) {
this.metrics = metricsService;
}

trackRateLimit(headers) {
const limit = parseInt(headers['x-ratelimit-limit']);
const remaining = parseInt(headers['x-ratelimit-remaining']);
const used = limit - remaining;
const usagePercent = (used / limit) * 100;

// ส่งไปยังบริการตรวจสอบ (DataDog, CloudWatch, ฯลฯ)
this.metrics.gauge('omise.rate_limit.remaining', remaining);
this.metrics.gauge('omise.rate_limit.used', used);
this.metrics.gauge('omise.rate_limit.usage_percent', usagePercent);

// เรียกการแจ้งเตือนหากใช้งานสูง
if (usagePercent > 90) {
this.metrics.event('omise.rate_limit.high_usage', {
alert_type: 'warning',
text: `ขีดจำกัดอัตรา Omise อยู่ที่ ${usagePercent.toFixed(2)}%`
});
}
}

trackRateLimitError() {
this.metrics.increment('omise.rate_limit.exceeded');
}
}

// การใช้งาน
const metrics = new MetricsCollector(datadogClient);

async function makeOmiseRequest(requestFn) {
try {
const response = await requestFn();

// ติดตามการใช้งานขีดจำกัดอัตรา
if (response._response && response._response.headers) {
metrics.trackRateLimit(response._response.headers);
}

return response;

} catch (error) {
if (error.code === 'rate_limit_exceeded') {
metrics.trackRateLimitError();
}
throw error;
}
}

กลยุทธ์การเพิ่มประสิทธิภาพ

1. แคชการตอบกลับ

# Ruby - แคชด้วย Redis
require 'redis'

class OmiseCache
def initialize
@redis = Redis.new
end

def get_charge(charge_id)
cache_key = "charge:#{charge_id}"

# ลองแคชก่อน
cached = @redis.get(cache_key)
return JSON.parse(cached) if cached

# ดึงจาก API
charge = Omise::Charge.retrieve(charge_id)

# แคชเป็นเวลา 5 นาที
@redis.setex(cache_key, 300, charge.to_json)

charge
end

def get_customer(customer_id)
cache_key = "customer:#{customer_id}"

cached = @redis.get(cache_key)
return JSON.parse(cached) if cached

customer = Omise::Customer.retrieve(customer_id)
@redis.setex(cache_key, 300, customer.to_json)

customer
end
end

cache = OmiseCache.new

# การเรียกครั้งแรก - เรียก API
charge = cache.get_charge('chrg_test_...')

# การเรียกครั้งต่อไป - จากแคช (ไม่มีคำขอ API)
charge = cache.get_charge('chrg_test_...')

2. ใช้เว็บฮุคแทนการตรวจสอบซ้ำ

// ❌ ไม่ดี - การตรวจสอบซ้ำทำให้สิ้นเปลืองขีดจำกัดอัตรา
async function waitForChargeComplete(chargeId) {
let charge;

// ตรวจสอบทุก 2 วินาที - สิ้นเปลืองคำขอ!
while (true) {
charge = await omise.charges.retrieve(chargeId);

if (charge.status === 'successful' || charge.status === 'failed') {
return charge;
}

await new Promise(resolve => setTimeout(resolve, 2000));
}
}

// ✅ ดี - ใช้เว็บฮุค
app.post('/webhooks/omise', async (req, res) => {
const event = req.body;

if (event.key === 'charge.complete') {
const charge = event.data;

// ประมวลผลการชำระเงินที่เสร็จสมบูรณ์
await processCharge(charge);
}

res.sendStatus(200);
});

3. การประมวลผลเว็บฮุคเป็นชุด

# ประมวลผลเว็บฮุคเป็นชุดเพื่อลดการเรียก API
class WebhookProcessor:
def __init__(self):
self.batch = []
self.batch_size = 10

def add_event(self, event):
self.batch.append(event)

if len(self.batch) >= self.batch_size:
self.process_batch()

def process_batch(self):
# ดึง ID
charge_ids = [e['data']['id'] for e in self.batch if e['key'] == 'charge.complete']

# คำขอรายการเดียวแทนการดึงข้อมูล N ครั้ง
charges = omise.Charge.list(limit=100)

# จับคู่และประมวลผล
for event in self.batch:
charge = next((c for c in charges['data'] if c.id == event['data']['id']), None)
if charge:
process_charge(charge)

self.batch = []

processor = WebhookProcessor()

@app.route('/webhooks/omise', methods=['POST'])
def webhook():
event = request.json
processor.add_event(event)
return '', 200

4. ปรับแต่งคิวรีรายการ

<?php
// ใช้ฟิลเตอร์เพื่อลดการถ่ายโอนและประมวลผลข้อมูล

// ❌ ไม่ดี - ดึงทุกอย่าง
$charges = OmiseCharge::retrieve(['limit' => 100]);
$successfulCharges = array_filter($charges['data'], function($c) {
return $c['status'] === 'successful';
});

// ✅ ดี - กรองที่ฝั่งเซิร์ฟเวอร์ (คุณสมบัติในอนาคต - ปัจจุบันใช้การแบ่งหน้าอย่างมีประสิทธิภาพ)
// หมายเหตุ: Omise API ยังไม่รองรับการกรองสถานะ แต่ใช้การแบ่งหน้าอย่างมีประสิทธิภาพ
$charges = OmiseCharge::retrieve([
'limit' => 100,
'offset' => 0
]);

// ประมวลผลอย่างมีประสิทธิภาพ
foreach ($charges['data'] as $charge) {
if ($charge['status'] === 'successful') {
processCharge($charge);
}
}

5. คำขอแบบขนานด้วยความระมัดระวัง

// Go - คำขอแบบขนานพร้อมการจำกัดอัตรา
package main

import (
"golang.org/x/time/rate"
"sync"
)

type RateLimitedClient struct {
client *omise.Client
limiter *rate.Limiter
}

func NewRateLimitedClient(client *omise.Client, requestsPerSecond int) *RateLimitedClient {
return &RateLimitedClient{
client: client,
limiter: rate.NewLimiter(rate.Limit(requestsPerSecond), requestsPerSecond),
}
}

func (c *RateLimitedClient) CreateCharge(params *operations.CreateCharge) (*omise.Charge, error) {
// รอตัวจำกัดอัตรา
err := c.limiter.Wait(context.Background())
if err != nil {
return nil, err
}

return c.client.CreateCharge(params)
}

func main() {
client, _ := omise.NewClient(
os.Getenv("OMISE_PUBLIC_KEY"),
os.Getenv("OMISE_SECRET_KEY"),
)

// จำกัดเป็น 16 คำขอต่อวินาที (ระยะขอบปลอดภัยต่ำกว่า 1000/นาที)
rateLimited := NewRateLimitedClient(client, 16)

var wg sync.WaitGroup

// ประมวลผล 100 การชำระเงินแบบขนาน
for i := 0; i < 100; i++ {
wg.Add(1)

go func(idx int) {
defer wg.Done()

charge, err := rateLimited.CreateCharge(&operations.CreateCharge{
Amount: 100000,
Currency: "thb",
Card: tokens[idx],
})

if err != nil {
log.Printf("การชำระเงิน %d ล้มเหลว: %v", idx, err)
return
}

log.Printf("การชำระเงิน %d ถูกสร้าง: %s", idx, charge.ID)
}(i)
}

wg.Wait()
}

6. ใช้งานเซอร์กิตเบรกเกอร์

// Node.js - เซอร์กิตเบรกเกอร์เพื่อป้องกันความล้มเหลวที่ลุกลาม
class CircuitBreaker {
constructor(threshold = 5, timeout = 60000) {
this.failureThreshold = threshold;
this.timeout = timeout;
this.failureCount = 0;
this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
this.nextAttempt = Date.now();
}

async execute(requestFn) {
if (this.state === 'OPEN') {
if (Date.now() < this.nextAttempt) {
throw new Error('เซอร์กิตเบรกเกอร์เปิดอยู่');
}
this.state = 'HALF_OPEN';
}

try {
const result = await requestFn();
this.onSuccess();
return result;

} catch (error) {
this.onFailure();
throw error;
}
}

onSuccess() {
this.failureCount = 0;
this.state = 'CLOSED';
}

onFailure() {
this.failureCount++;

if (this.failureCount >= this.failureThreshold) {
this.state = 'OPEN';
this.nextAttempt = Date.now() + this.timeout;
console.log(`เซอร์กิตเบรกเกอร์เปิด จะลองใหม่หลังจาก ${this.timeout}ms`);
}
}
}

// การใช้งาน
const breaker = new CircuitBreaker(5, 60000);

async function createChargeSafe(chargeData) {
return breaker.execute(() => omise.charges.create(chargeData));
}

การทดสอบขีดจำกัดอัตรา

จำลองการตอบกลับการจำกัดอัตรา

# RSpec - ทดสอบการจัดการขีดจำกัดอัตรา
require 'webmock'

RSpec.describe 'การจัดการขีดจำกัดอัตรา' do
it 'ลองใหม่เมื่อเกิดข้อผิดพลาดขีดจำกัดอัตรา' do
stub_request(:post, 'https://api.omise.co/charges')
.to_return(
{ status: 429, body: { code: 'rate_limit_exceeded' }.to_json },
{ status: 200, body: { object: 'charge', id: 'chrg_test_123' }.to_json }
)

charge = create_charge_with_retry(amount: 100000, currency: 'thb')

expect(charge.id).to eq('chrg_test_123')
expect(WebMock).to have_requested(:post, 'https://api.omise.co/charges').twice
end

it 'เคารพเฮดเดอร์ลองใหม่หลัง' do
stub_request(:post, 'https://api.omise.co/charges')
.to_return(
status: 429,
headers: { 'Retry-After' => '5' },
body: { code: 'rate_limit_exceeded' }.to_json
)

expect {
create_charge_with_retry(amount: 100000, currency: 'thb')
}.to raise_error(Omise::Error)

# ตรวจสอบว่ารอเวลาที่เหมาะสม (จำลองเวลาหากจำเป็น)
end
end

การทดสอบโหลด

// Node.js - ทดสอบโหลดขีดจำกัดอัตรา
async function loadTest() {
const results = {
success: 0,
rateLimited: 0,
errors: 0
};

const requests = [];

// ส่ง 1500 คำขอ (ควรเจอขีดจำกัดอัตราที่ 1000)
for (let i = 0; i < 1500; i++) {
const request = omise.charges.list({ limit: 1 })
.then(() => {
results.success++;
})
.catch((error) => {
if (error.code === 'rate_limit_exceeded') {
results.rateLimited++;
} else {
results.errors++;
}
});

requests.push(request);
}

await Promise.all(requests);

console.log('ผลการทดสอบโหลด:');
console.log(` สำเร็จ: ${results.success}`);
console.log(` ถูกจำกัดอัตรา: ${results.rateLimited}`);
console.log(` ข้อผิดพลาดอื่นๆ: ${results.errors}`);
}

// เรียกใช้การทดสอบ
loadTest();

แนวทางปฏิบัติที่ดีที่สุด

1. ใช้งานตรรกะการลองใหม่เสมอ

# ✅ ดี - มีตรรกะการลองใหม่ในตัว
@retry(
stop=stop_after_attempt(5),
wait=wait_exponential(multiplier=1, min=1, max=60),
retry=retry_if_exception_type(omise.errors.RateLimitError)
)
def create_charge(amount, currency, card):
return omise.Charge.create(
amount=amount,
currency=currency,
card=card
)

2. ตรวจสอบการใช้งานการจำกัดอัตรา

# ✅ ดี - ติดตามและแจ้งเตือน
after_action :track_rate_limit

def track_rate_limit
if response.headers['X-RateLimit-Remaining']
remaining = response.headers['X-RateLimit-Remaining'].to_i
limit = response.headers['X-RateLimit-Limit'].to_i

usage_percent = ((limit - remaining).to_f / limit * 100).round(2)

# บันทึกเมตริก
StatsD.gauge('omise.rate_limit.usage', usage_percent)

# แจ้งเตือนหากใช้งานสูง
if usage_percent > 90
AlertService.notify("การใช้งานขีดจำกัดอัตรา Omise สูง: #{usage_percent}%")
end
end
end

3. ใช้รูปแบบคำขอที่เหมาะสม

// ✅ ดี - ทำเป็นชุดและแคช
class EfficientOmiseClient {
constructor() {
this.cache = new Map();
this.batchQueue = [];
}

async getCharge(chargeId) {
// ตรวจสอบแคชก่อน
if (this.cache.has(chargeId)) {
return this.cache.get(chargeId);
}

// ดึงจาก API
const charge = await omise.charges.retrieve(chargeId);

// แคชเป็นเวลา 5 นาที
this.cache.set(chargeId, charge);
setTimeout(() => this.cache.delete(chargeId), 5 * 60 * 1000);

return charge;
}

async getCharges(chargeIds) {
// ใช้ endpoint รายการสำหรับการชำระเงินหลายรายการ
const charges = await omise.charges.list({ limit: 100 });

// แคชการชำระเงินทั้งหมด
charges.data.forEach(charge => {
this.cache.set(charge.id, charge);
});

return chargeIds.map(id =>
charges.data.find(c => c.id === id)
).filter(Boolean);
}
}

4. ใช้งานการจำกัดคำขอ

<?php
class ThrottledOmiseClient {
private $requestTimes = [];
private $maxRequestsPerMinute = 900; // ระยะขอบปลอดภัย

public function makeRequest($callable) {
$this->cleanOldRequests();

// ตรวจสอบว่าถึงขีดจำกัดแล้วหรือไม่
if (count($this->requestTimes) >= $this->maxRequestsPerMinute) {
// รอจนกว่าคำขอที่เก่าที่สุดจะหมดอายุ
$oldestRequest = min($this->requestTimes);
$waitTime = 60 - (time() - $oldestRequest);

if ($waitTime > 0) {
sleep($waitTime);
}

$this->cleanOldRequests();
}

// บันทึกคำขอนี้
$this->requestTimes[] = time();

// ทำคำขอ
return $callable();
}

private function cleanOldRequests() {
$cutoff = time() - 60;
$this->requestTimes = array_filter(
$this->requestTimes,
function($t) use ($cutoff) { return $t > $cutoff; }
);
}
}

// การใช้งาน
$client = new ThrottledOmiseClient();

$charge = $client->makeRequest(function() use ($params) {
return OmiseCharge::create($params);
});

5. จัดการการจำกัดอัตราอย่างสง่างาม

# ✅ ดี - การจัดการข้อผิดพลาดที่เป็นมิตรกับผู้ใช้
def create_charge(params)
Omise::Charge.create(params)

rescue Omise::Error => e
if e.code == 'rate_limit_exceeded'
# อย่าเปิดเผยรายละเอียดทางเทคนิคให้กับผู้ใช้
flash[:error] = "ระบบชำระเงินของเรากำลังยุ่งในขณะนี้ กรุณาลองใหม่อีกครั้งในอีกสักครู่"

# บันทึกเพื่อการตรวจสอบ
Rails.logger.warn("เกินขีดจำกัดอัตรา: #{e.message}")

# ลองใหม่ในงานเบื้องหลัง
ChargeCreationJob.perform_later(params)
else
raise
end
end

รายการตรวจสอบการจำกัดอัตรา

ก่อนเผยแพร่:

  • ใช้งานการชะลอแบบทวีคูณสำหรับการลองใหม่
  • เคารพเฮดเดอร์ลองใหม่หลัง
  • ตรวจสอบเฮดเดอร์การจำกัดอัตราในการตอบกลับ
  • ตั้งค่าการแจ้งเตือนสำหรับการใช้งานสูง (>80%)
  • แคชการตอบกลับเมื่อเหมาะสม
  • ใช้เว็บฮุคแทนการตรวจสอบซ้ำ
  • ดำเนินการเป็นชุดเมื่อเป็นไปได้
  • ทดสอบการจัดการการจำกัดอัตราใน staging
  • จัดทำเอกสารกลยุทธ์การจำกัดอัตราสำหรับทีม
  • มีแผนสำรองสำหรับข้อผิดพลาดการจำกัดอัตรา
  • ตรวจสอบเมตริกการจำกัดอัตราใน production
  • ตรวจสอบโค้ดสำหรับการเรียก API ที่ไม่จำเป็น

อ้างอิงอย่างรวดเร็ว

ขีดจำกัดปัจจุบัน

1,000 คำขอต่อนาทีต่อคีย์ API

เฮดเดอร์การจำกัดอัตรา

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 995
X-RateLimit-Reset: 1612137600

HTTP 429 การตอบกลับ

{
"code": "rate_limit_exceeded",
"message": "too many requests, please try again later"
}

รูปแบบการลองใหม่พื้นฐาน

begin
omise_request()
rescue Omise::Error => e
if e.code == 'rate_limit_exceeded'
sleep(2 ** attempt)
retry
end
raise
end

สูตรการชะลอแบบทวีคูณ

ความล่าช้า = ความล่าช้าเริ่มต้น * (2 ^ ความพยายาม) + การสั่นไหว

ตัวอย่าง:

  • ความพยายาม 1: 1 วินาที + การสั่นไหว
  • ความพยายาม 2: 2 วินาที + การสั่นไหว
  • ความพยายาม 3: 4 วินาที + การสั่นไหว
  • ความพยายาม 4: 8 วินาที + การสั่นไหว
  • ความพยายาม 5: 16 วินาที + การสั่นไหว

ทรัพยากรที่เกี่ยวข้อง


พร้อมที่จะรวม? ตรวจสอบคู่มือที่จำเป็นทั้งหมด: การยืนยันตัวตนการจัดการข้อผิดพลาดการแบ่งหน้าความเป็นเอกภาพการกำหนดเวอร์ชัน