Trong bài viết này, tôi sẽ chia sẻ cách xử lý lỗi 429 rate limit hiệu quả khi làm việc với API AI trong Go. Đây là bài học xương máu từ dự án thực tế của tôi.

Bối Cảnh Lỗi

Tối thứ 6 tuần trước, hệ thống chat bot của khách hàng bỗng dừng hoạt động. Trong Slack, tôi nhận được notification:

ERROR: 429 Too Many Requests - Retry-After: 45
POST https://api.holysheep.ai/v1/chat/completions
Duration: 2341.23ms
Status: rate_limit_exceeded

Sau 2 tiếng debug, tôi phát hiện nguyên nhân: script batch processing gọi API liên tục không có cơ chế chờ. Với HolyShehe AI, bạn có thể tiết kiệm 85%+ chi phí (tỷ giá ¥1=$1) nhưng vẫn cần xử lý rate limit đúng cách.

Exponential Backoff Là Gì?

Exponential backoff là chiến lược chờ tăng dần theo cấp số nhân khi gặp lỗi rate limit. Thay vì chờ cố định 1 giây, bạn chờ 1s → 2s → 4s → 8s...

Triển Khai Chi Tiết

1. Định Nghĩa Client Và Struct

package main

import (
    "bytes"
    "context"
    "encoding/json"
    "fmt"
    "math"
    "net/http"
    "time"

    "github.com/google/uuid"
)

const (
    baseURL     = "https://api.holysheep.ai/v1"
    apiKey      = "YOUR_HOLYSHEEP_API_KEY"
    maxRetries  = 5
    baseDelay   = 1 * time.Second
    maxDelay    = 60 * time.Second
)

type ChatMessage struct {
    Role    string json:"role"
    Content string json:"content"
}

type ChatRequest struct {
    Model       string        json:"model"
    Messages    []ChatMessage json:"messages"
    MaxTokens   int           json:"max_tokens,omitempty"
    Temperature float64       json:"temperature,omitempty"
}

type ChatResponse struct {
    ID      string   json:"id"
    Model   string   json:"model"
    Choices []Choice json:"choices"
    Usage   Usage    json:"usage"
}

type Choice struct {
    Message ChatMessage json:"message"
}

type Usage struct {
    PromptTokens     int json:"prompt_tokens"
    CompletionTokens int json:"completion_tokens"
    TotalTokens      int json:"total_tokens"
}

2. Hàm Exponential Backoff

func calculateBackoff(attempt int) time.Duration {
    // delay = baseDelay * 2^attempt + jitter
    delay := float64(baseDelay) * math.Pow(2, float64(attempt))
    
    // Thêm jitter ngẫu nhiên ±25% để tránh thundering herd
    jitter := delay * 0.25 * (float64(attempt%100) / 100)
    
    result := time.Duration(delay + jitter)
    
    // Không vượt quá maxDelay
    if result > maxDelay {
        return maxDelay
    }
    return result
}

func CallAPIWithRetry(ctx context.Context, req ChatRequest) (*ChatResponse, error) {
    var lastErr error
    
    for attempt := 0; attempt <= maxRetries; attempt++ {
        if attempt > 0 {
            delay := calculateBackoff(attempt - 1)
            fmt.Printf("Attempt %d/%d: Retry sau %v\n", attempt, maxRetries, delay)
            
            select {
            case <-ctx.Done():
                return nil, ctx.Err()
            case <-time.After(delay):
            }
        }
        
        response, statusCode, err := doRequest(ctx, req)
        if err != nil {
            lastErr = err
            fmt.Printf("Lỗi kết nối: %v\n", err)
            continue
        }
        
        switch statusCode {
        case 200:
            return response, nil
        case 429:
            // Rate limit - cần chờ theo Retry-After header
            retryAfter := getRetryAfter(response)
            if retryAfter > 0 {
                fmt.Printf("Rate limit. Chờ %v theo Retry-After header\n", retryAfter)
                time.Sleep(retryAfter)
            } else {
                fmt.Printf("Rate limit. Sử dụng exponential backoff\n")
            }
            lastErr = fmt.Errorf("rate limit exceeded")
        case 401:
            return nil, fmt.Errorf("unauthorized: kiểm tra API key")
        case 500, 502, 503:
            fmt.Printf("Server error %d, thử lại...\n", statusCode)
            lastErr = fmt.Errorf("server error: %d", statusCode)
        default:
            return nil, fmt.Errorf("unexpected status: %d", statusCode)
        }
    }
    
    return nil, fmt.Errorf("đã thử %d lần, lỗi cuối: %w", maxRetries, lastErr)
}

