时间:2022-06-21 10:02:53 | 栏目:Golang | 点击:次
在调用第三方 API 的时候, 基本都有访问限速的限制条件. 第三方的 API 有多个的时候, 就不太好控制访问速度, 常常会导致 HTTP 429(Too Many Requests) 然后就会有一段时间的禁止访问.
为了应对这种限速的情况, 通过一个简单的请求队列来控制访问的速度, 之后基本没遇到过 HTTP 429 了.
首先, 每个请求包装成一个 RequestParam 的 struct, 其中包含请求的地址,类型,参数以及 response 的 channel.
发送请求的时候, 只要将 RequestParam 放入请求队列中即可, 请求完成后, 将 response 放入对应的 channel 中.
整个代码实现很简单:
package util import ( "fmt" apiclient "gitee.com/wangyubin/gutils/api_client" "gitee.com/wangyubin/gutils/logger" ) // request 包含的内容 type RequestParam struct { Api string Method string JsonReq interface{} Resp chan []byte } // 请求队列, 本质是一个channel type RequestQueue struct { Queue chan RequestParam } var queue *RequestQueue // 获取队列 func GetQueue() *RequestQueue { return queue } // 初始化队列 func InitRequestQueue(size int) { queue = &RequestQueue{ Queue: make(chan RequestParam, size), } } // 将请求放入队列 func (rq *RequestQueue) Enqueue(p RequestParam) { rq.Queue <- p } // 请求队列服务, 一直等待接受和处理请求 func (rq *RequestQueue) Run() { lg := logger.GetLogger() for p := range rq.Queue { var resp []byte var err error switch p.Method { case "GET": resp, err = apiclient.GetJson(p.Api, p.JsonReq) case "POST": resp, err = apiclient.PostJson(p.Api, p.JsonReq) default: err = fmt.Errorf("Wrong type of METHOD(%s)\n", p.Method) } if err != nil { lg.Err(err).Msg("access api error: " + p.Api) continue } if p.Resp != nil { p.Resp <- resp close(p.Resp) } } lg.Info().Msg("request queue finished!") }
这里的请求是用了我自己封装的 apiclient, 可以根据实际情况替换.
在我的应用场景里, 只要 api 顺序访问就不会出现 HTTP 429 了, 如果这样觉得速度太快的的话, 可以尝试在 Run() 函数中加入一些时间间隔.
func (rq *RequestQueue) Run() { lg := logger.GetLogger() for p := range rq.Queue { time.Sleep(1 * time.Second) // ... 省略的代码 ... } lg.Info().Msg("request queue finished!") }
使用很简单, 首先启动, 然后每个调用的地方将 RequestParam 放入队列并等待 response 即可.
启动队列服务
func main() { // init request queue and start queue service util.InitRequestQueue(100) queue := util.GetQueue() defer close(queue.Queue) go queue.Run() // 其他启动代码 }
使用队列服务
func Request(param1 string, param2 int) error { api := "http://xxxx.com" api = fmt.Sprintf("%s?period=%s&size=%d", api, param1, param2) queue := util.GetQueue() param := util.RequestParam{ Api: api, Method: "GET", Resp: make(chan []byte, 1), } queue.Enqueue(param) var respData struct { Status string `json:"status"` Data []model.Data `json:"data"` } var err error for resp := range param.Resp { err = json.Unmarshal(resp, &respData) if err != nil { lg.Err(err).Msg("unmarshal json error") return err } } fmt.Println(respData) return err }