Mais conteúdo relacionado
Semelhante a Node.jsv0.8からv4.xへのバージョンアップ ~大規模Push通知基盤の運用事例~ (20)
Mais de Recruit Technologies (20)
Node.jsv0.8からv4.xへのバージョンアップ ~大規模Push通知基盤の運用事例~
- 4. Page 4
Pusna-RSとは
Push Notification Aggregator Realtime & Scalable
モバイルアプリのためのPush通知基盤
Node.jsを用いて高いリアルタイム性
AWSの各種機能を用いたスケーラビリティ
双方の確保
リクルートグループの主要なスマホアプリで
用いられている
運用開始から2年経ち、億のデバイスを扱う規模に
なっても無停止で安定稼動中!
- 10. Page 10
NodeとIoの関係性が落ち着いた
9 / 8にNode.js v4がリリース!
Node.jsとIo.jsの統合
LTSのリリース
Node.js v4.2 Argon
https://github.com/nodejs/LTS/
30ヶ月サポートされる
今が移行に最適なタイミング!
- 16. Page 16
どう移行させるか
正常動作させる
• Node.js v4.xへアップグ
レードすることにより
動かなくなるコードの修
正
• Node Core API
• npmパッケージのアッ
プグレード
新技術の取り入れ
• 古い実装を新しい実装
に書き換える
• Stream3
• ES2015
• npmパッケージのリプ
レース
• etc...
- 17. Page 17
どう移行させるか
正常動作させる
• Node.js v4.xへアップグ
レードすることにより、
動かなくなるコードの修
正
• Node Core API
• npmパッケージのアッ
プグレード
新技術の取り入れ
• 古い実装を新しい実装
に書き換える
• Stream3
• ES2015
• npmパッケージのリプ
レース
• etc...
- 18. Page 18
どう移行させるか
正常動作させる
• Node.js v4.xへアップグ
レードすることにより、
動かなくなるコードの修
正
• Node Core API
• npmパッケージのアッ
プグレード
新技術の取り入れ
• 古い実装を新しい実装
に書き換える
• Stream3
• ES2015
• npmパッケージのリプ
レース
• etc...
Pusna-RSにおけるStreamの移行の話をします!
- 20. Page 20
Stream API
データの”流れ”を綺麗に扱うためのAPI
データを一括で読み込むのではなく、破片ごとに読み
処理することができる
各Streamをpipe()で連結することができる
Readable
• I/Oなどからの
読み込み
Readable /
Writable
(Transform)
• データの整形
Writable
• I/Oなどへの書
き出し
- 21. Page 21
Node.jsのStream APIの変遷
実装Ver 安全性 後方互換性
Stream 1 - △
データの取りこぼしやStream
のpause(), resume()が頻繁
に呼ばれ、パフォーマンスが
劣化する危険性
-
Stream 2 v0.10 ◯
内部バッファの実装により
I/Oの不安定な流れに強く
なった。
△
following mode (Stream1互
換モード)とpuase modeを交
互に行き交えない
Stream 3 v0.12 ◯ ◯
Stream 3 = Stream 1 +
Stream2
- 22. Page 22
Node.jsのStream APIの変遷
実装Ver 安全性 後方互換性
Stream 1 - △
データの取りこぼしやStream
のpause(), resume()が頻繁
に呼ばれ、パフォーマンスが
劣化する危険性
-
Stream 2 v0.10 ◯
内部バッファの実装により
I/Oの不安定な流れに強く
なった。
△
following mode (Stream1互
換モード)とpuase modeを交
互に行き交えない
Stream 3 v0.12 ◯ ◯
Stream 3 = Stream 1 +
Stream2
現在のPusnaの実装
ここにUpgradeしたい
- 34. Page 34
Stream1では
自分でbufferで管理
function GcmNotificationStream(options) {
Stream.call(this);
this.requests = []; // ①: bufferを作成
}
util.inherits(GcmNotificationStream, Stream);
GcmNotificationStream.prototype.write = function(data) {
var req = send(data, function(err, result) {
this.requests.splice(....); // ③: bufferから削除
this.emit(‘data’, result); // ④: dataを書き出し
});s
this.requests.push(data); // ②: bufferにpush
.............................
// hwm以下だったら書き込み可
return self.requests.length < self.highWaterMark;
};
- 35. Page 35
Stream3では
標準でbufferingをしてくれる
function GcmNotificationStream(options) {
Transform.call(this); // Writable / Readable Stream
}
util.inherits(GcmNotificationStream, Transform);
GcmNotificationStream.prototype._write = function(chunk, enc, cb) {
// chunkを加工
this.push(data);
};
- 39. Page 39
class構文
ES5での書き方
ES2015での書き方
function GcmNotificationStream(options) {
Stream.call(this);
}
util.inherits(GcmNotificationStream, Stream);
GcmNotificationStream.prototype._write .....
class GcmNotificationStream extends Writable{
constructor(options) {
super();
}
_wirte(chunk, enc, cb) {
}
}
- 45. Page 45
簡易ベンチマーク
デバイス登録シナリオ
デバイス管理
登録API 登録WorkerSQS
DynamoDB
elasticsearch
登録APIに10000件リクエストを送信し、スループットを計測
登録APIサーバはSQSにキューイングした時点でレスポンスを返
す。
50同時リクエスト
server: AWS m3.medium
Node.js v4.2.1 + Express4 vs Node.js v0.8.24 + Express3
- 48. Page 48
確認
v0.8とv4.xで単純なexpressアプリでBenchmarkを
とってみる (ついでなのでv0.10, v0.12, v5もとりま
した)
スループットを比較
AWS m3.medium (client, serverともに)
var express = require('express');
var app = express();
app.get('/', function(req, res) {
res.send('Hello, World');
});
app.listen(3000);
- 62. Page 63
Node 0.8からNode 4のAPIの変更
細かい変更点はwikiのBreaking API Changesにのっ
てる
実際に遭遇したケースを紹介
net / tls / http(s)などのネットワーク系のモジュール
で大きな変更点があったという印象
- 63. Page 64
ケース1: Keep Alive Agent
v0.8のhttp.Agentは機能的に十分ではなかった
SocketをPoolしないでremoveしてしまう実装
self.on('free', function(socket, host, port, localAddress) {
.....................
if (!socket.destroyed &&
self.requests[name] && self.requests[name].length) {
self.requests[name].shift().onSocket(socket);
if (self.requests[name].length === 0) {
// don't leak
delete self.requests[name];
}
} else {
.....................
socket.destroy();
}
});
- 64. Page 65
Pusna-RSの独自実装のKeep Alive Agent
freeがemitされたらSocketをプールするように
その他細かいオプションが取れるようなAgentを実装
self.on('free', function(socket, host, port, localAddress) {
self.onFree(socket, host, port, localAddress);
});
KeepAliveAgent.prototype.onFree = function(socket, host, port,
localAddress) {
.......................
var count = lengthOfArray(this.freeSockets, name) +
lengthOfArray(this.sockets, name);
if (count > this.options.maxSockets) {
socket.destroy();
return;
}
pushToArray(this.freeSockets, name, socket);
};
- 66. Page 67
ケース2: net系のイベントの発火タイミング
(Socket.destroy)
socket.on('close', function() {
console.log('2: onclose');
});
async.waterfall([
function(next) {
socket.connect(80, function() {
console.log('1: connected');
next(null);
});
},function(next) {
socket.destroy();
async.nextTick(next);
}], function(err, result) {
if (err) return;
console.log('3: end');
});
- 68. Page 69
socket.on(‘close’)がemitされるタイミング
// Node 4.x
this._handle.close(function() {
debug('emit close');
self.emit('close', isException);
});
...........................
// Node 0.8.x
process.nextTick(function() {
self.emit('close', exception ? true : false);
});
...........................