Last active
September 13, 2020 09:08
-
-
Save nulltask/89e6f36e194c951697a0 to your computer and use it in GitHub Desktop.
Revisions
-
nulltask revised this gist
Jan 16, 2017 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -223,7 +223,7 @@ io.adapter(redis('redis.host:6379')); var emitter = require('socket.io-emitter')('redis.host:6379'); emitter.emit('broadcast', 'this is broadcasting'); // broadcasting emitter.of('/nsp').emit('broadcast', 'broadcasting to namespace'); emitter.of(socketId).emit('greeting', 'Hello, ' + socketId + '!'); ``` -
nulltask revised this gist
Jan 12, 2015 . No changes.There are no files selected for viewing
-
nulltask revised this gist
Aug 26, 2014 . 1 changed file with 2 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -262,6 +262,7 @@ io.httpServer.getConnection(function(err, count) { - require('socket.io-client'); - Node.js でクライアントを大量生成 - `http.globalAgent.maxSockets` で同時接続数を上げておく - クライアントの動作をシミュレートする - Browserify でクライアントとコードを共有できるかも @@ -285,4 +286,4 @@ iframe.src = '/client.html'; - Cluster, Nginx, ELB をうまく使う - プロセスは機能毎に思い切って分割する - 一つのアプリで多くのことをしすぎない - 経験上 Socket.IO と Express は密結合にならないほうが吉 -
nulltask revised this gist
Aug 26, 2014 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -127,7 +127,7 @@ CPU 使用率との戦いになるため、大規模になる場合は Web ア アプリケーションは Engine.IO と Socket.IO の関係性を真似るといいかも。 Socket.IO のクライアントを包含するようなクラスを作ってビジネスロジックを記述する。 クライアントサイドのコードは、DOM に依存するレイヤーを分けておくと、Node.js 側で socket.io-client を使ってベンチマークテストする時にコードが流用しやすくなる。 ## 同時接続数の上限緩和 -
nulltask revised this gist
Aug 26, 2014 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,7 +1,7 @@ # Express / Socket.IO をスケールアウトしてみよう - Seiya Konno - Works at Uniba Inc. (http://uniba.jp)  -
nulltask revised this gist
Aug 26, 2014 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,6 @@ # Express / Socket.IO をスケールアウトしてみよう - Seiya Konno - Works at Uniba Inc.  -
nulltask revised this gist
Aug 26, 2014 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -204,6 +204,7 @@ Redis の Pub/Sub を使って複数プロセスを協調するようにする - https://github.com/Automattic/socket.io-redis - 別プロセスに接続されているクライアントに対してメッセージを送信 - 特定のプロセスからのブロードキャストをプロセス全体に通知 - Emitter を使って非 Socket.IO プロセスからの通知をハンドリング出来る ```javascript var io = require('socket.io')(); -
nulltask renamed this gist
Aug 26, 2014 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
nulltask created this gist
Aug 26, 2014 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,287 @@ # Express / Socket.IO をスケールアウトしてみよう - nulltask - Works at Uniba Inc.  - https://twitter.com/nulltask - https://github.com/nulltask - https://fb.me/nulltask # スケーラビリティとは システムの規模に依らず機能を適応できること - リクエストに対するスケーラビリティ - アプリケーションコードに対するスケーラビリティ # Express - https://github.com/strongloop/express 言わずと知れたウェブアプリケーションフレームワーク - 右も左もわからなかった頃 => app.js の肥大化 - メンテナビリティの低下 - アプリの規模が大きくなってもメンテナビリティを確保したい ## Mounting Sub Application - Express のアプリを `use` でマウントする機構 - Rails Mountable Engine のような機能 - マトリョーシカみたい ```javascript var app = express(); var sub = express(); sub.get('/hello', function(req, res) { res.send('this is /sub/hello'); }); app.use('/sub', sub); // /sub 下にマウント ``` ## express.Router(); - ルーティングそのものをモジュール化 - アプリのマウントより粒度を細かく出来る - Express 4.0 以降の新機能 ```javascript var app = express(); var users = express.Router(); users.use(fn); // 特定の router に対する middleware users.get('/', fn); users.get('/:id', fn); users.post('/', fn); users.put('/:id', fn); users.del('/:id', fn); app.use('/users', users); ``` ## Cluster - よく言われること - 「Node.js はシングルスレッドだから CPU 使い切れない...」 - 「Cluster でマルチプロセス化すれば性能向上できるよ」 _boot.js_: ```javascript var cluster = require('cluster'); module.exports = function(child, num) { return cluster.isMaster() ? master(num) : child(); }; function master(num) { num = num || require('os').cpus().length; cluster.on('exit', function(worker, code, signal) { cluster.fork(); }); for (var i = 0; i < num; ++i) cluster.fork(); }; ``` _server.js_: ```javascript var app = require('./app'); // var io = require('./io'); var boot = requier('./boot'); boot(function() { var server = app.listen(3000, function() { // io.attach(server); }); }, require('os').cpus().length); ``` ## Benchmarking - JMeter! - 詳しくは Google で検索 ELB でバランシングする際、Node サーバに Nginx を設置しないほうがオーバーヘッドが少なく Node のプロセスを直接バランシングしたほうが高速だった。 # Socket.IO - https://github.com/Automattic/socket.io 言わずと知れたリアルタイムウェブアプリケーションフレームワーク - 接続数 - CPU リソース - Socket.IO / Engine.IO 自体のコスト - ハンドシェイキング - 接続クライアントの管理など - アプリケーションコードのコスト - ブロードキャストの頻度など - 要件次第で変動 CPU 使用率との戦いになるため、大規模になる場合は Web アプリと一緒にせず、分けたほうが吉。 アプリケーションは Engine.IO と Socket.IO の関係性を真似るといいかも。 Socket.IO のクライアントを包含するようなクラスを作ってビジネスロジックを記述する。 クライアントサイドのコードは、DOM に依存するレイヤーを分けておくと、ベンチマークテスト時に流用しやすくなる。 ## 同時接続数の上限緩和 オープンできるファイル数 (ソケット数) の上限が設定されているので緩和する _/etc/security/limits.conf_: ``` * soft nofile 65536 * hard nofile 65536 ``` これだけではデーモンプロセスには適応されない _/etc/sysconfig/init_: ``` ulimit -n 65536 ``` 設定を変更したら一旦再起動。再起動後に `/proc/${PID}/limits` で上限が緩和されているか確認しましょう ## Socket.IO + ELB の制約 Socket.IO と組み合わせた時の ELB の悲しい制約 - HTTP モードの場合 - HTTP ヘッダが書き換えられ HTTP Upgrade 出来ない - WebSocket が使えないため XHR polling が強制される - TCP モードの場合 - Sticky Session が使えない - 接続するたび別のサーバが応答するため Socket.IO のハンドシェイキングに失敗する ELB を使わず複数台をバランシングする場合、Socket.IO 接続前に、どの Nginx ホストに接続するかをサーバに問い合わせて、もらったホストに接続するように知る。 ## Nginx コネクション数の設定と、HTTP Upgrade が有効になっていることがキモ。`ip_hash` で stickiness を有効にするのを忘れずに。 _/etc/nginx/nginx.conf_: ``` events { worker_connections 32768; } worker_rlimit_nofile 65536; ``` _/etc/nginx/conf.d/virtual.conf_: ``` upstream io { ip_hash; # <= Important! server 0.0.0.0:3000; server 0.0.0.0:3001; server 0.0.0.0:3002; server 0.0.0.0:3003; } server { listen 80; location / { proxy_set_header Upgrade $http_upgrade; # <= Important! proxy_set_header Connection "upgrade"; # <= Important! proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_http_version 1.1; proxy_pass http://io; } } ``` ## Socket.IO Redis Adapter Redis の Pub/Sub を使って複数プロセスを協調するようにする - https://github.com/Automattic/socket.io-redis - 別プロセスに接続されているクライアントに対してメッセージを送信 - 特定のプロセスからのブロードキャストをプロセス全体に通知 ```javascript var io = require('socket.io')(); var redis = require('socket.io-redis'); io.adapter(redis('redis.host:6379')); ``` ## Socket.IO Emitter 別の Node プロセスからメッセージを送信する。(Redis Adapter に依存。) - https://github.com/Automattic/socket.io-emitter ```javascript var emitter = require('socket.io-emitter')('redis.host:6379'); emitter.emit('broadcast', 'this is broadcasting'); // broadcasting emitter.of('/nsp').emit('broadcast', 'broadcasting to namespace); emitter.of(socketId).emit('greeting', 'Hello, ' + socketId + '!'); ``` ## Socket.IO Emitter for Ruby Socket.IO Emitter の Ruby 版。別の Ruby プロセスからメッセージを送信する。(Redis Adapter に依存。) - https://github.com/nulltask/socket.io-ruby-emitter _Gemfile_: ```ruby gem 'redis' gem 'socket.io-emitter' ``` _broadcast.rb_: ```ruby emitter = Emitter.new(Redis.new) emitter.emit('greeting', 'hello from ruby'); emitter.of('/nsp').emit('greeting', 'hello from ruby'); ``` ## 接続数を知る ```javascript var io = require('socket.io')(); Object.keys(io.sockets.sockets).length; io.httpServer.getConnection(function(err, count) { console.log(count); }); ``` ## Benchmarking - require('socket.io-client'); - Node.js でクライアントを大量生成 - クライアントの動作をシミュレートする - Browserify でクライアントとコードを共有できるかも - Multiple IFRAME - クライアントと同じ振る舞いをするページを IFRAME で大量生成 - 親ページから postMesage で IFRAME を操る - 同一ホストへの同時接続数が設定できる Firefox 限定 - `about:config` => `network.http.max-persistent-connections-per-server` - Chrome は同時接続数を増やせないっぽい ```javascript var iframe = document.createElement('iframe'); iframe.src = '/client.html'; ``` 接続数と CPU・ネットワーク負荷を確認しながら 1 サーバ、1 プロセスあたりの接続上限数を見極める。 ## まとめ - Cluster, Nginx, ELB をうまく使う - プロセスは機能毎に思い切って分割する - 一つのアプリで多くのことをしすぎない - 経験上 Socket.IO と Express は密結合にならないほうが吉