Node.js v0.11.11 マニュアル & ドキュメンテーション


Cluster#

Stability: 2 - Unstable

一つの Node インスタンスは一つのスレッドで実行されます。 マルチコアシステムのメリットを生かすために、 ユーザは時々 Node プロセスのクラスを起動して負荷を分散したくなります。

クラスタモジュールは、サーバポートを共有する複数の子プロセスを簡単に 構築することを可能にします。

var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  // Fork workers.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', function(worker, code, signal) {
    console.log('worker ' + worker.process.pid + ' died');
  });
} else {
  // Workers can share any TCP connection
  // In this case its a HTTP server
  http.createServer(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");
  }).listen(8000);
}

node は 8000 番ポートをワーカ間で共有します。

% NODE_DEBUG=cluster node server.js
23521,Master Worker 23524 online
23521,Master Worker 23526 online
23521,Master Worker 23523 online
23521,Master Worker 23528 online

この機能は最近導入されたばかりであり、 将来のバージョンで変更される可能性があります。 これを試して、フィードバックを行ってください。

Windows では、ワーカが名前付きパイプによるサーバをセットアップすることは まだできないことにも注意してください。

How It Works#

ワーカプロセスは child_process.fork メソッドを使って起動されるため、 親プロセスと IPC で通信したり、サーバハンドルをやり取りしたりすることが できます。

クラスタモジュールは到着する接続を分散する方法を二種類提供します。

一つ目 (Windows 以外の全てのプラットフォームでデフォルト) はラウンドロビン方式で、マスタプロセスがポートをリッスンし、 新しい接続を受け付けるとラウンドロビン方式でワーカに分散します (ワーカの過負荷を避ける工夫が組み込まれています)。

二つ目の方法は、マスタプロセスがリスニングソケットを作成し、 ワーカに送信します。ワーカは到着する接続を直接受け付けます。

二つ目の方法は、原則的には、ベストなパフォーマンスであるべきです。 しかし実際には、OS の予測不可能なスケジューラにより、 非常に不均衡に分散される傾向があります。 全 8 プロセス中の 2 プロセスに 70% 以上の接続が割り当てられたことも 観測されました。

server.listen() は仕事の大部分をマスタプロセスに渡すため、 通常の node.js プロセスとクラスタのワーカプロセスの間には 振る舞いが異なるケースが 3 つあります。

  1. server.listen({fd: 7}) メッセージはマスタに渡されてるため、 ワーカのファイル記述子 7 が参照するものではなく、 親プロセスの ファイル記述子 7 がリスニングされてそのハンドルがワーカに 渡されます。
  2. server.listen(handle) 明示的なハンドルをリスニングするとマスタプロセスは 関与することなく、ワーカは与えられたハンドルを使うことになります。 ワーカがすでにハンドルを持っているなら、何をしようとしているか あなたは分かっているでしょう。
  3. 'server.listen(0) 通常、これはサーバがランダムなポートをリッスンすることを 意味します。しかしながらクラスタでは、各ワーカは listen(0) によって 同じ "ランダムな" ポートを受信します。 すなわち、初回はポートはランダムになりますが、その後はそうではありません。 もしユニークなポートをリッスンしたければ、クラスタのワーカ ID に基づいて ポート番号を生成してください。

Node.js にもあなたのプログラムにも、ルーティングのためのロジックや ワーカ間で共有される状態はありません。 したがって、あなたのプログラムがセッションやログインのためにメモリ内の データオブジェクトに過度に頼らないように設計することが重要です。

全てのワーカは独立したプロセスなので、他のワーカに影響を与えることなく プログラムのニーズに応じてそれらを殺したり再起動したりすることができます。 いくつかのワーカが生きている限り、サーバは接続を受け付け続けます。 しかしながら、Node はワーカの数を自動的に管理することはありません。 アプリケーションのニーズに応じてワーカのプールを管理することは、 あなたの責務です。

cluster.schedulingPolicy#

スケジューリングポリシーは、ラウンドロビンの cluster.SCHED_RR または、 OS に任せる cluster.SCHED_NONE のどちらかです。 これはグローバルな設定で、その効果は最初のワーカを起動する時か、 cluster.setupMaster() を呼び出した時、どちらかが最初に行われた時点で 凍結されます。

SCHED_RR は Windows 以外の全ての OS でデフォルトです。 Windows では、libuv が大きなパフォーマンス低下を招くことなく IOCP ハンドルを分散することが可能であれば、SCHED_RR に切り替わります。

cluster.schedulingPolicy は、NODE_CLUSTER_SCHED_POLICY 環境変数を 通じて設定することもできます。適切な値は "rr" または "none" です。

