2015-07-04

[Node.js] http client がリクエスト6回目以後ハングする

Node.js v0.10.x の http.globalAgent は、keepAlive = true, maxSockets = 5 になっているので、同一 Origin に対して5コネクション全てを腐らせるとハングします。

具体的には 公式ドキュメントの http.get のサンプルコード そのままで、複数回のリクエストを行うようにすると6回目以降必ずハングします。


var http = require('http');

setInterval(function () {
    http.get("http://www.google.com/index.html", function(res) {
        console.log("Got response: " + res.statusCode);
        // res.resume(); すれば OK
    }).on('error', function(e) {
        console.log("Got error: " + e.message);
    }); 
}, 1000);

この事情に関しては、Class: http.ClientRequest の章に丁寧に記述されているとおり、コールバック関数で受ける res は paused readable stream で、on data で内容を読むか、resume で捨てるかどちらかを必ず行う必要があります。

この状況の詳細なログを見るには、以下のように環境変数 NODE_DEBUG に http を入れるとデバッグログが出力されます。

$ node --version
v0.10.39

$ NODE_DEBUG=http node foobar.js

# 正しい場合の出力
HTTP: outgoing message end.
HTTP: AGENT incoming response!
HTTP: AGENT isHeadResponse false
Got response: 302
HTTP: AGENT socket keep-alive
HTTP: outgoing message end.
HTTP: AGENT incoming response!
HTTP: AGENT isHeadResponse false
Got response: 302
HTTP: AGENT socket keep-alive
HTTP: outgoing message end.
HTTP: AGENT incoming response!
HTTP: AGENT isHeadResponse false
Got response: 302
HTTP: AGENT socket keep-alive
HTTP: outgoing message end.
HTTP: AGENT incoming response!
HTTP: AGENT isHeadResponse false
Got response: 302
HTTP: AGENT socket keep-alive

# ダメな場合の出力
HTTP: outgoing message end.
HTTP: AGENT incoming response!
HTTP: AGENT isHeadResponse false
Got response: 302
HTTP: outgoing message end.
HTTP: AGENT incoming response!
HTTP: AGENT isHeadResponse false
Got response: 302
HTTP: outgoing message end.
HTTP: AGENT incoming response!
HTTP: AGENT isHeadResponse false
Got response: 302
HTTP: outgoing message end.
HTTP: AGENT incoming response!
HTTP: AGENT isHeadResponse false
Got response: 302
HTTP: outgoing message end.
HTTP: AGENT incoming response!
HTTP: AGENT isHeadResponse false
Got response: 302
HTTP: outgoing message end.
HTTP: outgoing message end.
HTTP: outgoing message end.
HTTP: outgoing message end.
HTTP: outgoing message end.
HTTP: outgoing message end.
HTTP: outgoing message end.

サンプルコードが間違っているという凶悪な状況だったので、この PR で修正してもらいましたが、今のドキュメントには反映されていないようなのでメモしておきました。

[raspberrypi] mackerel-agent を簡単に導入する

Raspbian を使っている場合、こちらの公式 GitHub mackerel-agent - Latest release
にて、以下のファイルをダウンロードします。バージョンは 2015/07/04 時点のものです。
mackerel-agent_0.17.1-1_all.deb
mackerel-agent_linux_arm.tar.gz

deb パッケージをインストールします

sudo dpkg -i mackerel-agent_0.17.1-1_all.deb

この状態だと設定ファイル類は正しく存在するのですが肝心のバイナリが i386 になっていて正常に動作しません。

$ file /usr/local/bin/mackerel-agent
/usr/local/bin/mackerel-agent: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped

この状態で起動しようとしたとき、/var/log/mackerel-agent.log には以下のような出力がありました。

/usr/local/bin/mackerel-agent: 1: /usr/local/bin/mackerel-agent: <E0>^G^H4<F4>4: not found
/usr/local/bin/mackerel-agent: 1: /usr/local/bin/mackerel-agent: ^?ELF^A^A^A^B^C^A<96>^A@<E0>^B^F^PQ<E5>td^F^D<80>^U^De*^D^A^A^F
<8C>^D^H^LP<84>: not found
/usr/local/bin/mackerel-agent: 2: /usr/local/bin/mackerel-agent: Syntax error: word unexpected (expecting ")")

arm のバイナリに上書きします。

$ tar zxf mackerel-agent_linux_arm.tar.gz
$ sudo cp mackerel-agent_linux_arm/mackerel-agent /usr/local/bin
$ file /usr/local/bin/mackerel-agent
/usr/local/bin/mackerel-agent: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, not stripped

これで /etc/mackerel-agent/mackerel-agent.conf に apikey を設定すれば動作するはずです。

各種ファイルがある場所

起動スクリプト /etc/init.d/mackerel-agent
設定ファイル /etc/mackerel-agent/mackerel-agent.conf
ログ /var/log/mackerel-agent.log

リンク
Mackerel(マカレル): 新世代のクラウドパフォーマンス管理・監視ツール