これが私がやろうとしていることです:私はNode.js httpサーバーを開発しています。これは、単一のマシンで何万ものモバイルクライアントからのプッシュ目的(redisとのコラボレーション)のために長い接続を保持します。
テスト環境:
1.80GHz*2 CPU/2GB RAM/Unbuntu12.04/Node.js 0.8.16
初めて、「express」モジュールを使用しました。これにより、スワップが使用される前に約 120k の同時接続に到達できました。つまり、RAM が十分ではありませんでした。次に、ネイティブの「http」モジュールに切り替えたところ、最大で約 160k の同時実行性が得られました。しかし、ネイティブ http モジュールにはまだ必要のない機能が多すぎることに気付いたので、ネイティブの "net" モジュールに切り替えました (これは、http プロトコルを自分で処理する必要があることを意味しますが、それで問題ありません)。今では、1 台のマシンあたり約 250,000 の同時接続に到達できます。
私のコードの主な構造は次のとおりです。
var net = require('net');
var redis = require('redis');
var pendingClients = {};
var redisClient = redis.createClient(26379, 'localhost');
redisClient.on('message', function (channel, message) {
var client = pendingClients[channel];
if (client) {
client.res.write(message);
}
});
var server = net.createServer(function (socket) {
var buffer = '';
socket.setEncoding('utf-8');
socket.on('data', onData);
function onData(chunk) {
buffer += chunk;
// Parse request data.
// ...
if ('I have got all I need') {
socket.removeListener('data', onData);
var req = {
clientId: 'whatever'
};
var res = new ServerResponse(socket);
server.emit('request', req, res);
}
}
});
server.on('request', function (req, res) {
if (res.socket.destroyed) {
return;
}
pendingClinets[req.clientId] = {
res: res
};
redisClient.subscribe(req.clientId);
res.socket.on('error', function (err) {
console.log(err);
});
res.socket.on('close', function () {
delete pendingClients[req.clientId];
redisClient.unsubscribe(req.clientId);
});
});
server.listen(3000);
function ServerResponse(socket) {
this.socket = socket;
}
ServerResponse.prototype.write = function(data) {
this.socket.write(data);
}
最後に、ここに私の質問があります:
メモリ使用量を減らして同時実行性をさらに高めるにはどうすればよいですか?
Node.js プロセスのメモリ使用量を計算する方法について、私は本当に混乱しています。Chrome V8 を搭載した Node.js を知っています。process.memoryUsage () API があり、rss/heapTotal/heapUsed の 3 つの値を返します。それらの違いは何ですか。どの部分をもっと気にする必要がありますか。 Node.jsプロセスによって使用されるメモリ?
いくつかのテストを行ったにもかかわらず、メモリリークが心配で、問題はないようです。気になる点やアドバイスはありますか?
V8 hidden classに関するドキュメントを見つけました。説明されているように、上記のコードのようにclientIdという名前のプロパティをグローバル オブジェクトpendingClientsに追加すると、新しい隠しクラスが生成されるということですか? それはメモリリークを引き起こしますか?
Node.js プロセスのヒープ マップを分析するためにwebkit-devtools-agentを使用しました。プロセスを開始してヒープ スナップショットを作成し、10k のリクエストを送信して後で切断し、その後再びヒープ スナップショットを作成しました。比較の視点を使用して、これら 2 つのスナップショットの違いを確認しました。これが私が得たものです: 誰かこれを説明できますか?(配列)/(コンパイルされたコード)/(文字列)/コマンド/配列の数とサイズが大幅に増加しましたが、これはどういう意味ですか?
編集:負荷テストはどのように実行しましたか?
1. まず、サーバー マシンとクライアント マシンの両方でいくつかのパラメータを変更しました(60k 以上の同時実行性を実現するには、複数のクライアント マシンが必要です。これは、1 台のマシンが最大 60k+ ポート (16 ビットで表される) しか持たないためです)。<br> 1.1. サーバーとクライアントの両方のマシンで、ファイル記述子を変更して、テスト プログラムが実行されるシェルでこれらのコマンドを使用します。
ulimit -Hn 999999
ulimit -Sn 999999
1.2. サーバー マシンでは、いくつかの net/tcp 関連のカーネル パラメータも変更しました。最も重要なものは次のとおりです。
net.ipv4.tcp_mem = 786432 1048576 26777216
net.ipv4.tcp_rmem = 4096 16384 33554432
net.ipv4.tcp_wmem = 4096 16384 33554432
1.3。クライアントマシンに関して:
net.ipv4.ip_local_port_range = 1024 65535
2. 次に、Node.js を使用してカスタム シミュレート クライアント プログラムを作成しました。ほとんどの負荷テスト ツール (ab、siege など) は短い接続用ですが、長い接続を使用しており、いくつかの特別な要件があるためです。
3. 次に、1 台のマシンでサーバー プログラムを起動し、別の 3 台のマシンで 3 つのクライアント プログラムを起動しました。
編集:1台のマシン(2GB RAM)で250kの同時接続に到達しましたが、あまり意味がなく実用的ではないことが判明しました。接続が接続されたとき、私は接続を保留にするだけなので、他には何もしません。それらに応答を送信しようとすると、同時実行数が約 150k に落ちました。私が計算したように、接続ごとに約 4KB のメモリ使用量があります。これは、 4096 16384 33554432に設定したnet.ipv4.tcp_wmemに関連していると思いますが、それを小さく変更しても何も変わりませんでした。理由がわかりません。
編集:実際には、tcp 接続ごとに使用されるメモリの量と、単一の接続で使用されるメモリの正確な構成にもっと興味がありますか? 私のテストデータによると:
150k の同時実行で約 1800M の RAM が消費され (無料の -m出力から)、Node.js プロセスには約 600M の RSS がありました。
次に、私はこれを仮定しました:
(1800M - 600M) / 150k = 8k、これは単一接続のカーネル TCP スタック メモリ使用量です。2 つの部分で構成されています: 読み取りバッファー (4KB) + 書き込みバッファー (4KB) (実際には、これは私の設定と一致しません)上記のnet.ipv4.tcp_rmemとnet.ipv4.tcp_wmemについて、システムはこれらのバッファに使用するメモリの量をどのように決定しますか?)
600M / 150k = 4k、これは単一接続の Node.js メモリ使用量です
私は正しいですか?両方の側面でメモリ使用量を減らすにはどうすればよいですか?
うまく説明できていないところがあれば教えてください。説明やアドバイスをいただければ幸いです。