はじめに
最近ちらほら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の方法
方法はいくつかあるようです。
request NPM の forever を利用する
request
NPM を利用した request-promise というNPMがあります。
例えば、expressで以下のような感じで使います。 これは GET /spike
で https://example.com/
にアクセスしてその内容を返すというものです。
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を調整すると性能的に改善することはよくあるので、検討すると良いと思います。