2014/12/13 第1回 scala関西勉強会 play2-memcached supports play 2.4 ~play...
TRANSCRIPT
play2-memcached supports Play 2.4 !
~ Play 2.4モジュールのつくりかた ~
@mumoshu
九岡 佑介(くおか ~)https://www.wantedly.com/users/392910
@mumoshu
• 2008年~ NECビッグローブ (JavaScript)
• 2010年~ フリュー (JavaScript/Scala)
• 2013年~ グリー (Scala/Ruby/インフラ)
• 2014年~ クラウドワークス(Ruby/インフラ)
福井県出身おろし蕎麦が好き
クラウドワークスクラウドソーシングサービス http://crowdworks.jp/
仕事を探す・仕事をする・報酬をもらうインターネット上で完結
仕事の発注と受注ができるリモートワーク前提
188種類の仕事が発注できる
“クラウドソーシングは労働の「オープンソース化」”ジェフ・ハウ http://crowdworks.jp/adviser
“自分が将来リモートワークをする際の下地づくり”@mumoshu
「働く」を通して人々に笑顔を クラウドワークスのスローガン http://crowdworks.co.jp/
「ありがとう」ボタン
感謝の気持ちの可視化
アジェンダ• play2-memcachedについて
• Playプラグイン?モジュール?
• Play 2.3までのキャッシュプラグイン
• Play 2.4からのキャッシュモジュール
• Play 2.4でキャッシュモジュールを書くときに知っておきたいこと
play2-memcached
play2-memcached
• https://github.com/mumoshu/play2-memcached
• Play2向けキャッシュ実装の一つ
play2-memcached• Play2標準はEhCacheベースのキャッシュ
• EhCacheはJavaベースのキャッシュ。Playではインメモリキャッシュとして、Play 1の時代から利用されている
• play2-memcachedを使うと、標準のキャッシュと同じインタフェースでMemcachedベースのキャッシュが使える
• Play2.0時代から存在。そこそこ歴史あり
アジェンダ• play2-memcachedについて
• Playプラグイン?モジュール?
• Play 2.3までのキャッシュプラグイン
• Play 2.4からのキャッシュモジュール
• Play 2.4でキャッシュモジュールを書くときに知っておきたいこと
Playプラグイン?モジュール?
Playプラグイン?モジュール?
• Playのバージョンによって、Playの拡張方法が異なります
Playプラグイン?モジュール?• Play 2.3まで
• Playプラグインでフレームワークを拡張する
• Play 2.4から
• Playモジュールで拡張する
• 2.4.0-M2時点ではプラグインもまだ使えるようだが、既にPlayの組み込みプラグインはモジュールに置き換えされている
Playプラグイン?モジュール?
• キャッシュプラグイン/モジュールでPlayを拡張すると、EhCache以外のキャッシュを使えるようになる
• 例えばplay2-memcached
アジェンダ• play2-memcachedについて
• Playプラグイン?モジュール?
• Play 2.3までのキャッシュプラグイン
• Play 2.4からのキャッシュモジュール
• Play 2.4でキャッシュモジュールを書くときに知っておきたいこと
Play 2.3までのキャッシュプラグイン
Play 2.3までのキャッシュ• play.api.cache.CachePlugin
• play.api.cache.CacheAPI
Play 2.3までのキャッシュ• play.api.cache.CachePlugin
• play.api.cache.CacheAPIの実装を返す
• Playの起動・終了時に処理をフックできる
• play.api.cache.CacheAPI
• キャッシュの読み書きのインタフェース
Play 2.3までのキャッシュ• イメージ
• EhcachePluginがEhcacheCacheAPIImplを提供
• MemcachedPluginがMemcachedCacheAPIImplを提供
• RedisPluginがRedisCacheAPIImplを提供
• みんなCacheAPIで同様に扱える!
Play 2.3までのキャッシュ• Playプラグインの機構で、PlayにCachePluginをロードさせる
• 標準のEhCachePluginを無効化してMemcachedPluginを有効化すれば、PlayはMemcachedの方を使う
CacheAPIをロードしてみよう• 突然のScalaコード
# application.confに設定が書かれている前提で…
val app = new play.core.StaticApplication(new java.io.File(“.”))
# 2.3までの、Playがプラグイン経由でMemcachedベースのキャッシュ実装をロードする方法
play.api.Play.current.plugin[play.api.cache.CachePlugin]
//=> Option[play.api.cache.CachePlugin] = Some(com.github.mumoshu.play2.memcached.MemcachedPlugin@47afef67)
# テストならapplication.confに相当する設定をもとに…
val app = play.api.test.FakeApplication( additionalConfiguration = Map( "ehcacheplugin" -> "disabled", "memcached.host" -> "127.0.0.1:11211" ) )
play.api.Play.current.plugin[play.api.cache.CachePlugin]
アジェンダ• play2-memcachedについて
• Playプラグイン?モジュール?
• Play 2.3までのキャッシュプラグイン
• Play 2.4からのキャッシュモジュール
• Play 2.4でキャッシュモジュールを書くときに知っておきたいこと
Play 2.4からのキャッシュモジュール
Play 2.4からのキャッシュ• play.api.inject.Module
• play.api.inject.ApplicationLifecycle
Play 2.4からのキャッシュ• play.api.inject.Module
• play.api.cache.CacheApiの実装を返す(2.3まではCachePluginでしたね)
• play.api.inject.ApplicationLifecycle
• Playの終了時に処理をフックできる(2.3まではCachePluginでしたね)
Play 2.4からのキャッシュ• プラグインとは別の「Playモジュール」の機構を使って、Playの標準モジュールを入れ替えたり、追加したりできる。
• 標準のEhCacheModuleを無効化して、代わりにMemcachedModuleを追加すればCacheApiの実装を変えることができる
CacheApiをロードしてみよう• 突然のScalaコード
# 2.4からの、Playがモジュール経由でMemcachedベースのキャッシュ実装をロードする方法app.injector.instanceOf(classOf[play.api.cache.CacheApi])
//=> play.api.cache.CacheApi = com.github.mumoshu.play2.memcached.MemcachedCacheApi@52556f47
Play 2.3との違い• 突然のScalaコード
# 2.3までの、Playがプラグイン経由でMemcachedベースのキャッシュ実装をロードする方法
play.api.Play.current.plugin[play.api.cache.CachePlugin].get.api
//=> play.api.cache.CacheAPI = com.github.mumoshu.play2.memcached.MemcachedPlugin$$anon$2@6ba816fe
# 2.4からの、Playがモジュール経由でMemcachedベースのキャッシュ実装をロードする方法app.injector.instanceOf(classOf[play.api.cache.CacheApi])
//=> play.api.cache.CacheApi = com.github.mumoshu.play2.memcached.MemcachedCacheApi@52556f47
Play 2.3との違い• これだけだと書き方の違いだけ、という印象
テスト中でCacheApiを使いたいとき• 突然のScalaコード
val app = play.api.test.FakeApplication( additionalConfiguration = Map( "play.modules.enabled" -> List("com.github.mumoshu.play2.memcached.MemcachedModule"), "play.modules.disabled" -> List("play.api.cache.EhCacheModule"), "play.modules.cache.defaultCache" -> “default”, "memcached.1.host" -> memcachedHost )
)
val cacheApi = app.injector.instanceOf(classOf[play.api.cache.CacheApi])
アジェンダ• play2-memcachedについて
• Playプラグイン?モジュール?
• Play 2.3までのキャッシュプラグイン
• Play 2.4からのキャッシュモジュール
• Play 2.4でキャッシュモジュールを書くときに知っておきたいこと
Play 2.4でキャッシュ関連のモジュールを書くときに知っておきたいこと ~パッケージ編~
これだけは知っておきたい パッケージ
• play.api.inject
• play.api.cache
これだけは知っておきたい クラス
• play.api.inject.Module
• play.api.cache.CacheApi
play.api.inject
play.api.inject• Play2のDependency InjectionのAPI
• Play2にはDIフレームワークのラッパとなるAPIと、デフォルトの実装が同梱
• デフォルト実装はGoogleのDIフレームワーク Guiceベース
• Play 2.4からはPluginは非推奨になり、play.api.inject.ModuleによってPlayを拡張
play.api.cache
play.api.inject• Play2のDependency InjectionのAPI
• Play2にはDIフレームワークのラッパとなるAPIと、デフォルトの実装が同梱
• デフォルト実装はGoogleのDIフレームワーク Guiceベース
• Play 2.4からはPluginは非推奨になり、play.api.inject.ModuleによってPlayを拡張
Play 2.4でキャッシュ関連のモジュールを書くときに知っておき
たいこと ~クラス編~
play.api.cache.CacheAPI /
play.api.cache.CacheApi
CacheAPI/CacheApi
• Play2のキャッシュが満たすべきインタフェース
• CacheAPIを実装していればEhCache、Memcached、Redisなど同じように扱えるうれしさ
• Play 2.4で少し変わります
CacheAPI/CacheApi
• 新(2.4から)・旧(2.3まで)のソースを見てみましょう
# Play 2.3まで
trait CacheAPI {
def set(key: String, value: Any, expiration: Int)
def get(key: String): Option[Any]
def remove(key: String)
}
# Play 2.4から
trait CacheApi {
def set(key: String, value: Any, expiration: Duration = Duration.Inf)
def get[T: ClassTag](key: String): Option[T]
def remove(key: String)
def getOrElse[A: ClassTag](key: String, expiration: Duration = Duration.Inf)(orElse: => A): A
}
CacheAPI/CacheApi
• get…値の取得
• set…値の設定
• remove…値の削除
• getOrElse…値があれば取得、なければ値を指定したデフォルト値で更新して返す
CacheAPI/CacheApi• 本質的にはこれらのメソッドを実装すれば、標準的なインタフェースでEhCache以外のキャッシュが使えます
• 簡単そう
• これをPlayのDependency Injectionの仕組みを活用して、Playモジュールにまとめ上げるのが最初大変かも?
play.api.cache.CachePlugin
play.api.cache.CachePlugin
• Play2.3までのキャッシュのインタフェース
• これを実装して標準外のキャッシュを提供
play.api.inject.ApplicationLifecycle
ApplicationLifecycle
• Play 2.4版play2-memcachedではPlay終了時にMemcachedクライアントのリソースを解放するため、ApplicationLifecycleを利用
• Play 2.3版まではPlugin中で同様の処理
• Play 2.4版からはMemcachedClientProviderで
trait ApplicationLifecycle {
def addStopHook(hook: () => Future[Unit]): Unit
def addStopHook(hook: Callable[F.Promise[Void]]): Unit = {
import play.api.libs.iteratee.Execution.Implicits.trampoline
addStopHook(() => hook.call().wrapped().map(_ => ()))
}
}
ApplicationLifecycle
• Play 2.4版play2-memcachedではPlay終了時にMemcachedクライアントのリソースを解放するため、ApplicationLifecycleを利用
val lifecycle: play.api.inject.ApplicationLifecycle
val memcachedClient: net.spy.memcached.MemcachedClient
!!!
lifecycle.addStopHook(() => Future.successful { logger.info("Stopping MemcachedPlugin.") client.shutdown() })
ApplicationLifecycle
• Play 2.3版まではPlugin中で同様の処理
• Play 2.4版からはMemcachedClientProviderで
ApplicationLifecycle
• 詳しくは↓
https://github.com/playframework/playframework/blob/2.4.0-M2/framework/src/play/src/main/scala/play/api/inject/ApplicationLifecycle.scala
play.api.inject.Module
play.api.inject.Module• 型からインスタンスへのバインディングの集合体
• Playはモジュールを使って依存性を解決
play.api.inject.Module• bind[CacheApi].to(new
MemcachedCacheApiProvider(…))で「CacheApiの実装はMemcachedApiProvider
から得られますよ」ということをPlayに伝える
• Playに「CacheApiの実装ください」というと、MemcachedApiProviderから得たCacheApiの実装を返す
Playモジュールをつくりたくなってきましたね?
Playモジュールをつくるときに参考になるソース
play.api.cache.EhCacheModule• Play2.4標準のEhCacheベースのキャッシュを提供するModule
https://github.com/playframework/playframework/blob/2.4.0-M2/framework/src/play-cache/src/main/scala/play/api/cache/Cache.scala
• 実質的にリファレンス実装ですよね
play.api.cache.EhCachePlugin• Play 2.3まで標準のEhCacheベースのキャッシュを提供するPlugin
• https://github.com/playframework/playframework/blob/2.3.7/framework/src/play-cache/src/main/scala/play/api/cache/Cache.scala
• ModuleよりPlugin時代のほうがとっつきやすいと思うので、まずPlay 2.3向けにプラグイン書くことからはじめてもいいかも
play2-memcachedのコード• https://github.com/mumoshu/play2-
memcached/tree/play-2.4/plugin/src/main/play_2.4/com/github/mumoshu/play2/memcached
Playモジュールってどうなの?
• Playプラグインからモジュールに変わって、結局何がうれしいの?
Playモジュールってどうなの?• Pluginに集中しがちだったコードが綺麗に別れる
• これまでPluginにはCacheAPI実装の提供と、Playのライフサイクルイベントにフックした処理、という2つの役割があった
• これがプラグイン以外のクラスに別れて、複雑なPlay2モジュールが作りやすくなってるはず
Playモジュールってどうなの?
• これまでPlugin化されていないPlayのコア機能が多く、込み入った拡張ができないケースがあった
• Module導入を機に、DIできる箇所が増えた模様。結果的にModuleを使うと、いままでより込み入った拡張がしやすくなってると思われる
trait Plugin { /** * Called when the application starts. */ def onStart() {} /** * Called when the application stops. */ def onStop() {} /** * Is the plugin enabled? */ def enabled: Boolean = true }
Playモジュールってどうなの?
Playモジュールってどうなの?• よさそう
そろそろまとめ
TODO• Moduleによる実行時の依存性解決だけでなく、コンパイル時に依存性解決にも対応する(CakePattern)
• 必須ではないがPlay2のドキュメントでも推奨されている
• https://www.playframework.com/documentation/2.4.0-M2/ScalaCompileTimeDependencyInjection
TODO• Play 2.0.x, 2.1.x, 2.2.x, 2.3.x, 2.4.x向けのクロスビルド
• 2.3と2.4間で互換性のない変更があるので、同じソースではコンパイルエラー。ソースわけないとだめ
• 今はPlayバージョン毎にブランチを分けててメンテがつらい
TODO
• Play 2.0.x, 2.1.x, 2.2.x, 2.3.x, 2.4.x向けのクロスビルド
• 最新ブランチでは以下のようにビルド仕分けられるようにした
PLAY_VERSION=2.3.0 sbt PLAY_VERSION=2.4.0-M2 sbt
まとめ• Playプラグインは2.4からPlayモジュールに
• play.api.{cache, inject}パッケージ
• play.api.inject.{Module, ApplicationLifecycle}
• play.api.cache.CacheApi
• play2-memcachedはPlay 2.4に対応してます
まとめ• コンパイル時の依存性解決に対応
• 複数バージョンのPlayとクロスビルドしたい
Playモジュールをつくろう!
We are hiring!https://crowdworks.co.jp/recruit/engineer/
質疑応答