文件

§Play 快取 API

快取資料是現代應用程式中常見的最佳化手法,因此 Play 提供了全域快取。

注意:快取的重要一點是,它的行為就像快取一樣:您剛剛儲存的資料可能會消失。

對於儲存在快取中的任何資料,如果資料消失,就需要制定一個重新產生策略。這種理念是 Play 背後的其中一個基本原則,與 Java EE 不同,Java EE 預期會話會在整個生命週期中保留值。

Play 提供了一個基於 Caffeine 的 CacheApi 實作,以及一個基於 Ehcache 2.x 的舊版實作。對於處理中的快取,Caffeine 通常是最佳選擇。如果您需要分散式快取,有第三方外掛程式可供 memcachedredis 使用。

§匯入快取 API

Play 提供 Cache API 和 Caffeine 和 Ehcache 實作的獨立相依性。

§Caffeine

若要取得 Caffeine 實作,請將 caffeine 加入您的相依性清單

libraryDependencies ++= Seq(
  caffeine
)

這也會自動設定執行時期 DI 的繫結,讓元件可注入。

§EhCache

若要取得 EhCache 實作,請將 ehcache 加入您的相依性清單

libraryDependencies ++= Seq(
  ehcache
)

這也會自動設定執行時期 DI 的繫結,讓元件可注入。

§自訂快取實作

若要只加入 API,請將 cacheApi 加入您的相依性清單。

libraryDependencies ++= Seq(
  cacheApi
)

如果您想為 Cached 輔助程式和 AsyncCacheApi 等定義自己的繫結,而不依賴任何特定快取實作,API 相依性會很有用。如果您正在撰寫自訂快取模組,您應該使用這個。

§存取快取 API

快取 API 由 AsyncCacheApiSyncCacheApi 特質定義,視您是要非同步或同步實作而定,而且可以像任何其他相依性一樣注入到您的元件中。例如

import javax.inject.Inject

import play.api.cache._
import play.api.mvc._

class Application @Inject() (cache: AsyncCacheApi, cc: ControllerComponents) extends AbstractController(cc) {}

注意:API 故意簡化,以允許插入多個實作。如果您需要更具體的 API,請使用快取外掛程式提供的 API。

使用這個簡單的 API,您可以在快取中儲存資料

val result: Future[Done] = cache.set("item.key", connectedUser)

然後稍後擷取資料

val futureMaybeUser: Future[Option[User]] = cache.get[User]("item.key")

還有一個方便的輔助程式,用於從快取中擷取資料或在資料不存在時設定快取中的值

val futureUser: Future[User] = cache.getOrElseUpdate[User]("item.key") {
  User.findById(connectedUser)
}

注意getOrElseUpdate 在 EhCache 中並非原子操作,而是實作為先 get,再計算值,然後 set。這表示如果有多個執行緒同時呼叫 getOrElse,則可能會計算值多次。

你可以傳遞持續時間來指定到期時間,預設持續時間為無限長

import scala.concurrent.duration._

val result: Future[Done] = cache.set("item.key", connectedUser, 5.minutes)

若要從快取中移除項目,請使用 remove 方法

val removeResult: Future[Done] = cache.remove("item.key")

若要從快取中移除所有項目,請使用 removeAll 方法

val removeAllResult: Future[Done] = cache.removeAll()

removeAll() 僅在 AsyncCacheApi 中可用,因為很少需要同步移除快取中的所有元素。預期只有在特殊情況下才需要以管理員操作移除快取中的所有項目,而不是應用程式的正常操作的一部分。

請注意,SyncCacheApi 具有相同的 API,只不過它直接傳回值,而不使用未來。

§存取不同的快取

可以依據名稱定義和使用具有不同組態的不同快取。若要存取不同的快取,請在注入快取時,對你的相依關係使用 NamedCache 限定詞,例如

import javax.inject.Inject

import play.api.cache._
import play.api.mvc._

class Application @Inject() (
    @NamedCache("session-cache") sessionCache: AsyncCacheApi,
    cc: ControllerComponents
) extends AbstractController(cc) {}

如果你要存取多個不同的快取,則需要指示 Play 在 application.conf 中繫結這些快取,如下所示

play.cache.bindCaches = ["db-cache", "user-cache", "session-cache"]

定義和組態命名快取取決於你使用的快取實作,以下是使用 Caffeine 組態命名快取的範例。

§使用 Caffeine 組態命名快取

如果您想傳遞將用作所有快取的備份的預設自訂組態,您可以透過指定來執行

    play.cache.caffeine.defaults = {
        initial-capacity = 200
        ...
    }

您也可以透過執行來傳遞特定快取的自訂組態資料

    play.cache.caffeine.user-cache = {
        initial-capacity = 200
        ...
    }

§使用 EhCache 組態命名快取

