如何为处理Webhook请求的Express应用实现Axios出站请求限流?
实现Node.js Express请求队列处理API速率限制
解决这个问题的核心是用全局队列存储待发送任务,通过统一调度逻辑按照目标API的速率限制(每秒2次,即每500ms一次)依次执行队列任务,避免所有请求同时触发。
具体实现步骤
- 定义全局队列与状态变量,存储待发送数据并标记队列是否正在处理
- 修改Webhook路由,将转换后的数据加入队列而非直接发送
- 实现队列处理函数,按固定间隔从队列取任务并发送到目标API
完整代码示例
const express = require('express'); const axios = require('axios'); const app = express(); app.use(express.json()); // 全局队列:存储待发送到API2的数据 const api2Queue = []; // 标记队列是否正在处理,避免重复启动调度逻辑 let isProcessingQueue = false; // 目标API速率限制:每秒2次,对应间隔500ms const REQUEST_INTERVAL = 500; // 队列处理核心函数 async function processQueue() { if (isProcessingQueue || api2Queue.length === 0) return; isProcessingQueue = true; // 取出队列首个任务 const data = api2Queue.shift(); try { await sendToApi2WithAxios(data); } catch (err) { // 处理发送失败:可选择重新入队或记录日志 console.error('发送到API2失败:', err.message); // 可选:将失败任务重新加入队列末尾 // api2Queue.push(data); } finally { isProcessingQueue = false; // 间隔500ms后处理下一个任务 setTimeout(processQueue, REQUEST_INTERVAL); } } // 封装API2请求函数 async function sendToApi2WithAxios(data) { return await axios.post('https://目标API地址', data); } // 数据转换函数(示例) async function transformData(data) { // 替换为你的实际转换逻辑 return { ...data, transformed: true }; } // Webhook接收路由 app.post("/api1", async (req, res) => { try { const data = req.body.object; const transformedData = await transformData(data); // 将转换后的数据加入队列 api2Queue.push(transformedData); // 启动队列处理(未启动时) if (!isProcessingQueue) { processQueue(); } // 立即返回响应,避免Webhook超时 res.status(200).send('请求已接收,将排队处理'); } catch (err) { res.status(500).send('处理请求失败'); console.error('处理Webhook请求失败:', err.message); } }); app.listen(3000, () => console.log('服务启动在3000端口'));
方案优势
- 全局队列统一调度,严格控制请求间隔,完全适配目标API的速率限制
- Webhook路由无需等待API2响应,直接返回结果,避免超时
- 可灵活处理发送失败的任务,保证数据不丢失
注意事项
- 应用重启时队列未处理数据会丢失,若需持久化可将队列存储到Redis等数据库
- 可根据API2的限流错误(如429)动态调整间隔或增加重试机制
内容的提问来源于stack exchange,提问作者Sn0
相关产品推荐
相关产品推荐

