NodeJS代理服务中跨多客户端请求复用TCP连接的实现方法
嘿,这个需求太贴合性能优化的实际场景了——复用TCP连接确实是提升代理效率的关键,尤其是短时间内有大量客户端请求的情况。针对Node.js 12 + Express的环境,咱们可以通过配置HTTP/HTTPS Agent来实现连接复用,不管你用原生http模块还是fetch工具,都能搞定,下面给你一步步拆解:
核心逻辑:TCP连接复用的本质
Node.js的http/https模块自带的Agent对象就是用来管理TCP连接池的。默认情况下,Agent已经开启了Keep-Alive,但默认参数可能不够适配你的场景。咱们只需要显式配置Agent的参数,让它在多个请求之间保留活跃的TCP连接,就能实现复用。
方案1:用原生http/https模块实现代理(推荐)
因为Express本身基于Node.js的http模块,直接用原生工具配置最稳妥。
步骤1:创建自定义的Agent
首先定义一个开启Keep-Alive的Agent,参数可以根据后端服务器的能力调整:
const http = require('http'); // 如果后端是HTTPS服务,就换成https模块 // const https = require('https'); // 配置连接池参数 const backendAgent = new http.Agent({ keepAlive: true, // 开启TCP连接复用(关键开关) keepAliveMsecs: 30000, // 连接保持活跃的时长,单位毫秒(比如设30秒) maxSockets: 10, // 允许同时建立的最大连接数,按需调整 maxFreeSockets: 5, // 空闲时保留的最大连接数,避免闲置连接过多 });
步骤2:在Express路由中使用这个Agent
每次代理请求后端B时,指定使用这个自定义Agent,它会自动从连接池中复用空闲连接:
const express = require('express'); const app = express(); app.get('/proxy', (req, res) => { // 发起对后端B的请求,指定复用的Agent const backendReq = http.get({ hostname: 'backend-server-b.com', path: req.url, // 把客户端的请求路径透传给后端B agent: backendAgent, // 核心:用这个Agent管理连接池 }, (backendRes) => { // 将后端响应转发给客户端,同时可以在这里做格式转换 res.writeHead(backendRes.statusCode, backendRes.headers); // 示例:如果需要转换格式,可以先读取数据再处理,比如转JSON后修改结构 // let data = ''; // backendRes.on('data', chunk => data += chunk); // backendRes.on('end', () => res.json(JSON.parse(data))); // 如果不需要转换,直接pipe转发最高效 backendRes.pipe(res); }); // 处理请求错误 backendReq.on('error', (err) => { res.status(500).send(`Proxy error: ${err.message}`); }); }); app.listen(3000, () => { console.log('Proxy server running on port 3000'); });
方案2:用fetch(node-fetch)实现连接复用
如果你的项目习惯用fetch,Node.js 12需要安装node-fetch@2.x(3.x是ESM格式,Node12需要额外配置,2.x更兼容)。
步骤1:安装依赖
npm install node-fetch@2.6.7
步骤2:配置Agent并在fetch中使用
const express = require('express'); const fetch = require('node-fetch'); const http = require('http'); const backendAgent = new http.Agent({ keepAlive: true, keepAliveMsecs: 30000, maxSockets: 10, }); const app = express(); app.get('/proxy', async (req, res) => { try { const backendRes = await fetch(`http://backend-server-b.com${req.url}`, { agent: backendAgent, // 指定复用的Agent method: 'GET', }); // 这里做格式转换,比如把后端返回的数据改成客户端需要的结构 const rawData = await backendRes.json(); const transformedData = { code: 200, data: rawData, msg: 'success' }; res.json(transformedData); } catch (err) { res.status(500).send(`Proxy error: ${err.message}`); } }); app.listen(3000, () => { console.log('Proxy server running on port 3000'); });
注意:如果后端是HTTPS服务,要改用https.Agent,同时fetch的URL改成https开头。
验证连接是否复用
你可以用系统命令查看TCP连接状态,比如在Linux/macOS上:
# 查看与后端B的TCP连接 ss -t | grep backend-server-b.com
当多个客户端请求过来时,你会看到只有少量(甚至1个)ESTABLISHED状态的连接,而不是每个请求都新建一个,这就说明连接复用生效了。
几个注意点
- 后端服务器B必须支持HTTP Keep-Alive:大部分现代HTTP服务器(Nginx、Apache、Node.js自身的http服务)默认都支持,如果后端是老旧服务,可能需要开启Keep-Alive配置。
- 调整Agent参数:根据你的请求频率和后端能力调整
maxSockets、maxFreeSockets等参数,避免连接过多压垮后端,也不要太少导致请求排队。 - 第三方代理库适配:如果用
http-proxy-middleware这类代理库,同样可以通过传入自定义Agent实现连接复用,原理完全一致:
const { createProxyMiddleware } = require('http-proxy-middleware'); app.use('/proxy', createProxyMiddleware({ target: 'http://backend-server-b.com', agent: backendAgent, // 传入自定义Agent changeOrigin: true, }));
内容的提问来源于stack exchange,提问作者Marc




