http-performance

在这几年使用node.js做后端开发,主要还是开发基于HTTP的相关应用服务,无论是做REST Server还是去调用其它的服务,都是以HTTP为主。在做优化的时候首要的就是对HTTP做相关的优化,而一直以来,我们的原则都是无统计不优化,怎么做好HTTP的相关性能统计则成为了系统的重中之重。

HTTP性能指标

HTTP的统计主要分两种,一种是作为客户端去调用其它服务获取数据,一种是作为服务端,接收到响应请求处理并响应。下面是两者需要关注的统计指标

  • Request

    • 创建Socket

    • 域名解析(如果是IP的不需要)

    • TCP连接

    • TLS连接

    • 后端程序处理生成响应数据

    • 接收响应数据

  • Response(在使用koa做测试时,发现response之后未立即响应close)

    • 创建Socket

    • 后端程序处理生成响应数据并返回

系统现状

在最开始的时候,对于接口的统计,我们都只是粗略的开始与结束的两个位置增加处理函数,生成时长,写入到influxdb中。现有流程中并没有做细化各处理的响应时间,而且在流程中增加代码来处理,每个模块需要各自增加自己的统计,标准不一。那么如果才能做更精准的统计,而且无入侵式的代码呢?

无入侵式的统计

首先先确认需要统计的指标值,对于统计的指标,我主要是参考了chrome的network面板与httpstat生成的统计时长,制定了以下的指标:

  • 响应类型(request, response)

  • dns解析的时间,解析获取的IP

  • 正在处理的请求(request, response分开),便于直观反映当前系统的处理能力

  • method

  • url

  • status code

  • bytes

  • timing

    • socket 创建socket的时长

    • dns 域名解析的时长

    • tcp 创建TCP连接握手时长

    • tls 如果是HTTPS,需要做TLS握手,其时长

    • processing 程序接收请求,处理时长

    • transfer 数据传输时长

    • all 整个HTTP处理时长

最开始的想法是对http模块做改造,在研究http模块的时候,发现可以通过对OutgoingMessage做调整获取得到HTTP处理过程中的socket,而通过监控socket中的相关事件,下面是简要的代码介绍:

// http-performance
function requestStats() {
  const timePoints = {};
  const result = {
    type: 'request',
  };
  statsData.requesting += 1;
  let done = false;
  const complete = () => {
    if (done) {
      return;
    }
    done = true;
    // 从timePoints中获取各流程的时长
  };
  timePoints.start = Date.now();
  this.once('socket', (socket) => {
    timePoints.socket = Date.now();
    // tcp(connect) tls(secureConnect)
    const events = 'connect data secureConnect'.split(' ');
    events.forEach((event) => {
      socket.once(event, () => {
        timePoints[event] = Date.now();
      });
    });
    socket.once('lookup', (err, ip, addressType, host) => {
      timePoints.lookup = Date.now();
      if (!err) {
        result.dns = {
          ip,
          addressType,
          host,
        };
      }
    });
    // if the socket will reuse(keepalive), the free event will be emit
    const endEvents = 'end free'.split(' ');
    endEvents.forEach(event => socket.once(event, complete));
  });
  this.once('close', complete);
}

function WrapOutgoingMessage() {
  OutgoingMessage.call(this);
  if (this.constructor.name === 'ServerResponse') {
    responseStats.apply(this);
    return;
  }
  requestStats.apply(this);
}
util.inherits(WrapOutgoingMessage, OutgoingMessage);


httpOutgoing.OutgoingMessage = WrapOutgoingMessage;

增加好统计监控之后,引入此模块,并将统计写入influxdb

const httpPerf = require('http-performance');
httpPerf.on('stats', (stats) => {
  influx.write('http-performance', stats);
});

之后对数据进行分析,做了下面的一些统计与调整:

  • http requesthttp response的比例大概是3:1,系统大部分接口都依赖于外部的服务,这比例属于正常比例

  • 系统在用户最活跃的时间内,各node.js实例的处理请求数在2000以下,设置监控阀值,如果大于2000则发送监控警报

  • DNS的解析耗时不少,因此使用dnscache模块,减少DNS解析时长

  • 每次请求都需要重新创建socket,而且系统的服务调用都是集中在某几个服务,因此使用agentkeepalive模块,增加连接的重用,提升性能

  • 根据40x, 50x的http status做监控警报

  • 其中一个外部服务的tls时间特别长,分析优化认证过程

结语

对于系统的优化,监控,我们都基于统计数据,而如何高效又无入侵式代码的统计则是我们的优先选择,如果你的系统也苦恼于性能的优化与系统的监控,可以参考本文对系统做调整,在获取到统计数据之后就可有的放矢。

* 在此我又要推荐一次influxdb,真的很好用!

Last updated