Play 2.6 在Play中使用快取
使用快取
對資料進行快取是一種很常見的優化方式,Play也提供了全聚德快取。對於cache有一點很重要,快取只能做快取能做的:你剛儲存的資料也許會丟失。
對於任何儲存在快取中的資料,當資料丟失時需要一個重新生成的策略。這個哲學存在於Play的基礎之中,而且與Java EE不同,session被期待在整個生命週期中儲存資料。
Play預設的快取API是Ehcache
匯入快取API
Play提供了API和預設的Ehcache實現。可以通過以下配置獲取完全的Ehcache實現
libraryDependencies ++= Seq(
ehcache
)
這會在執行時自動進行注入。
如果只想新增API,可以使用如下配置
libraryDependencies ++= Seq(
cacheApi
)
如果你先定義自己的Cached
helper和AsynCacheAPI
不依賴域Ehcache的繫結,這個API依賴會很有用。如果你使用自己定的快取模組只能使用這個配置。
JCache的支援
Ehcache支援JSR107標準,也被成為JCache,但是Play預設不繫結javax.caching.CacheManager。為了繫結javax.caching.CacheManager給預設的provider,新增以下配置
libraryDependencies + = jcache
如果你使用Guice,可以通過下面的配置來新增Java註解
libraryDependencies += "org.jsr107.ri" % "cache-annotations-ri-guice" % "1.0.0"
獲取快取API
快取API定義在AsyncCacheAPI和SyncCacheAPI兩個介面中,根據你需要同步或者非同步的實現,注入到你的類中
import play.cache.*;
import play.mvc.*;
import javax.inject.Inject;
public class Application extends Controller {
private AsyncCacheApi cache;
@Inject
public Application(AsyncCacheApi cache) {
this.cache = cache;
}
// ...
}
Note: The API is intentionally minimal to allow various implementations to be plugged in. If you need a more specific API, use the one provided by your Cache library. |
向快取中寫資料
CompletionStage<Done> result = cache.set("item.key", frontPageNews);
也可以設定一個時間(以秒為單位)
// Cache for 15 minutes
CompletionStage<Done> result = cache.set("item.key", frontPageNews, 60 * 15);
讀取資料
CompletionStage<News> news = cache.get("item.key");
也可以提供一個Callabel
在快取中資料不存在時生成一個
CompletionStage<News> maybeCached = cache.getOrElseUpdate("item.key", this::lookUpFrontPageNews);
注意:getOrElseUpdate
在Ehcache中不是一個原子操作,在實現上先是一個get操作然後是從Callable中計算值最後是set操作。這意味著在多執行緒的情況下會被計算多次。
刪除一個資訊
CompletionStage<Done> result = cache.remove("item.key");
清空cache
CompletionStage<Done> resultAll = cache.removeAll();
removeAll()
只在非同步API中可用,因為很少有情況你需要同步清空cache。只有在一些特殊情況下才需要管理員來清空cache,這不是應用的常規操作。
SyncCacheApi具有相同的API,只是返回值不是用future封裝的。
獲取不同的快取
在預設的Ehcache實現中,預設的快取叫做play,可以通過ehcache.xml進行配置。別的快取可以使用不同的配置甚至是不同的實現。
如果你需要多種不同的快取,可以在application.conf中進行繫結
play.cache.bindCaches = ["db-cache", "user-cache", "session-cache"]
預設情況下Play會為你建立這些快取。如果你想要在ehcache.xml中進行配置,你可以選擇
play.cache.createBoundCaches = false
為了在注入時獲取不同的快取,在依賴上使用NamedCache。
import play.cache.*;
import play.mvc.*;
import javax.inject.Inject;
public class Application extends Controller {
@Inject @NamedCache("session-cache") SyncCacheApi cache;
// ...
}
設定執行上下文
預設情況下Ehcache操作都是阻塞的,非同步的實現會阻塞預設執行上下文的執行緒。
如果你使用Play的預設設定一般情況下不會有問題,只會在內容中存資料所以需要讀的儘可能快。
考慮到Ehcache的配置和資料存貯的介質。使用阻塞操作可能太浪費了。
你可以配置一個不同的Akka dispatcher,然後通過play.cache.dispatcher來配置
play.cache.dispatcher = "contexts.blockingCacheDispatcher"
contexts {
blockingCacheDispatcher {
fork-join-executor {
parallelism-factor = 3.0
}
}
}
快取HTTP響應
通過Action組合可以很容易實現
@Cached(key = "homePage")
public Result index() {
return ok("Hello world");
}
自定義的快取實現
你可以提供一個自定義的實現來取代或者和預設實現一起使用。
如果要取代預設實現,只需要在build.sbt中新增依賴。如果仍需要Ehcache的實現,可以在application.conf中停用自動繫結
play.modules.disabled += "play.api.cache.ehcache.EhCacheModule"
你可以實現AsyncCacheApi然後繫結到DI容器中。也可以將SyncCacheApi繫結到 DefaultSyncCacheApi中。
注意你的實現不支援removeAll方法,也許是不可能實現也許是不必要。你可以在removeAll方法中丟擲一個UnsupportedOperationException 。
為了提供一個快取API實現,你可以自己建立一個qualifier或者重用NamedCache來繫結你的實現。