golang API 請求佇列
阿新 • • 發佈:2020-06-01
概要
在呼叫第三方 API 的時候,基本都有訪問限速的限制條件. 第三方的 API 有多個的時候,就不太好控制訪問速度,常常會導致 HTTP 429(Too Many Requests) 然後就會有一段時間的禁止訪問.
為了應對這種限速的情況,通過一個簡單的請求佇列來控制訪問的速度,之後基本沒遇到過 HTTP 429 了.
實現思路
首先,每個請求包裝成一個 RequestParam 的 struct,其中包含請求的地址,型別,引數以及 response 的 channel.
傳送請求的時候,只要將 RequestParam 放入請求佇列中即可,請求完成後,將 response 放入對應的 channel 中.
整個程式碼實現很簡單:
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() 函式中加入一些時間間隔.
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 即可.
啟動佇列服務
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 }