You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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参数:根据你的请求频率和后端能力调整maxSocketsmaxFreeSockets等参数,避免连接过多压垮后端,也不要太少导致请求排队。
  • 第三方代理库适配:如果用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

火山引擎 最新活动