侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

设计一个Express中间件记录HTTP请求响应时间并返回在响应头中

2025-12-12 / 0 评论 / 4 阅读

题目

设计一个Express中间件记录HTTP请求响应时间并返回在响应头中

信息

  • 类型:问答
  • 难度:⭐⭐

考点

中间件机制, HTTP模块, 性能监控, 响应头处理

快速回答

实现步骤:

  1. 创建中间件函数捕获请求开始时间
  2. 监听响应'finish'事件计算持续时间
  3. 将结果写入响应头(如X-Response-Time)
  4. 调用next()传递控制权

关键代码:

app.use((req, res, next) => {
  const start = Date.now();
  res.on('finish', () => {
    const duration = Date.now() - start;
    res.setHeader('X-Response-Time', `${duration}ms`);
  });
  next();
});
## 解析

原理说明

在Node.js的HTTP模块中,当服务器完成响应时会触发'finish'事件。中间件通过以下流程工作:

  1. 请求进入时记录起始时间戳
  2. 挂载'finish'事件监听器到响应对象
  3. 响应结束时计算时间差并写入响应头
  4. 通过next()保证后续中间件执行

代码实现与示例

// 基础实现
app.use((req, res, next) => {
  const start = process.hrtime.bigint(); // 使用高精度时间

  res.on('finish', () => {
    const end = process.hrtime.bigint();
    const duration = Number(end - start) / 1e6; // 纳秒转毫秒
    res.setHeader('X-Response-Time', `${duration.toFixed(2)}ms`);
  });

  next();
});

// 测试路由
app.get('/', (req, res) => {
  setTimeout(() => res.send('Hello'), 100); // 模拟延迟
});

最佳实践

  • 高精度计时:使用process.hrtime替代Date.now获取纳秒级精度
  • 错误处理:添加'close'事件监听处理连接中断场景
    res.on('close', () => {
      // 清理逻辑
    });
  • 性能优化:避免在中间件中执行阻塞操作
  • 标准化头字段:推荐使用X-Response-Time而非自定义名称

常见错误

错误类型后果解决方案
忘记调用next()请求挂起确保所有路径都调用next()
多次设置响应头报'Can't set headers after sent'错误仅在finish/close事件中操作
时间计算不准确负值或异常值使用BigInt防止数值溢出

扩展知识

  • 性能监控集成:将数据推送到Prometheus/Grafana等监控系统
  • 分布式追踪:结合OpenTelemetry实现全链路追踪
    // 添加TraceID示例
    res.setHeader('X-Trace-ID', generateTraceId());
  • 中间件顺序:该中间件应放在所有路由之前但错误处理之后
  • 替代方案:使用现成库如response-time(源码类似本实现)