nodejs多核処理

17175 ワード

はじめに、nodejsは単一プロセスの単一スレッドのサーバーエンジンであることをみんなは知っています.どんなに強力なハードウェアがあっても、単一のCPUでしか計算できません.そこで、第三者のclusterを開発し、nodeがマルチコアCPUを利用して並行して実現できるようにしました.
nodejsの発展に従って、nodejsに環境を生産させて、多プロセスの多核処理を支持しなければなりません.V 0.0.6バージョンでは、Nodejsにclusterの特性が内蔵されています.これにより、Nodejsはついに独立したアプリケーション開発ソリューションとして目に映るようになりました.
目次
  • cluster紹介
  • clusterの簡単な使用
  • clusterの動作原理
  • clusterのAPI
  • マスターとウォーカーの通信
  • clusterで負荷の均衡を実現します.win 7失敗
  • .clusterで負荷の均衡を実現するために-uuntu成功
  • cluster負荷バランスポリシーの試験
  • 1.clusterの紹介
    clusterはnodejs内蔵のモジュールで、nodejs多核処理に用いられます.clusterモジュールは、多プロセス並列化プログラムの開発難易度を簡略化し、負荷バランスのためのクラスタを容易に構築することができます.
    2.clusterの簡単な使用
    私のシステム環境
  • win 7 64 bit
  • Nodejs:v 0.0.5
  • Npm:1.2.19
  • winの環境の中で、私達はclusterを通じて多核のnodeをスタートさせてウェブサービスを提供します.
    新しいプロジェクトディレクトリ:
    
    ~ D:\workspace\javascript>mkdir nodejs-cluster && cd nodejs-cluster
    
    新規ファイル:ap.js
    
    ~ vi app.js
    
    var cluster = require('cluster');
    var http = require('http');
    var numCPUs = require('os').cpus().length;
    
    if (cluster.isMaster) {
        console.log("master start...");
    
        // Fork workers.
        for (var i = 0; i < numCPUs; i++) {
            cluster.fork();
        }
    
        cluster.on('listening',function(worker,address){
            console.log('listening: worker ' + worker.process.pid +', Address: '+address.address+":"+address.port);
        });
    
        cluster.on('exit', function(worker, code, signal) {
            console.log('worker ' + worker.process.pid + ' died');
        });
    } else {
        http.createServer(function(req, res) {
            res.writeHead(200);
            res.end("hello world
    "); }).listen(0); }
    コンソールでnodeプログラムを起動します.
    
    ~ D:\workspace\javascript
    odejs-cluster>node app.js master start... listening: worker 2368, Address: 0.0.0.0:57132 listening: worker 1880, Address: 0.0.0.0:57132 listening: worker 1384, Address: 0.0.0.0:57132 listening: worker 1652, Address: 0.0.0.0:57132
    マスターはマスターノードで、ウォーカーは実行ノードです.そしてCPUの数に応じて、ウォーカーを起動します.私はローカルにデュアルコアのCPUですので、4つのコアが検出され、4つのウォーカーが起動しました.
    3.clusterの動作原理
    各ワーカープロセスはchild_を使うことによってprocess.fork関数は、IPC(Inter-Parocess Communication、プロセス間通信)に基づいて、masterプロセスとの間の通信を実現します.
    workerがserver.listen(...)関数を使用するとパラメータシーケンスをmasterプロセスに伝えます.マスタープロセスがworkersにマッチしたら、最後の文を労働者に伝えます.マスターがワーカーにマッチしていない場合は、ワーカーを作成して、文を伝えてワーカーに伝えます.
    境界条件では、3つの面白い行動があります.注:下のserver.listenは、下の「http.Server-」net.Server類への呼び出しです.
  • 1.server.listen({fd:7}:masterとworkerの通信過程で、ファイルを転送することによって、masterは「ファイル記述は7」であって、「ファイル記述は7」の参照を転送するのではなく、「ファイル記述は7」を傍受します.
  • .server.listen(handle):masterとworkerの通信プロセスは、handle関数を介して通信し、プロセス連絡なしに
  • に通信する.
  • .server.listen(0):masterとworkerの通信プロセスでは、クラスタ内のworkerはランダムポートを開いて共有し、socket通信によって、上記の例の57132
  • のように通信する.
    複数のプロセスが同じリソースにある場合、オペレーティングシステムの負荷バランスは非常に効率的である.Node.jsにはルーティングロジックがありません.worker間に共有状態がありません.したがって、プログラムはメモリベースのsessionなど、簡単に設計する必要があります.
    ワーカーは独力で運行していますので、プログラムの必要に応じて独立して削除または再起動できます.ワーカーは互いに影響しません.ウォーカーが生きている限り、マスタは接続を受信し続けます.Nodeは自動的にworkersの数を維持しません.私たちは自分の接続池を作ることができます.
    4.clusterのAPI
    公式サイトのアドレス:http://nodejs.org/api/cluster.html#cluster_cluster
    clusterオブジェクトの各種属性と関数
  • cluster.settings:クラスタパラメータオブジェクト
  • を構成する.
  • cluster.isMaster:マスターノード
  • ではないかと判断する.
  • cluster.isWorker:ワーカーノード
  • であるかどうかを判定する.
  • Event:'fork':workプロセス作成イベントを傍受する
  • Event:'online':ウォーカーを待ち受けて成功イベントを作成しました.
  • Event:'listening':モニターワーカーがmaster状態イベント
  • に向かう.
  • Event:'disconnect':ウォーカー切断イベントを傍受する
  • Event:'exit':worker退出イベントを傍受する
  • Event:'setup':setupMasterイベントを傍受する
  • cluster.setupMaster(settings):クラスタパラメータを設定する
  • cluster.fork(env):workプロセス
  • を作成する.
  • cluster.disconnect(calback):worketプロセスを閉じる
  • cluster.worker:現在のworkerオブジェクト
  • を取得する.
  • cluster.workers:クラスタ内で生存している全てのウォーカーオブジェクト
  • を取得する.
    workerオブジェクトの各種属性と関数:cluster.workersを通じて、cluster.worketが得られます.
  • worker.id:プロセスID番号
  • worker.process:ChildProcessオブジェクト
  • worker.suicide:disconnect()後、workerが自殺したかどうかを判断する
  • worker.send:masterはworkerにメッセージを送ります.注:workerがmasterにメッセージを送るにはprocess.send
  • を使います.
  • worker.kill([signal='SIGTRM]):指定されたウォーカーを殺し、別名destory()
  • worker.disconnect():workカーを切断し、workerを自殺させます.
  • Event:'message':マスターとウォーカーのメッセージイベントを傍受する
  • Event:'online':指定されたウォーカーを傍受して成功イベントを作成する
  • Event:'listening':マスター・ウォーカー状態イベントを傍受する
  • Event:'disconnect':ウォーカー切断イベントを傍受する
  • Event:'exit':worker退出イベントを傍受する
  • 5.マスターとウォーカーの通信
    clusterのAPIを実現し、マスターとワーカーが通信するようにします.
    新規ファイル:cluster.js
    
    ~ vi cluster.js
    
    var cluster = require('cluster');
    var http = require('http');
    var numCPUs = require('os').cpus().length;
    
    if (cluster.isMaster) {
        console.log('[master] ' + "start master...");
    
        for (var i = 0; i < numCPUs; i++) {
            var wk = cluster.fork();
            wk.send('[master] ' + 'hi worker' + wk.id);
        }
    
        cluster.on('fork', function (worker) {
            console.log('[master] ' + 'fork: worker' + worker.id);
        });
    
        cluster.on('online', function (worker) {
            console.log('[master] ' + 'online: worker' + worker.id);
        });
    
        cluster.on('listening', function (worker, address) {
            console.log('[master] ' + 'listening: worker' + worker.id + ',pid:' + worker.process.pid + ', Address:' + address.address + ":" + address.port);
        });
    
        cluster.on('disconnect', function (worker) {
            console.log('[master] ' + 'disconnect: worker' + worker.id);
        });
    
        cluster.on('exit', function (worker, code, signal) {
            console.log('[master] ' + 'exit worker' + worker.id + ' died');
        });
    
        function eachWorker(callback) {
            for (var id in cluster.workers) {
                callback(cluster.workers[id]);
            }
        }
    
        setTimeout(function () {
            eachWorker(function (worker) {
                worker.send('[master] ' + 'send message to worker' + worker.id);
            });
        }, 3000);
    
        Object.keys(cluster.workers).forEach(function(id) {
            cluster.workers[id].on('message', function(msg){
                console.log('[master] ' + 'message ' + msg);
            });
        });
    
    } else if (cluster.isWorker) {
        console.log('[worker] ' + "start worker ..." + cluster.worker.id);
    
        process.on('message', function(msg) {
            console.log('[worker] '+msg);
            process.send('[worker] worker'+cluster.worker.id+' received!');
        });
    
        http.createServer(function (req, res) {
                res.writeHead(200, {"content-type": "text/html"});
                res.end('worker'+cluster.worker.id+',PID:'+process.pid);
        }).listen(3000);
    
    }
    
    コンソールログ:
    
    ~ D:\workspace\javascript
    odejs-cluster>node cluster.js [master] start master... [worker] start worker ...1 [worker] [master] hi worker1 [worker] start worker ...2 [worker] [master] hi worker2 [master] fork: worker1 [master] fork: worker2 [master] fork: worker3 [master] fork: worker4 [master] online: worker1 [master] online: worker2 [master] message [worker] worker1 received! [master] message [worker] worker2 received! [master] listening: worker1,pid:6068, Address:0.0.0.0:3000 [master] listening: worker2,pid:1408, Address:0.0.0.0:3000 [master] online: worker3 [worker] start worker ...3 [worker] [master] hi worker3 [master] message [worker] worker3 received! [master] listening: worker3,pid:3428, Address:0.0.0.0:3000 [master] online: worker4 [worker] start worker ...4 [worker] [master] hi worker4 [master] message [worker] worker4 received! [master] listening: worker4,pid:6872, Address:0.0.0.0:3000 [worker] [master] send message to worker1 [worker] [master] send message to worker2 [worker] [master] send message to worker3 [worker] [master] send message to worker4 [master] message [worker] worker1 received! [master] message [worker] worker2 received! [master] message [worker] worker3 received! [master] message [worker] worker4 received!
    6.clusterで負荷バランスを実現する--win 7失敗
    新規ファイル:server.js
    
    ~ vi server.js
    
    var cluster = require('cluster');
    var http = require('http');
    var numCPUs = require('os').cpus().length;
    
    if (cluster.isMaster) {
        console.log('[master] ' + "start master...");
    
        for (var i = 0; i < numCPUs; i++) {
             cluster.fork();
        }
    
        cluster.on('listening', function (worker, address) {
            console.log('[master] ' + 'listening: worker' + worker.id + ',pid:' + worker.process.pid + ', Address:' + address.address + ":" + address.port);
        });
    
    } else if (cluster.isWorker) {
        console.log('[worker] ' + "start worker ..." + cluster.worker.id);
        http.createServer(function (req, res) {
            console.log('worker'+cluster.worker.id);
            res.end('worker'+cluster.worker.id+',PID:'+process.pid);
        }).listen(3000);
    }
    
    サーバを起動:
    
    ~ D:\workspace\javascript
    odejs-cluster>node server.js [master] start master... [worker] start worker ...1 [worker] start worker ...2 [master] listening: worker1,pid:1536, Address:0.0.0.0:3000 [master] listening: worker2,pid:5920, Address:0.0.0.0:3000 [worker] start worker ...3 [master] listening: worker3,pid:7156, Address:0.0.0.0:3000 [worker] start worker ...4 [master] listening: worker4,pid:2868, Address:0.0.0.0:3000 worker4 worker4 worker4 worker4 worker4 worker4 worker4 worker4
    curlツールでアクセスする
    
    C:\Users\Administrator>curl localhost:3000
    worker4,PID:2868
    C:\Users\Administrator>curl localhost:3000
    worker4,PID:2868
    C:\Users\Administrator>curl localhost:3000
    worker4,PID:2868
    C:\Users\Administrator>curl localhost:3000
    worker4,PID:2868
    C:\Users\Administrator>curl localhost:3000
    worker4,PID:2868
    C:\Users\Administrator>curl localhost:3000
    worker4,PID:2868
    C:\Users\Administrator>curl localhost:3000
    worker4,PID:2868
    C:\Users\Administrator>curl localhost:3000
    worker4,PID:2868
    
    私たちはclusterのwinにおけるbugを発見しました.思い切ってLinuxテストに切り替えます.
    7.clusterで負荷の均衡を実現する--uuntu成功
    Linuxのシステム環境
  • Linux:Ubuntu 12.04.2 64 bit Server
  • Node:v 0.1.2
  • Npm:1.2.21
  • 構築項目:多く説明しない
    
    ~ cd :/home/conan/nodejs/
    ~ mkdir nodejs-cluster && cd nodejs-cluster
    ~ vi server.js
    
    var cluster = require('cluster');
    var http = require('http');
    var numCPUs = require('os').cpus().length;
    
    if (cluster.isMaster) {
        console.log('[master] ' + "start master...");
    
        for (var i = 0; i < numCPUs; i++) {
             cluster.fork();
        }
    
        cluster.on('listening', function (worker, address) {
            console.log('[master] ' + 'listening: worker' + worker.id + ',pid:' + worker.process.pid + ', Address:' + address.address + ":" + address.port);
        });
    
    } else if (cluster.isWorker) {
        console.log('[worker] ' + "start worker ..." + cluster.worker.id);
        http.createServer(function (req, res) {
            console.log('worker'+cluster.worker.id);
            res.end('worker'+cluster.worker.id+',PID:'+process.pid);
        }).listen(3000);
    }
    
    サーバを起動
    
    conan@conan-deskop:~/nodejs/nodejs-cluster$ node server.js
    [master] start master...
    [worker] start worker ...1
    [master] listening: worker1,pid:2925, Address:0.0.0.0:3000
    [worker] start worker ...3
    [master] listening: worker3,pid:2931, Address:0.0.0.0:3000
    [worker] start worker ...4
    [master] listening: worker4,pid:2932, Address:0.0.0.0:3000
    [worker] start worker ...2
    [master] listening: worker2,pid:2930, Address:0.0.0.0:3000
    worker4
    worker2
    worker1
    worker3
    worker4
    worker2
    worker1
    
    curlツールでアクセスする
    
    C:\Users\Administrator>curl 192.168.1.20:3000
    worker4,PID:2932
    C:\Users\Administrator>curl 192.168.1.20:3000
    worker2,PID:2930
    C:\Users\Administrator>curl 192.168.1.20:3000
    worker1,PID:2925
    C:\Users\Administrator>curl 192.168.1.20:3000
    worker3,PID:2931
    C:\Users\Administrator>curl 192.168.1.20:3000
    worker4,PID:2932
    C:\Users\Administrator>curl 192.168.1.20:3000
    worker2,PID:2930
    C:\Users\Administrator>curl 192.168.1.20:3000
    worker1,PID:2925
    
    Linux環境では、clusterが正しく動作します!!
    8.cluster負荷バランス戦略のテスト
    私達はLinuxの下でテストを完成しました.テストソフトを使ったことがあります.
    インストール
    ~ sudo apt-get install siege
    node clusterを起動します
    ~ node server.js > server.log
    siege起動コマンドを実行して、毎秒50個の同時要求を行います.
    
    ~ sudo siege -c 50 http://localhost:3000
    
    HTTP/1.1 200   0.00 secs:      16 bytes ==> /
    HTTP/1.1 200   0.00 secs:      16 bytes ==> /
    HTTP/1.1 200   0.00 secs:      16 bytes ==> /
    HTTP/1.1 200   0.01 secs:      16 bytes ==> /
    HTTP/1.1 200   0.00 secs:      16 bytes ==> /
    HTTP/1.1 200   0.00 secs:      16 bytes ==> /
    HTTP/1.1 200   0.00 secs:      16 bytes ==> /
    HTTP/1.1 200   0.01 secs:      16 bytes ==> /
    HTTP/1.1 200   0.00 secs:      16 bytes ==> /
    HTTP/1.1 200   0.00 secs:      16 bytes ==> /
    HTTP/1.1 200   0.00 secs:      16 bytes ==> /
    HTTP/1.1 200   0.02 secs:      16 bytes ==> /
    HTTP/1.1 200   0.00 secs:      16 bytes ==> /
    HTTP/1.1 200   0.02 secs:      16 bytes ==> /
    HTTP/1.1 200   0.01 secs:      16 bytes ==> /
    HTTP/1.1 200   0.01 secs:      16 bytes ==> /
    .....
    
    ^C
    Lifting the server siege...      done.                                                                Transactions:                    3760 hits
    Availability:                 100.00 %
    Elapsed time:                  39.66 secs
    Data transferred:               0.06 MB
    Response time:                  0.01 secs
    Transaction rate:              94.81 trans/sec
    Throughput:                     0.00 MB/sec
    Concurrency:                    1.24
    Successful transactions:        3760
    Failed transactions:               0
    Longest transaction:            0.20
    Shortest transaction:           0.00
    
    FILE: /var/siege.log
    You can disable this annoying message by editing
    the .siegerc file in your home directory; change
    the directive 'show-logfile' to false.
    
    私たちは3760回の要求を実行して、39.66秒を消費して、毎秒94.81回の要求を処理します.
    server.logファイルを表示します.
    
    ~  ls -l
    total 64
    -rw-rw-r-- 1 conan conan   756  9  28 15:48 server.js
    -rw-rw-r-- 1 conan conan 50313  9  28 16:26 server.log
    
    ~ tail server.log
    worker4
    worker1
    worker2
    worker4
    worker1
    worker2
    worker4
    worker3
    worker2
    worker1
    
    最後に、R言語で分析してみます.server.log
    
    ~ R 
    
    > df<-read.table(file="server.log",skip=9,header=FALSE)
    > summary(df)
           V1
     worker1:1559
     worker2:1579
     worker3:1570
     worker4:1535
    
    私たちは、要求がワーカーのデータ量に相当することを見ました.したがって、clusterの負荷バランスの戦略は、ランダムに割り当てられるべきです.
    はい、私たちはまた有用な技能を学びました.clusterを利用してマルチ核応用を構築し、マルチCPUのキャリア性能を十分に活用しましょう.
    転載は出典を明記してください.http://blog.fens.me/nodejs-core-cluster/