超时控制主要涉及服务器端连接超时客户端请求超时两大场景

一、服务器端超时设置(http.Server 相关)

服务器端的超时主要针对客户端连接:当客户端与服务器建立 TCP 连接后,如果长时间没有数据交互(如客户端未发送请求、或请求未完成),服务器可以主动断开连接,释放资源。

1. server.setTimeout(msecs[, callback])
  • 作用:设置客户端连接的超时时间(从连接建立到数据交互的最大间隔)。
  • 参数
    • msecs:超时时间(毫秒),默认值为 5000(5 秒)。
    • callback(可选):超时事件的回调函数,等同于监听 'timeout' 事件。
  • 行为:当连接超时后,会触发服务器的 'timeout' 事件,同时底层 TCP 套接字(socket)会被标记为超时状态。
  • 注意:超时后不会自动关闭连接,需手动调用 socket.destroy() 关闭,否则连接会保持。
2. 'timeout' 事件(服务器端)
  • 触发时机:客户端连接超时(超过 setTimeout 设置的时间)时触发。
  • 参数
    • socket:超时的 TCP 套接字对象(net.Socket 实例)。
  • 用途:通常在事件回调中手动关闭超时连接,避免资源泄漏。

示例:服务器端设置连接超时

const http = require('http');

const server = http.createServer((req, res) => {
  // 模拟耗时处理(若客户端 3 秒内未发送完整请求,会触发超时)
  setTimeout(() => {
    res.end('处理完成');
  }, 4000);
});

// 设置超时时间为 3 秒(3000 毫秒)
server.setTimeout(3000, (socket) => {
  console.log('连接超时(通过 callback 处理)');
  // 手动关闭超时连接
  socket.destroy();
});

// 或通过 'timeout' 事件处理
server.on('timeout', (socket) => {
  console.log('连接超时(通过事件监听处理)');
  socket.destroy(); // 关闭连接,避免资源占用
});

server.listen(3000, () => {
  console.log('服务器运行在 http://localhost:3000');
});

说明

  • 若客户端在 3 秒内未完成请求(如仅建立连接但未发送数据),服务器会触发 'timeout' 事件,调用 socket.destroy() 强制关闭连接。
  • 超时时间应根据业务场景设置(如 API 服务可设 5-10 秒,文件上传可设更长时间)。

二、客户端超时设置(http.ClientRequest 相关)

客户端的超时主要针对请求过程:当客户端发送请求后,若服务器长时间未响应(无数据返回),客户端可以主动终止请求,避免无限等待。

1. req.setTimeout(msecs[, callback])
  • 作用:设置客户端请求的超时时间(从请求发送到收到响应的最大间隔)。
  • 参数
    • msecs:超时时间(毫秒),无默认值(需手动设置,否则可能无限等待)。
    • callback(可选):超时事件的回调函数,等同于监听 'timeout' 事件。
  • 行为:当请求超时后,会触发 'timeout' 事件,但不会自动终止请求,需手动调用 req.abort() 或 req.destroy() 终止。
2. 'timeout' 事件(客户端)
  • 触发时机:请求超时(超过 setTimeout 设置的时间)时触发。
  • 用途:在事件回调中终止请求,释放本地资源。

示例:客户端设置请求超时

const http = require('http');

const options = {
  hostname: 'localhost',
  port: 3000,
  path: '/slow', // 假设该接口会延迟 5 秒响应
  method: 'GET'
};

const req = http.request(options, (res) => {
  let data = '';
  res.on('data', (chunk) => { data += chunk; });
  res.on('end', () => {
    console.log('响应内容:', data);
  });
});

// 设置请求超时时间为 3 秒(若 3 秒内无响应,则触发超时)
req.setTimeout(3000, () => {
  console.log('请求超时,终止请求');
  req.abort(); // 终止请求(会触发 'abort' 事件)
});

// 处理超时后的错误
req.on('error', (err) => {
  if (err.code === 'ECONNRESET') {
    console.log('请求被主动终止(超时)');
  } else {
    console.error('请求错误:', err);
  }
});

req.end();

说明

  • 若服务器 3 秒内未返回响应,客户端触发 'timeout' 事件,调用 req.abort() 终止请求,避免无限等待。
  • 终止后会触发 'error' 事件,错误码为 ECONNRESET(连接被重置),需在事件中处理。

三、底层套接字(net.Socket)的超时补充

无论是服务器端的 socket(来自 'connection' 事件)还是客户端请求的 socket(可通过 req.socket 访问),其底层都是 net.Socket 实例,也提供超时控制:

  • socket.setTimeout(msecs[, callback]):设置套接字的超时时间(与 http.Server 或 http.ClientRequest 的超时类似,但更底层)。
  • socket.on('timeout', () => { ... }):套接字超时事件,通常无需单独设置,因为 http 模块已封装上层超时。

注意http 模块的超时 API(server.setTimeoutreq.setTimeout)本质上是对 net.Socket 超时的封装,优先使用上层 API 更符合 HTTP 场景。


四、超时 API 对比与最佳实践

场景 核心 API 超时触发条件 处理方式
服务器端 server.setTimeout() 客户端连接后长时间无数据交互 调用 socket.destroy() 关闭连接
客户端 req.setTimeout() 发送请求后长时间未收到服务器响应 调用 req.abort() 终止请求

最佳实践

  1. 服务器端:根据业务设置合理超时(如普通接口 5-10 秒,大文件上传 60 秒以上),超时后必须关闭 socket
  2. 客户端:强制设置超时(如 10-30 秒),避免因网络异常导致请求永久挂起,超时后需终止请求并处理错误。
  3. 区分场景:服务器超时针对 “客户端不活跃”,客户端超时针对 “服务器无响应”,二者需分别设置。
