golang API请求队列的实现
作者:wang_yb 发布时间:2024-03-11 16:39:50
标签:golang,API,请求队列
概要
在调用第三方 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
}
来源:https://www.cnblogs.com/wang_yb/p/13018901.html


猜你喜欢
- 一、前言python的两个单元测试包分别是 doctest 和 unittest,这两个包的使用起来各有长处,适用于不同的场景doctest
- step函数概述step函数用于绘制阶梯图。根据源码可知,step函数是对plot函数的轻量级封装,很多概念和用法与plot函数非常相似。d
- <% Function ReplaceUrl2(HTMLstr) Dim n,st
- 写完调用天气接口的demo之后,小程序调用天气接口并且渲染在页面,顺便再调用了一下美图的接口API:美图APIurlwxml:<vie
- SESSION会话开启时,会首先发送一个对浏览器的唯一标识session_id的cookie(名字为PHPSESSID可以通过session
- 背景在本地开发vue项目的时候,当你习惯了proxyTable解决本地跨域的问题,切换到nuxt的时候,你会发现,添加了proxyTable
- 记录了MySQL 5.7.19 winx64解压缩版安装教程,具体内容如下系统环境:Win7 x64软件准备:mysql 5.7.19 wi
- 以前看过有人转换过的,当时仅仅惊叹了一下,就过去了,没有记下来,直至于用到的时候呢,开始到处找,找来找去都没有找不到痕迹了,心里也就郁郁寡欢
- 从句法上看,协程与生成器类似,都是定义体中包含 yield 关键字的函数。可是,在协程中, yield 通常出现在表达式的右边(例如, da
- 本文实例讲述了php+mysqli数据库连接的两种方式。分享给大家供大家参考。具体如下:这里讲述mysqli数据库连接两种方式比较,即面向对
- 本文实例讲述了Python with关键字,上下文管理器,@contextmanager文件操作。分享给大家供大家参考,具体如下:demo.
- 使用 Beanstalkd 作为消息队列服务,然后结合 Python 的装饰器语法实现一个简单的异步任务处理工具.最终效果定义任务:from
- 1、pyecharts绘制饼图(显示百分比)# 导入模块from pyecharts import options as optsfrom
- 枚举类Enum枚举类,在企业开发中用的比较多当我们需要定义常量时,一个办法是用大写的变量通过整数来定义,例如月份:# 当项目中需要使用12个
- 属性多层数组数据的添加修改为什么需要使用Vue.set?vue中不能检测到数组的一些变化比如一下两种:1、数组长度的变化 vm.arr.le
- 一、模拟数据库数据1-1 创建数据库及表脚本 - vim slap.sh#!/bin/bash HOSTNA
- 一、前言春节即将来临,大家肯定各种掏腰包花花花,小编相信大家在支付时候,微信、支付宝支付肯定是优先选择。今天小编心血来潮,为大家带来一个很有
- 前言:本篇主要讲两方面,错误和异常以及模块。在编程时遇见错误信息在所难免,Python中会也有很多种错误信息,常见的两种就是语法错误和逻辑错
- 1.学习sql之前回忆一下,什么是变量?变量:能存储数据的值。变量是一块内存空间的表示。数组一连串空间变量是存储数据的容器(通俗讲)2.变量
- Python有自己内置的标准GUI库--Tkinter,只要安装好Python就可以调用。今天学习到了图形界面设计的问题,刚开始就卡住了。为