38
Servlet と Future の関わり方 Kazuhiro Sera @seratch_ja 2015/08/01

Servlet と Future の関わり方 #scala_ks

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• Future scaladoc

• Future.apply { ここは別スレッド }

• Future.successful { ここは同じスレッド }

• カジュアルに別スレッドの処理が生まれる、適当にやってもスレッドプール(ExecutionContext で指定)で管理される

• Future を生んだ元スレッドの値を Future 側のスレッドから簡単に参照できてしまう(後述)

Servlet API の mutability

mutability

• request#setAttribute(String, AnyRef) を始めとする setter メソッド

• request attributes に依存する実装パターン

• session などコンテナ管理のオブジェクト

•任意のタイミングで response を出力

•マルチスレッドでアクセスする場合、素のままで扱うのはあやうい

Async モードでの request リサイクル問題

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 まで維持されると考えてよいはず

Scalatra の DynamicScope 問題

この Servlet 上で作られた

Future のスレッド内も含めどこからでもこの request に気軽にアクセスできてしまう

request/response

• Scalatra の request/response はスレッドローカル、Future 側からそのまま触れない

• implicit パラメータで引き渡すアプローチにすべての DSL、拡張が対応していない、意図せず DynamicScope が呼び出される

•最初期の目的・設計は妥当だった(Future はまだメインストリームでなかった、模倣した Sinatra は Rack アプリ)

Skinny 2 での ソリューション

Skinny 2.0

skinny-engine

skinny-engine

• Scalatra を fork して DSL の互換性は維持しつつ、内部実装を大きく変えた

• trait の構成はあまり互換性を維持していない(改名・構造の変更・削除)

• Scalatra の既存のテストをほぼそのままの状態ですべて pass している

•改善アイデア、潜在的問題の発見があった、Scalatra にもフィードバックしていきたい

stable request

stable request

• request を Future が処理中のタイミングで recycle されたとしても read-only な情報、request attributes 等へのアクセスを保証

• request attributes の更新も考慮

•コンテナ管理オブジェクトの操作はメインスレッド以外ではアクセスエラーに(2.0.0.M3)

•少なくとも Jetty、Tomcat で動作保証

No more DynamicScope

No more DynamicScope

•前提として、すべての DSL に Context を implicit parameter として渡す

• Context を引数にとる関数をアクションとしてとる AsyncSkinnyEngineServlet/Filter

• Scalatra 互換な trait では ’error: ambiguous implicit values’(コンパイルエラー)を発生させて確実に潰す

New async trait

Scalatra compatible trait

話したこと•前提・背景

•スレッドローカルが存在する世界

• Future はスレッドプールが裏にいる

• Servlet API の mutability

• Async モードでの request リサイクル問題

• Scalatra の DynamicScope 問題

• Skinny 2 でのソリューション

ハンズオンの宣伝

Minimum Skinny 2 App

おそらく一番簡単なScala Web アプリ実装

13:30 ~ @第4会議室

•これから Scala を始めたい方向けです

• Servlet に馴染みがあるなら、まず Skinny で Scala を始めてみましょう

•安定版の 1.3 or skinny-engine-server で

• 作者なので使い方の質問すぐ答えます

• 13:30 - 第 4 会議室でお待ちしています

おおきに :)