大津です。
これは http.Agent に関する非常に良い課題だと思います。
# 本当は XXXX の YYYY を見てくださいと言いたいところなんですけど、それは
今後の楽しみにしていただけるとありがたいです。
> nodeに興味が出て、勉強のprogramを書いてるところです。
> javascript自体、ecma scriptの仕様書を見ながら書いているレベルなので、node自体の使い方の質問では無く、javascriptの質問になっているかもしれませんが、その際は指摘していただければと思います。
いえJSの問題ではなく Node.js 固有の原因によるものですので安心してください。
> 書いているのは、httpへの負荷をかけるツールです。
> 書いたcodeを質問用に簡略化したのが、
(中略)
> のようなwarningが出ます。
まず Node.js のHTTPクライアントの動作について少し解説が必要かなと思います。
Node では、通常 http.request() で生成したリクエストオブジェクト(req)が
HTTPサーバに接続する際、リクエストオブジェクトが直接 HTTPサーバにソケッ
ト接続するのではなく、http.Agent が替りにソケット接続を行い、ソケットと
リクエストを結びつける管理する役割を担います。
ソケットの最大同時接続数は http.Agent.maxSockets の値が上限で、それを超
えるとリクエストは queue に溜まります。
Agent は接続したソケットをHTTPリクエストに割り当て、HTTPリクエストが終了
すると Agent は既存のソケットを使いまわして他のリクエストに再度割り当て
ます。(HTTP keep-alive が有効になっていることが前提です。)
リクエストの際、特に Agent の指定をしなればデフォルトで globalAgent が利
用され、最大同時接続数は5つに制限されています。(管理単位は
host+port+sourceIP)
上記の知識を前提として以下に回答を書いてみます。
> 疑問なのが
> 1.
> 51行目で、走ると思われる並列数分setMaxListeners()しているのに、それ以上のlistenerを追加しようとしているのはなぜ?
'socket' イベントで渡される socket オブジェクトはデフォルト(globalAgent
利用時)では5つです。毎回 golbalAgent がリクエストに socket を割り当てる
度にその5つに対してリスナ追加をするので setMaxListners() 値を超えてし
まっています。
> 2.
> そもそも、http接続毎に生成されるhttp.ClientRequest用のsocketのconnect eventを、once()でつかまえようとしているのに、複数回のlistener登録が行なわれるのはなぜ?
>
globalAgent 利用時 socket は 'connect' イベントは最初に接続した5回分しか
emit しません。(途中で接続が他の理由で切れない前提ですけど)
なのでいくら once でリスナ登録しても emit されないのでリスナは次々登録さ
れる状況になります。
> 3.
> 結局どう書くのがnode流なの?
うーん、いろいろやり方があります。100個ぐらいなら一気に http.request()
してやればメモリは多少使いますが、 Agent が勝手に接続をしてくれるでしょう。
スケールするよう最大接続数を parallel のカスタムエージェントを作って、レ
スポンス終了毎に順次リクエスト追加するやり方場合を下記 gist に置きました。
https://gist.github.com/2781370
コメントとか書いていませんが、今後の参考にしていただけたらと思います。
(gjslint ベースに書式は変えてます。)