3. Hàm Thực Hiện Request

func doRequest(ctx context.Context, req ChatRequest) (*ChatResponse, int, error) {
    jsonData, err := json.Marshal(req)
    if err != nil {
        return nil, 0, err
    }
    
    httpReq, err := http.NewRequestWithContext(ctx, "POST", 
        baseURL+"/chat/completions", 
        bytes.NewBuffer(jsonData))
    if err != nil {
        return nil, 0, err
    }
    
    httpReq.Header.Set("Authorization", "Bearer "+apiKey)
    httpReq.Header.Set("Content-Type", "application/json")
    httpReq.Header.Set("X-Request-ID", uuid.New().String())
    
    client := &http.Client{
        Timeout: 60 * time.Second,
    }
    
    resp, err := client.Do(httpReq)
    if err != nil {
        return nil, 0, err
    }
    defer resp.Body.Close()
    
    var result ChatResponse
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
        // Đọc body để debug
        return nil, resp.StatusCode, err
    }
    
    return &result, resp.StatusCode, nil
}

func getRetryAfter(resp *ChatResponse) time.Duration {
    // Nếu server trả Retry-After header, parse nó
    // Trong thực tế, bạn cần đọc từ http.Response
    return 0 // Tạm thời return 0, sử dụng backoff
}

4. Sử Dụng Trong Thực Tế

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
    defer cancel()
    
    request := ChatRequest{
        Model: "gpt-4o", // Hoặc claude-sonnet-4.5, deepseek-v3.2
        Messages: []ChatMessage{
            {Role: "system", Content: "Bạn là trợ lý AI hữu ích."},
            {Role: "user", Content: "Giải thích exponential backoff"},
        },
        MaxTokens:   500,
        Temperature: 0.7,
    }
    
    start := time.Now()
    
    response, err := CallAPIWithRetry(ctx, request)
    if err != nil {
        fmt.Printf("Thất bại sau nhiều lần thử: %v\n", err)
        return
    }
    
    elapsed := time.Since(start)
    fmt.Printf("Thành công! Thời gian: %v\n", elapsed)
    fmt.Printf("Response: %s\n", response.Choices[0].Message.Content)
    fmt.Printf("Tokens used: %d\n", response.Usage.TotalTokens)
}

Tối Ưu Hóa Với Concurrent Workers

Để xử lý hàng nghìn requests, sử dụng worker pool pattern:

type WorkerPool struct {
    workers    int
    jobQueue   chan ChatRequest
    resultChan chan Result
    wg         sync.WaitGroup
}

type Result struct {
    Response *ChatResponse
    Error    error
    Index    int
}

func NewWorkerPool(workers int) *WorkerPool {
    return &WorkerPool{
        workers:    workers,
        jobQueue:   make(chan ChatRequest, workers*2),
        resultChan: make(chan Result, workers*2),
    }
}

func (wp *WorkerPool) Start(ctx context.Context) {
    for i := 0; i < wp.workers; i++ {
        wp.wg.Add(1)
        go func(workerID int) {
            defer wp.wg.Done()
            for req := range wp.jobQueue {
                resp, err := CallAPIWithRetry(ctx, req)
                wp.resultChan <- Result{
                    Response: resp,
                    Error:    err,
                    Index:    workerID,
                }
            }
        }(i)
    }
}

func (wp *WorkerPool) Submit(job ChatRequest) {
    wp.jobQueue <- job
}

func (wp *WorkerPool) Close() {
    close(wp.jobQueue)
    wp.wg.Wait()
    close(wp.resultChan)
}

// Sử dụng:
// pool := NewWorkerPool(5) // 5 workers song song
// pool.Start(ctx)
// for _, req := range requests {
//     pool.Submit(req)
// }
// pool.Close()

