2014/12/13 第1回 Scala関西勉強会 play2-memcached supports Play 2.4 ~Play...

Preview:

Citation preview

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/

質疑応答

Recommended