cluster.settings#

  • {Object}
    • execArgv {Array} node 実行ファイルに渡される引数を表す、文字列の配列 (デフォルトは process.execArgv)。
    • exec {String} ワーカで実行するファイルへのパス (デフォルトは process.argv[1])。
    • args {Array} ワーカに渡される引数となる文字列 (デフォルトは process.argv.slice(2))。
    • silent {Boolean} 出力を親プロセスに送るかどうか (デフォルトは false)。

.setupMaster() (または .fork()) が呼び出された後、この settings オブジェクトはデフォルト値を含む設定オブジェクトを持ちます。

.setupMaster() は一度しか呼び出せないため、それは設定された後で事実上 凍結されます。

このオブジェクトはあなたによって変更されることを想定していません。

cluster.isMaster#

  • Boolean

現在のプロセスがマスタの場合は true です。 これは process.env.NODE_UNIQUE_ID から決定されます。 process.env.NODE_UNIQUE_ID が未定義だと isMastertrue になります。

cluster.isWorker#

  • Boolean

このプロセスがマスタでなければ true (これは cluster.isMaster の否定です)。

Event: 'fork'#

  • worker Worker object

新しいワーカがフォークされると、クラスタモジュールは 'fork' イベントを 生成します。 これはワーカの活動をロギングしたり、タイムアウトのために使うことができます。

var timeouts = [];
function errorMsg() {
  console.error("Something must be wrong with the connection ...");
}

cluster.on('fork', function(worker) {
  timeouts[worker.id] = setTimeout(errorMsg, 2000);
});
cluster.on('listening', function(worker, address) {
  clearTimeout(timeouts[worker.id]);
});
cluster.on('exit', function(worker, code, signal) {
  clearTimeout(timeouts[worker.id]);
  errorMsg();
});

Event: 'online'#

  • worker Worker object

新しいワーカをフォークした後、ワーカはオンラインメッセージを応答します。 マスタがオンラインメッセージを受信すると、このイベントが生成されます。 'fork''online' の違いは、'fork' はマスタがワーカをフォークした時点で 生成されるのに対し、'online' はワーカが実行されてから生成される点です。

cluster.on('online', function(worker) {
  console.log("Yay, the worker responded after it was forked");
});

Event: 'listening'#

  • worker Worker object
  • address Object

ワーカが net.Server.listen() を呼び出した後、(net や http などの) サーバでは 'listening' イベントが生成され、マスタの cluster でも 'listening' イベントが生成されます。

イベントハンドラは二つの引数を伴って実行されます。 worker はワーカオブジェクトを、address オブジェクトは 以下の接続プロパティを含みます: addressprot、そして addressType です。 これはワーカが複数のアドレスをリッスンしている場合にとても便利です。

cluster.on('listening', function(worker, address) {
  console.log("A worker is now connected to " + address.address + ":" + address.port);
});

addressType は以下のいずれかです:

  • 4 (TCPv4)
  • 6 (TCPv6)
  • -1 (unix ドメインソケット)
  • "udp4" または "udp6" (UDP v4 または v6)

Event: 'disconnect'#

  • worker Worker object

ワーカとの IPC チャネルが切断された後で生成されます。 それはワーカが自然に終了したり、殺されたり、あるいは (worker.disconnect() により) 手動で切断された場合に発生します。

'disconnect''exit' の間には遅延があるかもしれません。 このイベントはプロセスがクリーンナップで行き詰まったり、長時間生きている接続が ないかを検出することに使用できます。

cluster.on('disconnect', function(worker) {
  console.log('The worker #' + worker.id + ' has disconnected');
});

Event: 'exit'#

  • worker {Worker object}
  • code {Number} 正常に終了した場合は終了コード。
  • signal {String} プロセスが殺される原因となったシグナルの名前 (例: 'SIGHUP')。

どのワーカが死んだ場合でも、クラスタモジュールは 'exit' イベントを 生成します。

これは .fork() を呼び出してワーカを再開する場合に使用することができます。

cluster.on('exit', function(worker, code, signal) {
  console.log('worker %d died (%s). restarting...',
    worker.process.pid, signal || code);
  cluster.fork();
});

See child_process event: 'exit'.

Event: 'setup'#

setupMaster() が最初に呼ばれた時に生成されます。

cluster.setupMaster([settings])#

  • settings {Object}
    • exec {String} ワーカで実行するファイルへのパス. (デフォルトは process.argv[1])
    • args {Array} ワーカに渡される引数となる文字列。 (デフォルトは process.argv.slice(2))
    • silent {Boolean} 出力を親プロセスに送るかどうか。 (デフォルトは false)

