You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用不同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:

  1. 更新限流中间件的IP获取逻辑:
// 优先读取自定义请求头用于测试,生产环境保留req.ip逻辑
const clientIp = req.headers['x-client-ip'] || req.ip;
if (!cash[clientIp]) cash[clientIp] = { count: 0, time: [] };
  1. 简化测试脚本,直接在请求头中传入模拟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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 22:45:32