如何用不同IP测试Node.js网关限流及解决Axios自定义IP报错
问题:Node.js网关限流功能的本地多IP测试问题
背景
我正在学习并基于Node.js实现网关,重点自研基于IP地址统计请求次数的**rate limiting(限流)**功能。为验证功能正确性,需在本地发起multiple requests with different IP addresses(多不同IP请求)进行压力测试。
限流示例代码(非最终版本)
const cash = {}; const limit = 50; const rateLimiter = async (req, res, next) => { if (!cash[req.ip]) cash[req.ip] = { count: 0, time: [] }; if (cash[req.ip].count >= limit) { if (Date.now() - cash[req.ip].time[0] < 60000) { return res.send("Limit reached"); } else { for (const t of cash[req.ip].time) { if (Date.now() - t > 60000) break; cash[req.ip].time.shift(); // Remove the oldest entry cash[req.ip].count--; } } } cash[req.ip].count++; cash[req.ip].time.push(Date.now()); next(); };
测试脚本(尝试自定义请求IP)
const axios = require("axios"); const http = require("http"); function getRandomPrivateIP() { const octet3 = Math.floor(Math.random() * 256); const octet4 = Math.floor(Math.random() * 256); return `192.168.${octet3}.${octet4}`; } function createAxiosInstanceWithIP(ipAddress) { const agent = new http.Agent({ localAddress: ipAddress, }); return axios.create({ httpAgent: agent, httpsAgent: agent }); } const url = "http://localhost:8080/gateway"; const numberOfRequests = 1000; for (let i = 0; i < numberOfRequests; i++) { const customIPAddress = getRandomPrivateIP(); // Invoke the function const axiosInstance = createAxiosInstanceWithIP(customIPAddress); axiosInstance .get(url) .then((response) => { // Handle the response console.log(`Request ${i + 1} successful`); }) .catch((error) => { // Handle errors console.error(`Request ${i + 1} failed: ${error.message}`); }); }
报错信息
Request 1000 failed: bind EINVAL 192.168.72.253
需求
希望在脚本内自定义请求IP完成测试,已知Apache服务器及专用VPS基准测试方案,但更倾向于脚本内实现。需指出错误原因并提供优化思路。
解决方案
错误原因分析
localAddress参数限制:http.Agent的localAddress要求指定的IP必须是本地网卡已配置的真实IP,随机生成的未绑定IP无法被系统识别,因此抛出bind EINVAL绑定无效错误。- 随机IP逻辑缺陷:生成的IP可能包含无效地址(如192.168.0.0网络地址、192.168.255.255广播地址),但核心问题仍是IP未在本地配置。
优化思路
方案1:修改网关IP获取逻辑(推荐)
无需修改本地网络,通过自定义请求头模拟不同客户端IP:
- 更新限流中间件的IP获取逻辑:
// 优先读取自定义请求头用于测试,生产环境保留req.ip逻辑 const clientIp = req.headers['x-client-ip'] || req.ip; if (!cash[clientIp]) cash[clientIp] = { count: 0, time: [] };
- 简化测试脚本,直接在请求头中传入模拟IP:
const url = "http://localhost:8080/gateway"; const numberOfRequests = 1000; for (let i = 0; i < numberOfRequests; i++) { const customIPAddress = getRandomPrivateIP(); axios.get(url, { headers: { 'X-Client-IP': customIPAddress } }) .then((response) => { console.log(`Request ${i + 1} successful, IP: ${customIPAddress}`); }) .catch((error) => { console.error(`Request ${i + 1} failed (IP: ${customIPAddress}): ${error.message}`); }); }
这种方式零网络配置,可快速验证限流逻辑的正确性。
方案2:本地配置虚拟IP(真实多IP场景)
若需模拟真实出口IP,可给本地网卡添加多个虚拟IP:
- Windows:在网卡IPv4设置中添加多个192.168段IP
- Linux/macOS:执行命令添加,如
ifconfig en0 alias 192.168.1.100 netmask 255.255.255.0
修改测试脚本的IP生成逻辑,从已配置的IP列表中随机选取:
// 本地已配置的虚拟IP列表 const availableIPs = [ '192.168.1.100', '192.168.1.101', '192.168.1.102' ]; function getRandomLocalIP() { return availableIPs[Math.floor(Math.random() * availableIPs.length)]; }
限流代码额外优化建议
- 内存泄漏防护:定期清理超过1小时的IP请求记录,避免
cash对象无限膨胀 - 滑动窗口效率优化:用索引标记有效时间范围替代
shift()遍历,提升性能 - 异步逻辑简化:中间件无异步操作,可移除
async关键字,避免不必要的Promise包装
内容的提问来源于stack exchange,提问作者Aditya
相关产品推荐
相关产品推荐