Lỗi Thường Gặp Và Cách Khắc Phục

1. Lỗi "Connection timeout" Khi Server Bận

Nguyên nhân: Timeout quá ngắn hoặc server đang quá tải.

// ❌ Sai: Timeout quá ngắn
client := &http.Client{Timeout: 5 * time.Second}

// ✅ Đúng: Timeout đủ để handle retry
client := &http.Client{
    Timeout: 120 * time.Second,
}
// Hoặc sử dụng context với deadline hợp lý
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()

2. Lỗi "401 Unauthorized" Với API Key Hết Hạn

Nguyên nhân: API key không đúng hoặc hết hạn.

// ❌ Sai: Hardcode API key trực tiếp
const apiKey = "sk-xxxx"

// ✅ Đúng: Load từ environment variable
func getAPIKey() string {
    key := os.Getenv("HOLYSHEEP_API_KEY")
    if key == "" {
        // Kiểm tra file config
        key = loadFromConfig("api_key")
    }
    if key == "" {
        log.Fatal("HOLYSHEEP_API_KEY not set. Đăng ký tại: https://www.holysheep.ai/register")
    }
    return key
}

// Sử dụng:
const apiKey = getAPIKey()

3. Lỗi "Thundering Herd" Khi Nhiều Process Khởi Động Cùng Lúc

Nguyên nhân: Tất cả clients chờ cùng một thời điểm rồi gửi lại.

// ❌ Sai: Không có jitter
delay := baseDelay * time.Duration(math.Pow(2, attempt))

// ✅ Đúng: Thêm jitter ngẫu nhiên ±25%
func calculateBackoffWithJitter(attempt int) time.Duration {
    base := float64(baseDelay) * math.Pow(2, float64(attempt))
    // Jitter: ±25%
    jitter := base * 0.25 * (float64(time.Now().UnixNano()%100) / 100)
    return time.Duration(base + jitter)
}

// Hoặc sử dụng package chuyên dụng
// go get github.com/cenkalti/backoff/v4
import "github.com/cenkalti/backoff/v4"

retry := backoff.ExponentialBackoff{
    InitialInterval:     baseDelay,
    RandomizationFactor: 0.25,
    Multiplier:          2.0,
    MaxInterval:         maxDelay,
}

4. Memory Leak Khi Response Body Không Được Đóng

Nguyên nhân: Forget defer resp.Body.Close().

// ❌ Sai: Không close body
func doRequestBad(ctx context.Context, req ChatRequest) (*ChatResponse, error) {
    resp, err := client.Do(httpReq)
    // Quên defer resp.Body.Close()
    // → Memory leak!
    
    var result ChatResponse
    json.NewDecoder(resp.Body).Decode(&result)
    return &result, nil
}

// ✅ Đúng: Luôn defer close
func doRequestGood(ctx context.Context, req ChatRequest) (*ChatResponse, error) {
    resp, err := client.Do(httpReq)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close() // ← Quan trọng!
    
    if resp.StatusCode != 200 {
        body, _ := io.ReadAll(resp.Body)
        return nil, fmt.Errorf("status %d: %s", resp.StatusCode, string(body))
    }
    
    var result ChatResponse
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
        return nil, err
    }
    return &result, nil
}

So Sánh Chi Phí Với HolySheep AI

Model HolySheep AI OpenAI Tiết kiệm
GPT-4.1 $8/MTok $60/MTok 86%
Claude Sonnet 4.5 $15/MTok $18/MTok 17%
DeepSeek V3.2 $0.42/MTok $2.50/MTok 83%

Với latency trung bình dưới 50ms, HolySheep AI là lựa chọn tối ưu cho production workloads. Đặc biệt, bạn có thể thanh toán qua WeChat/Alipay và nhận tín dụng miễn phí khi đăng ký.

Kết Luận

Exponential backoff là kỹ thuật thiết yếu khi làm việc với bất kỳ API AI nào. Qua bài viết này, bạn đã học được:

Đừng để rate limit làm chậm production của bạn. Triển khai exponential backoff ngay hôm nay!

👉 Đăng ký HolySheep AI — nhận tín dụng miễn phí khi đăng ký