概要

在调用第三方 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 }

最新文章

  1. MMORPG大型游戏设计与开发(服务器 AI 概述)
  2. low-rank 的相关求解方法 (CODE) Low-Rank Matrix Recovery and Completion via Convex Optimization
  3. WPF的ComboBox 数据模板自定义
  4. memarch
  5. 【转】JS 和 java 交互
  6. 通过注解(annotation)配置Bean
  7. Spring学习笔记之bean配置
  8. java web页面 base
  9. 一个react的完整项目展示
  10. cocod2d-x 之 CCDirector、CCScene、CCSprite
  11. 【转载】 Searching过程粗略梳理
  12. 201521123012 《Java程序设计》第十三周学习总结
  13. 设计模式——适配器模式(C++实现)
  14. Laravel-mix 中文文档
  15. centos 打包报错License for package Android SDK Build-Tools 25.0.3 not accepted
  16. android自定义控件 几种方式总结
  17. 虚拟机压力测试延迟高的可能原因及 ILPIP 配置 / 查询脚本
  18. VS2010/MFC编程入门之四十六(MFC常用类:MFC异常处理)
  19. Docker 监控之 SaaS 解决方案
  20. 学习python第三天数据库day2

热门文章

  1. Mybatis源码学习第六天(核心流程分析)之Executor分析
  2. Kubernetes-12:Secret介绍及演示
  3. SpringBoot写后端接口,看这一篇就够了!
  4. command三国杀开发日记20200914
  5. Jenkins+Git+Gitlab+Ansible实现持续集成自动化部署静态网站(6)
  6. Redis基础认识及常用命令使用
  7. Vue render函数 函数时组件 jsx
  8. hystrix源码之线程池
  9. Spring系列 之数据源的配置 数据库 数据源 连接池的区别
  10. pytest自学第一期