ド初心者が
5000QPSの広告配信APIを
Node.jsで構築したおはな死
自己紹介
@zuqqhi2
• EC系企業広告エンジニア(3年目)
• 好きなこと:観葉植物を愛でる
@Jimisky
• EC系企業広告エンジニア(2年目)
• 好きなこと:食べること
未だにドが取れたぐらいの初心者です
間違ったことを言ったら、遠慮無くマサカリを!
ちなみにLTもド初心者です
注意事項
背景
背景
上司
「この広告の表示、一ヶ月で3倍速くできない?」
「!?」
スピード改善前の広告パーツ
複数のAPIを叩く
↓
データの加工
↓
Viewの動的生成
ほぼ全てブラウザ側のJavascriptで処理
スピード改善前の広告パーツ
• CSS:約4000行
• JavaScript:いっぱい
• リクエスト数:34 / 1 display
• データ転送量:1MB / 1 display
• MAX5000QPSくらい
え、私の広告リッチすぎ・・・?
表示スピード改善計画
1. 直列で呼んでいる複数APIの一本化
2. APIのレスポンスをKVSにキャッシュ
3. ロジック部分をバックエンド化
4. PaaS化してスケール可能に
Browser
← →
API
API
API
API
Browser
← →
API
API
K
V
S
API
API
API
表示スピード改善計画
1. 直列で呼んでいる複数APIの一本化
2. APIのレスポンスをKVSにキャッシュ
3. ロジック部分をバックエンド化
4. PaaS化してスケール可能に
Browser
← →
API
API
API
API
Browser
← →
API
API
K
V
S
API
API
API
ここをNode.jsで!
But…
しかし…
当時の私たち
Node.js経験
5日
0日
しかも
広告の仕様をほぼ把握していない
さらに
[悲報]実質の開発期間は2週間
になりました
本当にいろいろあったものの
かろうじて動くレベルになり
初回リリースの日がやってきた!
(クライアントのJSをほぼ機械的にコピペ)
結果
結果
• PaaSのインスタンス1台で7QPS
結果
• PaaSのインスタンス1台で7QPS
• 絶えず自壊するインスタンス
結果
• PaaSのインスタンス1台で7QPS
• 絶えず自壊するインスタンス
• ユーザー間でレスポンスが混線…?
(グローバル変数の削除漏れ)
ぎゃああああ
※撮影前日水道が止まり、お風呂に入れていません
これはアカン!と速攻ロールバック
バグ潰しとQPS改善の長い戦いが始まった…
バトンタッチ
得られた教訓(一部)
1. child_process.exec以外の方法を使うべし
2. C/C++ベースのライブラリを積極的に使うべし
3. よく使用するデータはローカルメモリにのせて使うべし
4. チューニングは勘に頼らずダンプを使うべし
1.child_process.exec
広告配信サーバ APIサーバ
1.child_process.exec
広告配信サーバ APIサーバ
httpモジュール
1.child_process.exec
広告配信サーバ APIサーバ
httpモジュール
1.child_process.exec
広告配信サーバ APIサーバ
httpモジュール
+ proxy
1.child_process.exec
広告配信サーバ APIサーバ
httpモジュール
+ proxy A
1.child_process.exec
広告配信サーバ APIサーバ
child_process.exec(‘curl’)
1.child_process.exec
広告配信サーバ APIサーバ
child_process.exec(‘curl’)
1.child_process.exec
広告配信サーバ APIサーバ
child_process.exec(‘curl’)
通信方法 1通信あたりの速度(ms)
child_process.exec(‘curl’) 50ms
httpモジュール 30ms
1.child_process.exec
広告配信サーバ APIサーバ
httpモジュール
+ proxy
教訓1:child_process.exec以外の方法を使うべし
2.ライブラリ
• 巨大オブジェクトのclone
– node-clone
• 巨大XMLの解析
– Jquery
• Redisクライアント
– node_redis
2.ライブラリ
• 巨大オブジェクトのclone
– node-clone 10ms
– node-v8-clone 1ms
• 巨大XMLの解析
– jquery 250ms
– node-expat 1ms
• Redisクライアント
– node_redis 10ms
– hiredis-node 7ms
2.ライブラリ
• 巨大オブジェクトのclone
– node-clone 10ms
– node-v8-clone 1ms
• 巨大XMLの解析
– jquery 250ms
– node-expat 1ms
• Redisクライアント
– node_redis 10ms
– hiredis-node 7ms
教訓2:C/C++ベースのライブラリを積極的に使うべし
3.ローカルオンメモリ
広告配信サーバ Redis Cluster
加工済み
広告情報
3.ローカルオンメモリ
広告配信サーバ Redis Cluster
②加工済み
広告情報
メモリ
①加工済み
広告情報
3.ローカルオンメモリ
広告配信サーバ Redis Cluster
②加工済み
広告情報
メモリ
①加工済み
広告情報
メモリ使用量が一定値を超えたら
利用回数が少ないデータを削除
3.ローカルオンメモリ
広告配信サーバ Redis Cluster
②加工済み
広告情報
メモリ
①加工済み
広告情報
メモリ使用量が一定値を超えたら
利用回数が少ないデータを削除
ストレージ 1通信あたりの速度(ms)
Redis 7ms
ローカルオンメモリ 1ms
3.ローカルオンメモリ
広告配信サーバ Redis Cluster
②加工済み
広告情報
メモリ
①加工済み
広告情報
メモリ使用量が一定値を超えたら
利用回数が少ないデータを削除
教訓3:よく使用するデータはローカルメモリにのせて使うべし
4.CPU/Heapダンプ
• CPUダンプ
– nodegrind
• 指定した期間内のCPUの状態をダンプする
• Chromeの開発者ツールで結果を確認できる
• Heapダンプ
– heapdump
• ダンプ取得関数を実行した時点でのHeap状態をダンプする
• Chromeの開発者ツールで結果を確認できる
4.CPU/Heapダンプ
• CPUダンプ
– nodegrind
4.CPU/Heapダンプ
• Heapダンプ
– heapdump
4.CPU/Heapダンプ
• CPUダンプ
– nodegrind
• 指定した期間内のCPUの状態をダンプする
• Chromeの開発者ツールで結果を確認できる
• Heapダンプ
– heapdump
• ダンプ取得関数を実行した時点でのHeap状態をダンプする
• Chromeの開発者ツールで結果を確認できる
教訓4:チューニングは勘に頼らずダンプを使うべし
得られた教訓再掲
1. child_process.exec以外の方法を使うべし
2. C/C++ベースのライブラリを積極的に使うべし
3. よく使用するデータはローカルメモリにのせて使うべし
4. チューニングは勘に頼らずダンプを使うべし
得られた教訓再掲
1. child_process.exec以外の方法を使うべし
2. C/C++ベースのライブラリを積極的に使うべし
3. よく使用するデータはローカルメモリにのせて使うべし
4. チューニングは勘に頼らずダンプを使うべし
5.安請け合いしないようにしよう!!
成果
• 1インスタンスごとのQPS
– 初期バージョン 7
– 最終バージョン 350
• 広告表示速度
– 約30~60% UP
• 広告のクリック率
– 約15% UP
Node.jsいいね!
※撮影前日水道が止まり、お風呂に入れていません
ご静聴ありがとうございました

20141115_node_school_festival_lt

Editor's Notes

  • #24 フォーマット揃える!!! 1.フロントからglobal変数
  • #42 ソースコード例はzuqqhi2.comで
  • #46 フォーマット揃える!!! 1.フロントからglobal変数
  • #47 フォーマット揃える!!! 1.フロントからglobal変数