node.js clusterでHTTPサーバをマルチプロセス化する

提供: Node.js/JavaScript入門
2014年12月3日 (水) 00:19時点におけるDaemon (トーク | 投稿記録)による版

(差分) ←前の版 | 最新版 (差分) | 次の版→ (差分)
移動: 案内検索
スポンサーリンク

node.jsのclusterを使用してHTTPサーバ(ウェブサーバ)を並列化し、パフォーマンスを上げられます。

読み方

cluster
くらすた

概要

node.jsの問題点として、シングルプロセスで処理をしていて、あるリクエストの処理が長くかかる場合、ほかのすべてのリクエストをブロックする可能性があります。httpモジュールで、ウェブサーバを起動したとき、同時に処理できるアクセスは、1つだけなので、処理がつまると、後ろに並んでいるリクエストの待ち行列は、ブロックされた状態になります。

そのような問題を解決するために、並行サーバ(並列)、マルチスレッドモデルなどがあるわけです。 node.jsでは、この問題に対処するいくつかの方法がありますが、ここでは、clusterを用いた例を示します。

clusterは、マルチコアを活かすためのものです。並行サーバは、コア数以上は実際に動作しないようで、コアが合計2個しかなければ、コア数より多くfork()したとしても、2個分のプロセスしか仕事をしません。

clusterの考え方は、伝統的なUnixプログラミングのforkモデルと同じと捉えて差し支えありません。

cluster.js で示すコードでは、node で起動されたプログラム(親)は、コア数だけ fork を行い、子プロセスがHTTPサーバを起動し、リクエストを処理します。

Unixのfork()と異なるのは、Unixのforkは、forkを呼び出した直後のプログラムが親プロセスと子プロセスで実行されますが、node.jsのclusterの場合は、forkを実行すると、同じプログラムが最初から起動されます。

Clusterのサンプルプログラム

HTTPサーバをマルチプロセス化する前に、Clusterの簡単な例を示します。親プロセスかどうかの判断は、 cluster.isMaster を参照し、真であれば、親と判断します。 子プロセスは、 process.exit() で終了させることができます。

/*
 * cluster1.js
 * Copyright (C) 2014 kaoru <kaoru@bsd>
 */
var cluster = require('cluster');
if (cluster.isMaster) {
        console.log ('parents');
        var worker = cluster.fork();
        cluster.on('exit', function(worker, code, signal) {
                console.log('worker ' + worker.process.pid + ' died');
        });
} else {
        console.log("worker");
        process.exit();
}

実行例は、以下の通りです。

node cluster1.js
parents
worker
worker 47429 died

子プロセスが何らかの理由で終了してしまった場合に、以下のコードが実行されます。

cluster.on('exit', function(worker, code, signal) {
	console.log('worker ' + worker.process.pid + ' died');
});

子プロセスが終了してしまった場合に、子プロセスを作り直すには、上記のコードに fork() を追加することで実現できます。

cluster.on('exit', function(worker, code, signal) {
	console.log('worker ' + worker.process.pid + ' died');
	cluster.fork();
});

マルチプロセスのHTTPサーバでは、リクエストをさばくための子プロセスが終了したままになってしまうと、リクエストをさばく子プロセスが減ったままになってしまいます。また、何らかの理由で、すべての子プロセスが終了してしまった場合、リクエストがさばけなくなってしまいます。そのために、いなくなってしまった子プロセスの分のプロセスを再作成する必要が出てきます。

CPUの数を調べる方法

マルチプロセス化する時にCPUの数(コアの数)を調べて、fork()します。そのために、以下のコードでCPUの数を調べることができます。

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

ClusterでHTTPサーバをマルチプロセス化する例

cluster.js

/*
 * cluster.js
 * Copyright (C) 2014 kaoru <kaoru@bsd>
 */
var cluster = require('cluster');
var http = require('http');
 
var numCPUs = require('os').cpus().length;
 
var port = 8080;
var max_server = numCPUs; // max server
if (cluster.isMaster) {
        console.log ('master');
 
        for ( var i = 0 ; i < max_server ; ++i) {
                cluster.fork();
        }
 
        cluster.on('exit', function(worker, code, signal) {
                console.log('worker ' + worker.process.pid + ' died');
                cluster.fork();
        });
 
} else {
        console.log("worker");
        var server = http.createServer(function(req, res){
                res.writeHead(200);
                res.end('Hello World');
        }).listen(port);
}

実行例

node cluster.js

ベンチマーク

node.jsによるHTTPサーバの作り方の簡単なHTTPサーバとcluster.jsを ab でベンチマークしました。 Hello World レベルのプログラムのベンチマークです。10000 リクエストを並列30でベンチマークした場合です。

ab -n 10000 -c 30 http://localhost:8080/

ベンチマークの結果は、平行サーバは、 2.3 秒、非平行サーバは 3.1秒と、ほんのすこしだけ、パフォーマンスが上がっていることが確認できました。

コア数 2 の並行サーバ

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      30
Time taken for tests:   2.323 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      870000 bytes
HTML transferred:       120000 bytes
Requests per second:    4304.82 [#/sec] (mean)
Time per request:       6.969 [ms] (mean)
Time per request:       0.232 [ms] (mean, across all concurrent requests)
Transfer rate:          365.74 [Kbytes/sec] received

非平行サーバ

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      30
Time taken for tests:   3.175 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1130000 bytes
HTML transferred:       120000 bytes
Requests per second:    3150.08 [#/sec] (mean)
Time per request:       9.524 [ms] (mean)
Time per request:       0.317 [ms] (mean, across all concurrent requests)
Transfer rate:          347.62 [Kbytes/sec] received

関連項目




スポンサーリンク