You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何设计Go的Goroutines程序处理API限流错误并控制请求速率

Go 并发API调用限流实现方案

针对每秒最多20次API调用的需求,推荐两种低改造成本的实现方式:

方案1:使用官方扩展库golang.org/x/time/rate令牌桶限流器(推荐)

令牌桶限流器会固定每秒生成20个调用令牌,所有Goroutine必须拿到令牌才能发起API请求,天然满足速率限制要求,改造成本极低:

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

// 新增limiter入参
func readInstance(acc string, region string, wg *sync.WaitGroup, limiter *rate.Limiter) {
    defer wg.Done()
    // 等待获取令牌,拿不到就阻塞直到有可用令牌
    if err := limiter.Wait(context.Background()); err != nil {
        log.Println("获取令牌失败:", err)
        return
    }
    response, err := client.DescribeInstances(acc, region)
    if err != nil {
        log.Println(err)
        return
    }
    // 后续业务逻辑不变
    // ...
}

func main() {
    accounts := []string{"g", "h", "i", ...}
    regions := []string{"g", "h", "i", ...}
    var wg sync.WaitGroup
    // 初始化限流器:每秒生成20个令牌,桶最大容量20(允许短时间内最多并发20次)
    limiter := rate.NewLimiter(rate.Limit(20), 20)
    
    for _, region := range regions {
        for i := 0; i < len(accounts); i++ {
            wg.Add(1)
            go readInstance(accounts[i], region, &wg, limiter)
        }
    }
    wg.Wait()
}

方案2:无第三方依赖的Ticker实现

如果不想引入额外扩展库,可以用定时器配合channel实现速率控制:

import (
    "sync"
    "log"
    "time"
)

func readInstance(acc string, region string, wg *sync.WaitGroup, limiterChan <-chan struct{}) {
    defer wg.Done()
    // 等待获取槽位
    <-limiterChan
    response, err := client.DescribeInstances(acc, region)
    if err != nil {
        log.Println(err)
        return
    }
    // 后续业务逻辑不变
    // ...
}

func main() {
    accounts := []string{"g", "h", "i", ...}
    regions := []string{"g", "h", "i", ...}
    var wg sync.WaitGroup
    // 初始化限流槽位channel,容量20
    limiterChan := make(chan struct{}, 20)
    // 启动定时器,每50ms填充一个槽位(1秒/20次=50ms间隔)
    go func() {
        ticker := time.NewTicker(50 * time.Millisecond)
        defer ticker.Stop()
        for range ticker.C {
            limiterChan <- struct{}{}
        }
    }()
    
    for _, region := range regions {
        for i := 0; i < len(accounts); i++ {
            wg.Add(1)
            go readInstance(accounts[i], region, &wg, limiterChan)
        }
    }
    wg.Wait()
}

注意事项

  • 两种方案都是全局速率控制,刚好匹配API对单个客户端的限流规则
  • 如果后续需要调整调用速率,只需要修改限流器的初始化参数即可,无需改动业务逻辑
  • 生产环境使用时可以给limiter.Wait传入带超时的context,避免长时间等待导致程序阻塞

内容的提问来源于stack exchange,提问作者user14171711

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 19:18:02