setupMaster() は 'fork' のデフォルト動作を変更するために使われます。 一度呼び出されると、その設定は cluster.settings に反映されます。

注意事項:

  • .setupMaster() の最初の呼び出しだけ効果があります。 その後の呼び出しは無視されます。
  • 上記のため、ワーカごとにカスタマイズできる属性は .fork() に渡すことのできる env だけ です。
  • .fork() はデフォルト値を反映するために内部で .setupMaster() を呼び出すため、.setupMaster() が効果を持つには .fork() よりも前に 呼び出す必要があります。

例:

var cluster = require("cluster");
cluster.setupMaster({
  exec : "worker.js",
  args : ["--use", "https"],
  silent : true
});
cluster.fork();

これはマスタプロセスからのみ、呼び出すことができます。

cluster.fork([env])#

  • env {Object} ワーカプロセスの環境に加えられるキーと値のペア。
  • return {Worker object}

新しいワーカプロセスを起動します。

これはマスタプロセスからのみ呼び出すことができます。

cluster.disconnect([callback])#

  • callback {Function} 全てのワーカが切断し、ハンドルがクローズされると 呼び出されます。

cluster.workers 内の各ワーカに対して .disconnect() を呼び出します。

ワーカとの接続が切断して内部的なハンドルが全てクローズされると、 他に待機しているイベントがなければ、マスタプロセスは自然に終了します。

このメソッドはオプションの引数としてコールバックを受け取ります。

これはマスタプロセスからのみ呼び出すことができます。

cluster.worker#

  • Object

現在のワーカオブジェクトへの参照です。 マスタプロセスでは利用できません。

var cluster = require('cluster');

if (cluster.isMaster) {
  console.log('I am master');
  cluster.fork();
  cluster.fork();
} else if (cluster.isWorker) {
  console.log('I am worker #' + cluster.worker.id);
}

cluster.workers#

  • Object

id をキーとしてアクティブなワーカオブジェクトを保存しているハッシュです。 これは全てのワーカに対して繰り返しを行うことを容易にします。 マスタプロセスでのみ利用可能です。

ワーカは 'disconnect''exit' が生成される前に cluster.worker から 削除されます。

// Go through all workers
function eachWorker(callback) {
  for (var id in cluster.workers) {
    callback(cluster.workers[id]);
  }
}
eachWorker(function(worker) {
  worker.send('big announcement to all workers');
});

通信チャネルを越えてワーカの参照を渡す場合は、 ワーカのユニークな ID を使ってワーカを探すのが簡単です。

socket.on('data', function(id) {
  var worker = cluster.workers[id];
});

Class: Worker#

ワーカに関する全ての公開された情報やメソッドを持つオブジェクトです。 マスタでは cluster.wrokers から取得することができます。 ワーカでは cluster.worker から取得することができます。

worker.id#

  • String

新しいワーカはいずれもユニークな ID を与えられます。 この ID は id に保存されます。

ワーカが生きている間、これは cluseter.workers のキーとなります。

worker.process#

  • ChildProcess object

全てのワーカは child_process.fork() によって作成されます。 その戻り値は .process に設定されます。 ワーカでは、グローバルの process に設定されます。

参照: Child Process module

process'disconnect' イベントが生成されるとワーカが process.exit(0) を呼び出し、.suicidetrue にならないことに注意してください。 これは偶発的な切断を防ぎます。

worker.suicide#

  • Boolean

.kill() または .disconnect() によって設定されます。 それまでは undefined です。

真偽値の worker.suicide は、ワーカが自発的に終了したのか偶発的に終了したのかを 区別します。 マスタはこの値に基づいて、ワーカを再起動しないことを選ぶことができます。

cluster.on('exit', function(worker, code, signal) {
  if (worker.suicide === true) {
    console.log('Oh, it was just suicide\' – no need to worry').
  }
});

// kill worker
worker.kill();

worker.send(message, [sendHandle])#

  • message Object
  • sendHandle Handle object

この関数は child_process.fork() が返すオブジェクトの send() メソッドと同じです。 マスタは特定のワーカにメッセージを送信するためにこの関数を 使用することができます。

ワーカでは process.send(message) を使うこともできます。 それは同じ関数です。

この例はマスタからのメッセージをエコーバックします。

if (cluster.isMaster) {
  var worker = cluster.fork();
  worker.send('hi there');

} else if (cluster.isWorker) {
  process.on('message', function(msg) {
    process.send(msg);
  });
}

worker.kill([signal='SIGTERM'])#

  • signal {String} ワーカプロセスに送られるシグナルの名前です。

