Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

lua_nginx_module JSON-RPC 2.0 Batch Request

5,849 views

Published on

nginx luaでJSON-RPC 2.0 Batch Requestを実装して地雷踏んだ話

Published in: Technology
  • Be the first to comment

lua_nginx_module JSON-RPC 2.0 Batch Request

  1. 1. nginx luaでJSON-RPC batch requestを実装して地雷踏んだ話 2016/02/08 @mosa_siru 1
  2. 2. @mosa_siru 2
  3. 3. @mosa_siru (もさ) • ボンバーマン極めてる人 • 2013-2015 DeNA • Mobage 3rd party API運用 • ハッカドール新規開発(サーバーサイド) • 2015- Gunosy • 広告運用、新規事業開発 3
  4. 4. 今日の内容 4
  5. 5. 今日の内容 • JSON-RPCの紹介 • JSON-PRCって? • JSON-RPC Batch Requestが便利! • lua_nginx_moduleでBatch Request実装した話 • 地雷踏んだ話 5
  6. 6. JSON-RPCって? 6
  7. 7. JSON-PRC • とってもシンプルなRPCプロトコル • リクエストもレスポンスも全部指定のJSON形 式でやろうぜ! • 多数の言語でサーバー・クライアント共にフ レームワーク化やライブラリ化されている 7
  8. 8. Example(1) 8 Request {"jsonrpc": "2.0", "method": "subtract", "params": [5, 2], "id": 1} {"jsonrpc": "2.0", "result": 3, "id": 1} Response
  9. 9. Example(2) 9 Request {"jsonrpc": "2.0", "method": "getUser", "params": { "user_id": 1 }, "id": 1} {"jsonrpc": "2.0", "result": {"name": "mosa", "email": "mosa@hogefuga.com"}, "id": 1 Response
  10. 10. JSON-PRC • とってもシンプル! • 特定のmethodにパラメータつきで1つの単純なリクエスト を送って、1つのレスポンスをもらう • 外部に公開するAPIでもない限り、URI設計や冪等性とか 色々気にしないといけないRestFulよりも扱いやすい • 大抵のネイティブアプリとサーバーAPI間の通信、 Internal APIなどはこれで十分 10
  11. 11. Batch Request Example 11 Request [ {"jsonrpc": "2.0", "method": "subtract", "params": [5, 2], "id": 1}, {"jsonrpc": "2.0", "method": "getUser", "params": { "user_id": 1 }, "id": 2} ] [ {"jsonrpc": "2.0", "result": 3, "id": 1}, {"jsonrpc": "2.0", "result": {"name": "mosa", "email": "mosa@hogefuga.com"}, "id": 2} ] Response
  12. 12. Batch Request • 複数のリクエストを一気に送って、一気にレ スポンスがもらえるプロトコル • JSON Arrayで送ってJSON Arrayで返る
 
 12 • 仕様上、送ったArrayの順番通りにレスポンスが並ぶこ とは保証されない
 => 識別子として "id"のkeyをclientは見るべき
  13. 13. Batch Requestのメリット • リクエスト数を減らすことができる • APIの設計がシンプルになる(1つのリソースに対して1つ 作っておけば良く、UIを気にしなくて良い) • クライアント側で、複数のAPIのレスポンスを待ったハンド リングがシンプルにできる • 「2つのAPIの結果を待たないと描画してはいけない」 • 「どれかがこけた場合は全体をエラーにする」 13
  14. 14. Batch Requestのデメリット • 重い処理に引きずられる • 処理に10秒かかるリクエストA • 処理に1秒かかるリクエストB • A,BをBatch Requestにすると、最低10秒かか る 14
  15. 15. Batch Requestは
 どう実装すべきか 15
  16. 16. 実装(1) フレームワークで行う フレームワーク側で、for loop でそれぞれ順番 に処理し、全て終わったらまとめてレスポンス を生成する 16
  17. 17. 実装(1) フレームワークで行う 17 • 既存のフレームワーク実装はだいたいこれ。 • 10個分のbatch requestを投げたら処理に10倍 時間がかかることになり、イケてない • 並列でそれぞれのスレッド(プロセス)が処理す るのが理想だが、言語によっては複雑な実装 になる
  18. 18. 実装(2) Proxy serverをたてる proxyがJSON Arrayのそれぞれに対し、非同期 で並列にバックエンドのJSON-RPCサーバーに リクエストを投げ、全て返ってきたら1つにまと めてレスポンスを生成する
  19. 19. 実装(2) Proxy serverをたてる 19 • 管理コンポーネントが増えるので、運用の複雑さが増 す • 異常が起きた場合、どの経路で死んだのか多数のロ グを漁ることになる • proxy serverの台数管理も考えないといけない • single requestも考えると、appへのreverse proxy設定 をnginxとproxyで二重管理することになりうる
  20. 20. 実装(3) nginxで行う nginxのレイヤーでbatch requestはバラしてし まう (2)のproxyの役割をluaが担当 20
  21. 21. 実装(3) nginxで行う 21 • backendのJSON-RPC APIはsingle requestさえ捌ければ良 く、サーバー言語に依らずこの構成が取ることができる • 管理コンポーネントも増えない • イベントドリブンなnginxの性質を活かし、ノンブロッキング (=backendの処理を待たない)並列なbatch requestを簡単に 実装することができる • single requestも同じnginxで捌けるので、reverse proxy設定 が二重管理にならない
  22. 22. というわけで nginx + luaで実装した話 22
  23. 23. library化しました 23 https://github.com/mosasiru/lua-resty-jsonrpc- batch 簡単に、batch requestが並列にノンブロッキン グに実装できます。
  24. 24. 24
  25. 25. Basic Usage解説 25 • /api が裏側のJSON-RPC APIに投げる(single request用) • /api/batch がbatch requestを分解し、 
 /api に subrequest (処理を移譲) • 内部的には、lua_nginx_moduleの localtion.capture_multi を利用しています
  26. 26. 26
  27. 27. Advanced Usage解説 27 • でかいArrayが来ると攻撃になるので、最大サイズを10とする • subrequestの処理時間をnginx access logに入れるために、 nginx変数に値を入れている • ライブラリに用意されたフックポイントを利用している • 「jsonrpc "substract" methodは /jsonrpc/method/substract のパスで受けたい」など、動的なlocationに対応している • というわけで、色々拡張できるようになっています
  28. 28. 運用可能なの? 28
  29. 29. subrequest の調査 29 • 事前にかなりの検証をした (CentOS 6.2, ngx_openresty1.7.10.1) • lua実行中にnginx reloadされるとどうなるか? • subrequest実行中にnginx reloadされるとどうなるか? • luaでエラーになるとどうなるか? • upstreamでエラーになるとどうなるか? • upstreamがtimeoutするとどうなるか? • client timeoutするとどうなるか?
  30. 30. subrequest の調査 30 • 検証の結果、以下に気をつければ問題ないことがわかる • アクセスログにsubrequestの情報を盛り込むべき • luaエラー時にハンドリングできるようlua pcallを利用するべき • nginx reloadは問題なく可能! • subrequest upstreamで刺さる分には影響ない • luaで刺さるとnginx workerプロセスごと刺さる(がくぶる • upstreamのレスポンスサイズがproxy buffersを超え、proxy_max_temp_file_size も超えるかOFFにしていると応答が返らない
 (しかしnginx workerプロセスは刺さらない)
  31. 31. 導入してみた 31
  32. 32. 問題なくリクエストを捌ける、 が… 32 • 数万req/min を余裕で1台で捌けるが • なぜか徐々にリークするメモリー • なぜか時々出るソケットリークエラー
 
 • そしてソケット開けなくなって死ぬ [alert] 15248#0: open socket #123 left in connection 456
  33. 33. とりあえず 定期reloadする運用… 33
  34. 34. 調査の結果 34 upstreamのレスポンスサイズがproxy buffersを超え、 proxy_max_temp_file_size も超えるかOFFにしていると 応答が返らない(ただしworkerプロセスはブロックされ ない) • とあるAPIにてこれが起きていた。 • このときソケットを閉じ忘れるようだ • メモリも開放されないようだ
  35. 35. コードとか読むかんじ 35 upstreamのレスポンスサイズがproxy buffersを 超え、proxy_max_temp_file_size も超えたとき • single request: bufferingしないで直接 upstream =>clientにwriteしてくれる • subrequest: 全部bufferingしようとして死ぬ
  36. 36. subrequest利用時には proxy_buffersを 適切に上げる運用にしましょう 36
  37. 37. おわり 37

×