使用 EhCache 實作時,預設快取稱為 play,且可以透過建立名為 ehcache.xml 的檔案來組態。其他快取可以使用不同的組態,甚至實作來組態。

預設情況下,Play 會嘗試為您建立具有來自 play.cache.bindCaches 名稱的快取。如果您想在 ehcache.xml 中自行定義,您可以設定

play.cache.createBoundCaches = false

§設定執行內容

預設情況下,Caffeine 和 EhCache 會將元素儲存在記憶體中。因此,讀取和寫入快取的速度應該非常快,因為幾乎沒有任何封鎖 I/O。
然而,視快取的組態方式而定(例如,使用 EhCache 的 DiskStore),可能會出現封鎖 I/O,這可能會變得太昂貴,因為即使非同步實作也會封鎖預設執行內容中的執行緒。

對於這種情況,您可以組態不同的 Pekko 分派器,並透過 play.cache.dispatcher 設定,讓快取外掛程式使用它

play.cache.dispatcher = "contexts.blockingCacheDispatcher"

contexts {
  blockingCacheDispatcher {
    fork-join-executor {
      parallelism-factor = 3.0
    }
  }
}

§Caffeine

使用 Caffeine 時,這將設定 Caffeine 的 內部執行器。實際上,設定 play.cache.dispatcher 會設定 play.cache.caffeine.defaults.executor。如同 上述所述,因此您可以為不同的快取設定不同的執行器

    play.cache.caffeine.user-cache = {
        executor = "contexts.anotherBlockingCacheDispatcher"
        ...
    }

§EhCache

對於 EhCache,Play 會在給定分派器的執行緒上以 Future 執行任何 EhCache 作業。

§快取 HTTP 回應

您可以輕鬆使用標準動作組合來建立智慧快取動作。

注意:Play HTTP Result 實例可以安全快取並在稍後重複使用。

Cached 類別可協助您建立快取動作。

import javax.inject.Inject

import play.api.cache.Cached

class Application @Inject() (cached: Cached, cc: ControllerComponents) extends AbstractController(cc) {}

您可以使用固定金鑰(例如 "homePage")快取動作的結果。

def index = cached("homePage") {
  Action {
    Ok("Hello world")
  }
}

如果結果有所不同,您可以使用不同的金鑰快取每個結果。在此範例中,每個使用者都有不同的快取結果。

def userProfile = WithAuthentication(_.session.get("username")) { userId =>
  cached(req => "profile." + userId) {
    Action.async {
      User.find(userId).map { user => Ok(views.html.profile(user)) }
    }
  }
}

§控制快取

您可以輕鬆控制要快取的內容或要從快取中排除的內容。

您可能只想快取 200 Ok 結果。

def get(index: Int) = cached.status(_ => "/resource/" + index, 200) {
  Action {
    if (index > 0) {
      Ok(Json.obj("id" -> index))
    } else {
      NotFound
    }
  }
}

或僅快取 404 Not Found 幾分鐘

def get(index: Int) = {
  val caching = cached
    .status(_ => "/resource/" + index, 200)
    .includeStatus(404, 600)

  caching {
    Action {
      if (index % 2 == 1) {
        Ok(Json.obj("id" -> index))
      } else {
        NotFound
      }
    }
  }
}

§自訂實作

可以提供快取 API 的自訂實作。請確定您有 cacheApi 相依性。

接著,您可以實作 AsyncCacheApi 並將其繫結在 DI 容器中。您也可以將 SyncCacheApi 繫結到 DefaultSyncCacheApi,後者僅封裝非同步實作。

請注意,removeAll 方法可能不受您的快取實作支援,原因可能是不可行或會造成不必要的低效率。如果是這種情況,您可以在 removeAll 方法中擲回 UnsupportedOperationException

若要提供快取 API 的實作,除了預設實作之外,您可以建立自訂限定詞,或重複使用 NamedCache 限定詞來繫結實作。

§與自訂實作一起使用 Caffeine

若要使用 Caffeine 的預設實作,您需要 caffeine 相依性,而且必須在 application.conf 中停用 Caffeine 模組自動繫結。

play.modules.disabled += "play.api.cache.caffeine.CaffeineCacheModule"

§將 EhCache 與自訂實作搭配使用

若要使用 EhCache 的預設實作,您將需要 ehcache 相依性,而且必須停用 EhCache 模組在 application.conf 中自動繫結。

play.modules.disabled += "play.api.cache.ehcache.EhCacheModule"

下一步: 使用 Play WS 呼叫 REST API


在這個文件裡發現錯誤嗎?此頁面的原始程式碼可以在 這裡 找到。在閱讀 文件指南 後,請隨時貢獻一個 pull request。有問題或建議要分享嗎?請前往 我們的社群論壇 與社群展開對話。