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

NodeJS中如何在主fetch函数外预建立并维持HTTP长连接?

如何预建立undici的TCP/HTTP连接以避免首次请求的连接开销

我使用undici fetch库编写了如下fetch函数:

async function call() {
        return await fetch('http://127.0.0.1:3000/', {
            dispatcher: new Agent({
                keepAliveTimeout: 100000000,
                keepAliveMaxTimeout: 100000000
              }),
            method: 'POST',
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            },
    
            body: JSON.stringify({
                'a': 2,
                'b': '0x',
                'data': `${Data}`,
                'limit': 100,
                'loops': 1,
                'value': '0'
                
            })
            
            
        }).then((response) => response.json())
        .then((responseData) => {
            //console.log(responseData);
            return responseData;
        })
        .catch(error => console.warn(error));
        
        
    }; 

我运行的是高时效性应用,需要实现延迟低于1ms的POST请求,API服务器部署在本地。
经排查发现每次调用call函数都会新建TCP/HTTP连接,这是性能瓶颈,因此我添加了dispatcher参数配置keepalive,在undici fetch中似乎能生效。
由于首次请求会建立TCP连接,我编写了一个与call函数几乎一致的独立函数,用于在调用call()前预建立连接。
但我无法通过这种方式维持连接,只有多次调用call函数后,才能消除新建连接的开销。
请问如何在调用call()函数前预建立TCP/HTTP连接,避免首次调用时新建连接?

解决方案

核心问题分析

你当前的代码每次调用call函数都会新建一个Agent实例,每个Agent维护独立的连接池。预热函数用的是另一个Agent的连接池,和call函数的Agent完全无关,所以无法复用预热的连接。

具体解决步骤

1. 全局复用同一个Agent实例

将Agent实例从call函数中提取出来,全局创建一次,所有请求共用这个实例的连接池。

2. 编写正确的预热函数

使用同一个全局Agent发起预热请求,确保连接被存入连接池,后续call请求可以直接复用。

3. 优化Agent配置(针对本地场景)

调整连接池相关参数,适配本地高时效需求。

修改后的代码示例

const { Agent, fetch } = require('undici');

// 全局复用的Agent实例,配置长连接与连接池参数
const keepAliveAgent = new Agent({
  keepAliveTimeout: 100000000,
  keepAliveMaxTimeout: 100000000,
  maxConnections: 10, // 本地服务可按需增大,确保有足够的空闲连接
  connectTimeout: 100, // 本地连接超时设置为短时间
  tls: false // 本地服务如果不用HTTPS,可显式关闭减少开销
});

// 预热连接函数:用同一个Agent发起轻量请求,预建立连接
async function prewarmConnection() {
  try {
    // 优先使用OPTIONS请求(如果服务器支持),比POST更轻量
    const response = await fetch('http://127.0.0.1:3000/', {
      dispatcher: keepAliveAgent,
      method: 'OPTIONS'
    });
    // 读取响应内容,确保连接被正确加入连接池(避免undici主动关闭未完成的连接)
    await response.text();
    console.log('连接预热完成');
  } catch (error) {
    // 如果服务器不支持OPTIONS,改用最小合法的POST请求
    try {
      const response = await fetch('http://127.0.0.1:3000/', {
        dispatcher: keepAliveAgent,
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ a: 0, b: '', data: '', limit: 0, loops: 0, value: '0' })
      });
      await response.text();
      console.log('连接预热完成(使用POST fallback)');
    } catch (fallbackError) {
      console.warn('预热连接失败:', fallbackError);
    }
  }
}

// 修改后的call函数:使用全局复用的Agent
async function call(Data) {
  try {
    const response = await fetch('http://127.0.0.1:3000/', {
      dispatcher: keepAliveAgent,
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        'a': 2,
        'b': '0x',
        'data': `${Data}`,
        'limit': 100,
        'loops': 1,
        'value': '0'
      })
    });
    return await response.json();
  } catch (error) {
    console.warn(error);
    throw error; // 建议抛出错误,让调用方处理,避免吞掉异常影响业务逻辑
  }
}

使用方式

  1. 在应用启动后、首次调用call函数之前,先执行prewarmConnection(),等待预热完成。
  2. 后续所有call请求都会复用预热好的TCP连接,彻底消除首次请求的连接建立开销。

补充说明

  • 本地场景下,TCP连接建立本身耗时极短,但复用连接可以完全避免三次握手+TCP慢启动的开销,确保首次请求延迟稳定低于1ms。
  • 若长期没有请求,keepAliveTimeout配置会让连接保持存活,避免再次建立连接的开销。

内容的提问来源于stack exchange,提问作者Optimus Servers

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 22:15:30