如何确定发送 HTTP 响应正文所花费的时间 Node.js?

How to determine the time Node.js spends to send an HTTP response body?

我当前的设置涉及使用 Express.js 的 Node.js 网络应用程序。
作为我的 APM 解决方案的一部分,我正在使用 DataDog 的 dd-tracer 来测量 Node.js 花费在特定方法调用上的时间。

我想知道是否可以测量传入 HTTP 请求忙于将数据作为 HTTP 响应正文发送回客户端的时间部分。

尝试进行此类检测时是否存在任何陷阱或不准确之处?
有人知道为什么默认情况下 APM 客户端库不测量这个吗?

I would like to know if it is possible to measure the portion of time an incoming HTTP request is busy sending data back to the client as HTTP response body.

您可以手动包装对 res.write 的调用,以在请求跟踪中创建额外的跨度。我只会在请求中对该方法的调用不多时才推荐这样做,否则我会建议只捕获一个指标。

或者,分析可能是一个选项,它可以为您提供更多关于 res.write 调用中究竟花费时间的信息。

I look for a "global" solution which can be integrated into a Nest.js application without instrumenting each call to res.write manually.

如上所述,您可以直接在每个请求的开头简单地包装 res.write。使用示踪剂,可以这样实现:

res.write = tracer.wrap('http.write', res.write)

这应该在任何其他中间件有机会写入数据之前完成。

示例中间件:

app.use((req, res) => {
  res.write = tracer.wrap('http.write', res.write)
})

Are there any pitfalls or inaccuracies involved when trying to do this kind of instrumentation?

我能想到的没什么大不了的。

Does anybody know why this is not measured by APM client libraries by default?

开箱即用的主要问题是,如果调用太多,为每个对 res.write 的调用创建跨度可能会很昂贵。如果您认为开箱即用的选项有意义,我们绝对可以考虑添加它。

希望对您有所帮助!

您可以在 res.end 之前启动计时器,然后 res.end 之后的任何代码都应该在完成后 运行 因此在 res.end 函数之后停止计时器。不要引用我的话。

首先我声明我不知道dd-tracer,但我可以尝试提供一种获取请求时间的方法,然后由开发人员根据需要使用它。

我想到的主要错误是每个 OS 都有自己的 TCP 堆栈,并且在 TCP 套接字上写入是一个缓冲操作:对于小于 OS TCP 堆栈缓冲区的响应主体,我们可能会测量接近 0 的时间;我们得到的结果还受到 Node.js 事件循环负载的影响。响应 body 越大,与事件循环负载相关的时间就越可以忽略不计。所以,如果我们想测量所有请求的写入时间只有一个点,但我们只对长时间的请求进行分析,我认为测量会非常准确。

另一个可能的不准确来源是请求处理程序如何写入它们的输出:如果请求处理程序写入 body 的一部分,然后执行长时间的操作来计算 body 的最后一部分, 然后写入 body 的缺失部分,测量时间受长时间计算操作的影响;我们应该注意所有请求处理程序都同时写入 headers 和 body。

我的解决方案(只有在服务器没有实现keep alive的情况下才有效)是添加这样的中间件。

app.use((req, res, next) => {
    let start;
    const { write } = res.socket;

    // Wrap only first write call
    // Do not use arrow function to get access to arguments
    res.socket.write = function() {
        // Immediately restore write property to not wrap next calls
        res.socket.write = write;

        // Take the start time
        start = new Date().getTime();

        // Actually call first write
        write.apply(res.socket, arguments);
    };

    res.socket.on("close", () => {
        // Take the elapsed time in result
        const result = new Date().getTime() - start;

        // Handle the result as needed
        console.log("elapsed", result);
    });

    next();
});

希望对您有所帮助。

这取决于您是想知道每个呼叫的响应时间,还是想收集有关响应时间的统计信息。

首先,要在每个请求的响应的 header 中获取响应时间,您可以使用 response-time 包:https://github.com/expressjs/response-time

这将向响应 header 添加一个值(默认为 X-Response-Time)。那将是从请求进入中间件到写出 header 所经过的时间。

var express = require('express')
var responseTime = require('response-time')

var app = express()

app.use(responseTime())

app.get('/', function (req, res) {
  res.send('hello, world!')
})
  • 如果您想要更完整的解决方案并收集包括响应时间在内的统计数据,您可以使用

express-node-metrics 包裹

https://www.npmjs.com/package/express-node-metrics

var metricsMiddleware = require('express-node-metrics').middleware;
app.use(metricsMiddleware);

app.get('/users', function(req, res, next) {
    //Do Something
})
app.listen(3000);

您可以像这样公开和访问此统计信息:

'use strict'
var express = require("express");
var router = express.Router();
var metrics = require('express-node-metrics').metrics;

router.get('/', function (req, res) {
    res.send(metrics.getAll(req.query.reset));
});
router.get('/process', function (req, res) {
    res.send(metrics.processMetrics(req.query.reset));
});
router.get('/internal', function (req, res) {
    res.send(metrics.internalMetrics(req.query.reset));
});
router.get('/api', function (req, res) {
    res.send(metrics.apiMetrics(req.query.reset));
});