この関数はワーカを終了します。 マスタでは、これは worker.process と切断することによって行われます。 そして切断されると、signal によってワーカを殺します。 ワーカでは、これはチャネルの切断によって行われ、コード 0 で終了します。

.suicide が設定される原因となります。

後方互換性のため、このメソッドには worker.destroy() という別名があります。

ワーカでは、process.kill() は存在するものの、それは関数ではないことに 注意してください。 kill を参照してください。

worker.disconnect()#

ワーカでは、この関数は全てのサーバをクローズし、それらのサーバの 'close' イベントを待機し、そして IPC チャネルを切断します。

マスタでは、ワーカが自分の .disconnect() を呼び出すことになる内部メッセージを ワーカに送ります。

.suicide が設定される原因となります。

サーバがクローズした後、それはもう新たな接続を受け付けなくなりますが、 他のワーカによって接続は受け付けられることに注意してください。 既存のコネクションは通常通りクローズすることができます。 コネクションが無くなると (server.close() 参照)、 ワーカが自然に終了できるように IPC チャネルはクローズされます。

上記はサーバ側のコネクションにのみ適用されます。 クライアント側のコネクションはワーカによって自動的にクローズされることはなく、 終了する前にそれらがクローズすることを待つこともありません。

ワーカでは、process.disconnect は存在しますが、それはここで説明した関数では ありません。それは disconnect です。

長時間生きているサーバ側のコネクションはワーカが切断することを妨げるため、 それらをクローズするためにアプリケーション固有のメッセージを送ることは有用です。 加えて、一定の時間が経過しても 'disconnect' イベントが発生しなかった場合に ワーカを強制終了する実装も有用です。

if (cluster.isMaster) {
  var worker = cluster.fork();
  var timeout;

  worker.on('listening', function(address) {
    worker.send('shutdown');
    worker.disconnect();
    timeout = setTimeout(function() {
      worker.kill();
    }, 2000);
  });

  worker.on('disconnect', function() {
    clearTimeout(timeout);
  });

} else if (cluster.isWorker) {
  var net = require('net');
  var server = net.createServer(function(socket) {
    // connections never end
  });

  server.listen(8000);

  process.on('message', function(msg) {
    if(msg === 'shutdown') {
      // initiate graceful close of any connections to server
    }
  });
}

Event: 'message'#

  • message Object

このイベントは child_process.fork() が提供するものと同じです。

ワーカでは、process.on('message') を使うこともできます。

メッセージシステムを使用してクラスタ全体のリクエスト数を マスタプロセスで保持する例です:

var cluster = require('cluster');
var http = require('http');

if (cluster.isMaster) {

  // Keep track of http requests
  var numReqs = 0;
  setInterval(function() {
    console.log("numReqs =", numReqs);
  }, 1000);

  // Count requestes
  function messageHandler(msg) {
    if (msg.cmd && msg.cmd == 'notifyRequest') {
      numReqs += 1;
    }
  }

  // Start workers and listen for messages containing notifyRequest
  var numCPUs = require('os').cpus().length;
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  Object.keys(cluster.workers).forEach(function(id) {
    cluster.workers[id].on('message', messageHandler);
  });

} else {

  // Worker processes have a http server.
  http.Server(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");

    // notify master about the request
    process.send({ cmd: 'notifyRequest' });
  }).listen(8000);
}

Event: 'online'#

cluster.on('online') と同様ですが、このワーカに特化しています。

cluster.fork().on('online', function() {
  // Worker is online
});

このイベントはワーカでは生成されません。

Event: 'listening'#

  • address Object

cluster.on('listening') と同様ですが、このワーカに特化しています。

cluster.fork().on('listening', function(address) {
  // Worker is listening
});

このイベントはワーカでは生成されません。

Event: 'disconnect'#

cluster.on('disconnect') と同様ですが、このワーカに特化しています。

cluster.fork().on('disconnect', function() {
  // Worker has disconnected
});

Event: 'exit'#

  • code {Number} 正常に終了した場合は終了コード。
  • signal {String} プロセスが殺される原因となったシグナルの名前 (例: 'SIGHUP')。

cluster.on('exit') と同様ですが、このワーカに特化しています。

var worker = cluster.fork();
worker.on('exit', function(code, signal) {
  if( signal ) {
    console.log("worker was killed by signal: "+signal);
  } else if( code !== 0 ) {
    console.log("worker exited with error code: "+code);
  } else {
    console.log("worker success!");
  }
});

Event: 'error'#

このイベントは child_process.fork() が提供するものと同じです。

ワーカでは process.on('error') を使うこともできます。