在调用第三方 API 的时候, 基本都有访问限速的限制条件. 第三方的 API 有多个的时候, 就不太好控制访问速度, 经常会致使 HTTP 429(Too Many Requests) 而后就会有一段时间的禁止访问.git
为了应对这种限速的状况, 经过一个简单的请求队列来控制访问的速度, 以后基本没遇到过 HTTP 429 了.json
首先, 每一个请求包装成一个 RequestParam 的 struct, 其中包含请求的地址,类型,参数以及 response 的 channel.api
发送请求的时候, 只要将 RequestParam 放入请求队列中便可, 请求完成后, 将 response 放入对应的 channel 中.函数
整个代码实现很简单:code
1 package util 2 3 import ( 4 "fmt" 5 6 apiclient "gitee.com/wangyubin/gutils/api_client" 7 "gitee.com/wangyubin/gutils/logger" 8 ) 9 10 // request 包含的内容 11 type RequestParam struct { 12 Api string 13 Method string 14 JsonReq interface{} 15 Resp chan []byte 16 } 17 18 // 请求队列, 本质是一个channel 19 type RequestQueue struct { 20 Queue chan RequestParam 21 } 22 23 var queue *RequestQueue 24 25 // 获取队列 26 func GetQueue() *RequestQueue { 27 return queue 28 } 29 30 // 初始化队列 31 func InitRequestQueue(size int) { 32 queue = &RequestQueue{ 33 Queue: make(chan RequestParam, size), 34 } 35 } 36 37 // 将请求放入队列 38 func (rq *RequestQueue) Enqueue(p RequestParam) { 39 rq.Queue <- p 40 } 41 42 // 请求队列服务, 一直等待接受和处理请求 43 func (rq *RequestQueue) Run() { 44 lg := logger.GetLogger() 45 for p := range rq.Queue { 46 var resp []byte 47 var err error 48 switch p.Method { 49 case "GET": 50 resp, err = apiclient.GetJson(p.Api, p.JsonReq) 51 case "POST": 52 resp, err = apiclient.PostJson(p.Api, p.JsonReq) 53 default: 54 err = fmt.Errorf("Wrong type of METHOD(%s)\n", p.Method) 55 } 56 57 if err != nil { 58 lg.Err(err).Msg("access api error: " + p.Api) 59 continue 60 } 61 if p.Resp != nil { 62 p.Resp <- resp 63 close(p.Resp) 64 } 65 } 66 67 lg.Info().Msg("request queue finished!") 68 }
这里的请求是用了我本身封装的 apiclient, 能够根据实际状况替换.队列
在个人应用场景里, 只要 api 顺序访问就不会出现 HTTP 429 了, 若是这样以为速度太快的的话, 能够尝试在 Run() 函数中加入一些时间间隔.string
1 func (rq *RequestQueue) Run() { 2 lg := logger.GetLogger() 3 for p := range rq.Queue { 4 time.Sleep(1 * time.Second) 5 // ... 省略的代码 ... 6 } 7 8 lg.Info().Msg("request queue finished!") 9 }
使用很简单, 首先启动, 而后每一个调用的地方将 RequestParam 放入队列并等待 response 便可.it
1 func main() { 2 // init request queue and start queue service 3 util.InitRequestQueue(100) 4 queue := util.GetQueue() 5 defer close(queue.Queue) 6 go queue.Run() 7 8 // 其余启动代码 9 }
1 func Request(param1 string, param2 int) error { 2 api := "http://xxxx.com" 3 api = fmt.Sprintf("%s?period=%s&size=%d", api, param1, param2) 4 5 queue := util.GetQueue() 6 param := util.RequestParam{ 7 Api: api, 8 Method: "GET", 9 Resp: make(chan []byte, 1), 10 } 11 queue.Enqueue(param) 12 13 var respData struct { 14 Status string `json:"status"` 15 Data []model.Data `json:"data"` 16 } 17 var err error 18 for resp := range param.Resp { 19 err = json.Unmarshal(resp, &respData) 20 if err != nil { 21 lg.Err(err).Msg("unmarshal json error") 22 return err 23 } 24 } 25 26 fmt.Println(respData) 27 return err 28 }