Upload
kazuhiro-sera
View
2.792
Download
2
Embed Size (px)
Citation preview
Servlet と Future の関わり方
Kazuhiro Sera @seratch_ja 2015/08/01
自己紹介
•東京から来ました
• @seratch / @seratch_ja
• ScalikeJDBC(2011 ~)
• Skinny Framework(2013 ~)
• Scalatra、json4s、Scalate メンテナ
•エムスリー株式会社 ソフトウェアエンジニア
アジェンダ•前提・背景
•スレッドローカルが存在する世界
• Future はスレッドプールが裏にいる
• Servlet API の mutability
• Async モードでの request リサイクル問題
• Scalatra の DynamicScope 問題
• Skinny 2 でのソリューション
2 つのパターン
•メインスレッドから Future を複数投げて Await.result(Future) で待ち合わせ、同期で Servlet response を返す
• Servlet 3 からの async mode により非同期で Servlet response を返す(今日のテーマは主にこっち)
なぜ Future か
• Servlet において Scala の Future を積極的に使っていく必要性は特にない
•とはいえ 2.10 から入った Future は十分に普及してきた、Future を返す実装の組み込みに対応できないと実用上困るケースがある
•身近な具体例では nulab/scala-oauth2-provider を使うにあたり、支障があった
スレッドモデル
• request を受けて response を返すスレッド
• HTTP レスポンス返すまでメインスレッドだけで処理するのが基本的なスタイル
•このメインスレッドは Servlet コンテナが持つスレッドプールで管理される
• JavaEE 7 は JSR-236 でコンポーネント管理と非同期処理を両立するための仕様を提示
スレッドローカル
•Scala では DynamicVariable
• シングルスレッドで完結する前提ならスレッドローカルを使って色々楽ができる
•状態をパラメータで引き回さない
• AOP で透過的な処理をはさみこみやすい
•暗黙の状態を活用、コードがシンプルに
Future• Future scaladoc
• Future.apply { ここは別スレッド }
• Future.successful { ここは同じスレッド }
• カジュアルに別スレッドの処理が生まれる、適当にやってもスレッドプール(ExecutionContext で指定)で管理される
• Future を生んだ元スレッドの値を Future 側のスレッドから簡単に参照できてしまう(後述)
mutability
• request#setAttribute(String, AnyRef) を始めとする setter メソッド
• request attributes に依存する実装パターン
• session などコンテナ管理のオブジェクト
•任意のタイミングで response を出力
•マルチスレッドでアクセスする場合、素のままで扱うのはあやうい
JSR 340: Java Servlet 3.1 Specification https://jcp.org/en/jsr/detail?id=340
Bug 433321 - request.getContextPath() is null when working in async mode https://bugs.eclipse.org/bugs/show_bug.cgi?id=433321
Jetty
Bug 46792 - NullPointerException in org.apache.catalina.connector https://bz.apache.org/bugzilla/show_bug.cgi?id=46792#c5
Tomcat
JSR 340: Java Servlet 3.1 Specification https://jcp.org/en/jsr/detail?id=340
recycling objects•実際の挙動として request は AsyncContext#complete() が呼ばれる前に recycle されることがある
•パフォーマンス上のオーバーヘッドを避けるため複数の Servlet コンテナに共通する挙動
• Servlet の仕様に response についても同様の記述(5.7)はあるが、少なくとも stateless な HTTP 通信に関しては close まで維持されると考えてよいはず
request/response
• Scalatra の request/response はスレッドローカル、Future 側からそのまま触れない
• implicit パラメータで引き渡すアプローチにすべての DSL、拡張が対応していない、意図せず DynamicScope が呼び出される
•最初期の目的・設計は妥当だった(Future はまだメインストリームでなかった、模倣した Sinatra は Rack アプリ)
skinny-engine
• Scalatra を fork して DSL の互換性は維持しつつ、内部実装を大きく変えた
• trait の構成はあまり互換性を維持していない(改名・構造の変更・削除)
• Scalatra の既存のテストをほぼそのままの状態ですべて pass している
•改善アイデア、潜在的問題の発見があった、Scalatra にもフィードバックしていきたい
stable request
• request を Future が処理中のタイミングで recycle されたとしても read-only な情報、request attributes 等へのアクセスを保証
• request attributes の更新も考慮
•コンテナ管理オブジェクトの操作はメインスレッド以外ではアクセスエラーに(2.0.0.M3)
•少なくとも Jetty、Tomcat で動作保証
No more DynamicScope
•前提として、すべての DSL に Context を implicit parameter として渡す
• Context を引数にとる関数をアクションとしてとる AsyncSkinnyEngineServlet/Filter
• Scalatra 互換な trait では ’error: ambiguous implicit values’(コンパイルエラー)を発生させて確実に潰す
話したこと•前提・背景
•スレッドローカルが存在する世界
• Future はスレッドプールが裏にいる
• Servlet API の mutability
• Async モードでの request リサイクル問題
• Scalatra の DynamicScope 問題
• Skinny 2 でのソリューション
13:30 ~ @第4会議室
•これから Scala を始めたい方向けです
• Servlet に馴染みがあるなら、まず Skinny で Scala を始めてみましょう
•安定版の 1.3 or skinny-engine-server で
• 作者なので使い方の質問すぐ答えます
• 13:30 - 第 4 会議室でお待ちしています