1、服务器端超时设置(防止客户端连接长期无活动)

服务器需要限制客户端连接的最大空闲时间,避免资源浪费。

const http = require('http');

// 创建服务器
const server = http.createServer((req, res) => {
  // 模拟处理耗时请求(例如:5秒后响应)
  setTimeout(() => {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('请求处理完成\n');
  }, 5000);
});

// 1. 设置服务器连接超时时间(3秒)
// 超过3秒无数据交互则触发超时
server.setTimeout(3000);

// 2. 监听超时事件,主动关闭无效连接
server.on('timeout', (socket) => {
  console.log('客户端连接超时,即将关闭连接');
  // 关闭底层TCP连接(必须手动调用,否则连接会保持)
  socket.destroy(new Error('连接超时:服务器已关闭长时间无活动的连接'));
});

// 3. 监听连接错误(如超时后关闭连接的错误)
server.on('connection', (socket) => {
  socket.on('error', (err) => {
    console.log('连接错误:', err.message);
  });
});

// 启动服务器
server.listen(3000, () => {
  console.log('服务器运行在 http://localhost:3000');
  console.log('连接超时设置:3秒(3000ms)');
});

测试方式
用 curl http://localhost:3000 发起请求,由于服务器处理需要 5 秒,超过 3 秒超时设置,会触发超时并关闭连接,客户端会收到错误。

2、客户端请求超时设置(防止服务器无响应)

客户端需要限制请求的最大等待时间,避免因服务器故障导致无限等待。

const http = require('http');

// 向服务器发送请求(假设服务器处理耗时较长)
const options = {
  hostname: 'localhost',
  port: 3000,
  path: '/',
  method: 'GET'
};

const req = http.request(options, (res) => {
  console.log(`收到响应,状态码:${res.statusCode}`);
  
  let data = '';
  res.on('data', (chunk) => { data += chunk; });
  res.on('end', () => {
    console.log('响应内容:', data);
  });
});

// 1. 设置客户端请求超时时间(4秒)
// 若4秒内未收到服务器响应,则触发超时
req.setTimeout(4000);

// 2. 监听超时事件,主动终止请求
req.on('timeout', () => {
  console.log('请求超时(4秒未收到响应),终止请求');
  // 终止请求(会触发后续的error事件)
  req.abort();
});

// 3. 处理请求错误(包括超时导致的错误)
req.on('error', (err) => {
  if (err.code === 'ECONNRESET') {
    console.log('请求已被主动终止(超时)');
  } else {
    console.error('请求错误:', err.message);
  }
});

// 发送请求
req.end();

测试方式
配合上面的服务器代码运行,服务器处理需要 5 秒,客户端设置 4 秒超时,会触发超时并终止请求,输出 “请求超时” 信息。

3、精细化控制:按请求类型设置不同超时

实际业务中,不同接口(如普通接口、文件上传)可能需要不同的超时时间,可在请求处理函数中动态设置。

const http = require('http');

const server = http.createServer((req, res) => {
  // 1. 按请求路径动态设置超时
  if (req.url === '/upload' && req.method === 'POST') {
    // 文件上传允许更长超时(30秒)
    req.socket.setTimeout(30000); 
    console.log('文件上传接口超时设置:30秒');
  } else {
    // 普通接口超时(5秒)
    req.socket.setTimeout(5000);
    console.log('普通接口超时设置:5秒');
  }

  // 2. 监听当前请求的超时事件
  req.socket.on('timeout', () => {
    console.log(`[${req.url}] 请求超时`);
    res.writeHead(408, { 'Content-Type': 'text/plain' }); // 408 Request Timeout
    res.end('请求超时:服务器未在规定时间内收到完整请求\n');
    req.socket.destroy(); // 关闭连接
  });

  // 模拟处理请求
  setTimeout(() => {
    res.end(`[${req.url}] 处理完成\n`);
  }, 6000); // 模拟6秒处理时间(会触发普通接口超时)
});

server.listen(3000, () => {
  console.log('服务器运行在 http://localhost:3000');
  console.log('测试:GET / (5秒超时),POST /upload (30秒超时)');
});

测试方式

  • 访问 http://localhost:3000(普通接口):6 秒处理时间超过 5 秒超时,会返回 408 状态码。
  • 发送 POST 请求到 http://localhost:3000/upload:6 秒处理时间未超过 30 秒超时,会正常响应。
4、HTTPS 场景的超时设置(与 HTTP 类似)

https 模块的超时 API 与 http 完全一致,只需替换模块引入即可。

const https = require('https');
const fs = require('fs');

// 证书配置(实际使用时替换为真实证书)
const options = {
  key: fs.readFileSync('server.key'),
  cert: fs.readFileSync('server.crt')
};

// 创建HTTPS服务器,超时设置与HTTP相同
const server = https.createServer(options, (req, res) => {
  res.end('HTTPS 响应\n');
});

// 设置超时(5秒)
server.setTimeout(5000);
server.on('timeout', (socket) => {
  console.log('HTTPS连接超时');
  socket.destroy();
});

server.listen(443, () => {
  console.log('HTTPS服务器运行在 https://localhost');
});
核心总结
  1. 服务器端:通过 server.setTimeout() 或 req.socket.setTimeout() 设置超时,超时后需调用 socket.destroy() 关闭连接。
  2. 客户端:通过 req.setTimeout() 设置超时,超时后调用 req.abort() 终止请求,并在 'error' 事件中处理 ECONNRESET 错误。
  3. 灵活性:可根据请求类型(如接口、文件上传)动态调整超时时间,平衡用户体验与资源利用率。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