1. 程式人生 > >Play 2.6 在Play中使用快取

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
)

如果你先定義自己的Cachedhelper和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來繫結你的實現。