Как определить время, которое Node.js тратит на отправку тела ответа HTTP? - PullRequest
7 голосов
/ 27 мая 2020

Моя текущая настройка включает веб-приложение Node.js, использующее Express. js.
Я использую dd-tracer DataDog для измерения времени, которое Node.js тратит на вызовы конкретных методов как часть моего решения APM .

Я хотел бы знать, можно ли измерить часть времени, в течение которого входящий HTTP-запрос занят отправкой данных обратно клиенту в виде тела HTTP-ответа.

Есть ли какие-то подводные камни или неточности, возникающие при попытке сделать этот вид инструментовки?
Кто-нибудь знает, почему по умолчанию это не измеряется клиентскими библиотеками APM?

Ответы [ 4 ]

4 голосов
/ 05 июня 2020

Это зависит от того, хотите ли вы иметь время ответа для каждого из вызовов или хотите ли вы собрать статистику о времени ответа.

Во-первых, чтобы получить время ответа в заголовке ответа для каждого запроса, вы можете использовать пакет времени ответа: https://github.com/expressjs/response-time

Это будет добавить в заголовок ответа значение (по умолчанию X-Response-Time). Это будет время, прошедшее с момента поступления запроса в промежуточное ПО до момента записи заголовков.

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));
});
4 голосов
/ 27 мая 2020

Я хотел бы знать, можно ли измерить часть времени, в течение которого входящий HTTP-запрос занят отправкой данных обратно клиенту в виде тела HTTP-ответа.

Вы можете обернуть вызывает res.write вручную для создания дополнительных промежутков в трассировке запроса. Я бы рекомендовал это только в том случае, если в запросе не так много вызовов метода, а в противном случае я бы рекомендовал вместо этого захватывать только метри c.

В качестве альтернативы профилирование может быть вариантом, который даст вам гораздо больше информации о том, что именно занимает время в вызовах res.write.

Я ищу «глобальное» решение, которое можно интегрировать в приложение Nest. js без инструментальной обработки каждого вызова для перезаписи вручную.

Как описано выше, вы можете просто обернуть res.write непосредственно в начале каждого запроса. Используя трассировщик, это может быть достигнуто следующим образом:

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

Это должно быть сделано до того, как любое другое промежуточное ПО получит возможность записывать данные.

Пример промежуточного программного обеспечения:

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

Есть ли какие-нибудь подводные камни или неточности при попытке сделать этот вид инструментов?

Ничего серьезного, о чем я могу подумать.

Кто-нибудь знает, почему это не измеряется клиентскими библиотеками APM по умолчанию?

Основная проблема при выполнении этого из коробки заключается в том, что создание диапазона для каждого звонок на res.write может быть дорогим, если звонков слишком много. Если вы считаете, что имеет смысл иметь возможность сделать это из коробки, мы определенно можем рассмотреть возможность добавления этого.

Надеюсь, это поможет!

3 голосов
/ 04 июня 2020

Прежде всего, я заявляю, что не знаю dd-tracer, но я могу попытаться предоставить способ получения запрошенного времени, тогда разработчик должен использовать его по мере необходимости.

Основная неточность, которая приходит мне на ум, заключается в том, что каждая ОС имеет свой собственный стек TCP, а запись в сокет TCP является буферизованной операцией: для тела ответа, меньшего, чем буфер стека TCP ОС, мы, вероятно, собираемся измерить время, близкое к 0; на результат, который мы имеем, кроме того, влияет Node.js событие l oop load. Чем больше становится тело ответа, тем больше время, связанное с загрузкой события l oop, становится незначительным. Итак, если мы хотим измерить время записи для всех запросов только для одной точки, но мы будем проводить анализ только для длительных запросов, я думаю, что измерение будет достаточно точным.

Другой возможный источником неточности является то, как обработчики запросов записывают свои выходные данные: если обработчик запросов записывает часть тела, затем выполняет длительную операцию для вычисления последней части тела, затем записывает недостающую часть тела, на измеренное время влияет длительная вычислительная работа; мы должны позаботиться о том, чтобы все обработчики запросов записывали заголовки и тело сразу.

Мое предложение решения (которое работает только в том случае, если сервер не реализует функцию 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();
});

Надеюсь, это поможет.

1 голос
/ 03 июня 2020

Вы можете запустить таймер до res.end, а затем любой код после res.end должен выполняться после его завершения, поэтому остановите таймер после функции res.end. Не цитируйте меня по этому поводу.

...