nodejsからHTTPサーバへ通信する場合はConnectionPoolを使おう


はじめに

最近ちらほらnodejsを使っているのですが、
下図の(※1)のようにnodejsのWebサーバが外部のAPIサーバにHTTPでアクセスするときに ConnectionPool を有効にするとレイテンシーやスループットが改善するので検討してみると良いですよ、という話です。この方法はnodejsに限らず有効なのですが、nodejsだととても簡単にできます(たぶん)。

Version

  • nodejs: v4.2.3
  • npm
    • request: 2.67.0
    • request-promise: 1.0.2

ConnectionPoolの方法

方法はいくつかあるようです。

  1. request NPM の forever を利用する
  2. nodejsの httpモジュールに備わっている機能を利用する

request NPM の forever を利用する

request NPM を利用した request-promise というNPMがあります。

例えば、expressで以下のような感じで使います。 これは GET /spikehttps://example.com/にアクセスしてその内容を返すというものです。

app.js
rp = require('request-promise');

app.get('/spike', (req, res) => {
  rp({url: 'https://example.com', forever: true}).then((body) => res.send(body));
});

例えばこれに ab などでベンチマークを取ると、 forever: true を付けるのと付けないのでかなりパフォーマンスが変わります。(注意:実際に example.com に高負荷アクセスするのは迷惑なのでやめましょう)

ConnectionPool されているかどう確認するか

netstat -nt | grep -c <target ip>:<443 or 80>

などでtarget ipとのTCP接続の状況(数)が見ることができます。
ConnectionPool されてないときは、これが時間とともに増加していきます。また、 TIME_WAITという接続終了したConnectionが大量に表示されます。

ConnectionPool されているときはおよそ並列数程度で収まります。 ESTABLISHEDという接続中であるConnectionが見て取れます。

どれくらい効果があるか

あくまで参考ですが、200 request/sec くらいから 900 request/sec くらいになりました。
これは新規にConnectionを張るコストがどれくらい高いかによって変わってきます。

nodejsの httpモジュールに備わっている機能を利用する

結論からいうとちゃんと試していません。

request npm でも デフォルトでこのOptionを使うと書いてあるのですが、私が試した時は効果がありませんでした。

自前で httpモジュールを直接使うとちゃんと機能するのかもしれません。
こんな感じで使っていくようです。

var http = require('http');
var keepAliveAgent = new http.Agent({ keepAlive: true });
options.agent = keepAliveAgent;
http.request(options, onResponseCallback);

さいごに

最近、Micro Service的にこういう連携が増えてきています。
APIサーバ側のkeep-aliveやConnection Poolingを調整すると性能的に改善することはよくあるので、検討すると良いと思います。