文档

§Play 缓存 API

缓存数据是现代应用程序中常见的优化方式,因此 Play 提供了一个全局缓存。

注意:关于缓存的一个重要点是,它的行为就像缓存应该的那样:您刚刚存储的数据可能会丢失。

对于存储在缓存中的任何数据,都需要制定一个再生策略,以防数据丢失。这种理念是 Play 背后的基本原则之一,它与 Java EE 不同,在 Java EE 中,会话预期在其整个生命周期内保留值。

Play 提供了一个基于 Caffeine 的 CacheApi 实现,以及一个基于 Ehcache 2.x 的传统实现。对于进程内缓存,Caffeine 通常是最佳选择。如果您需要分布式缓存,则有针对 memcachedredis 的第三方插件。

§导入缓存 API

Play 为缓存 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.cache.*;
import play.mvc.*;

public class Application extends Controller {

  private AsyncCacheApi cache;

  @Inject
  public Application(AsyncCacheApi cache) {
    this.cache = cache;
  }

  // ...
}

注意:API 故意保持最小化,以允许插入各种实现。如果您需要更具体的 API,请使用您的缓存库提供的 API。

使用此简单 API,您可以在缓存中存储数据

CompletionStage<Done> result = cache.set("item.key", frontPageNews);

您可以选择为缓存指定一个过期时间(以秒为单位)

// Cache for 15 minutes
CompletionStage<Done> result = cache.set("item.key", frontPageNews, 60 * 15);

您以后可以检索数据

CompletionStage<Optional<News>> news = cache.get("item.key");

您还可以提供一个 Callable,它在缓存中找不到值时生成存储值

CompletionStage<News> maybeCached =
    cache.getOrElseUpdate("item.key", this::lookUpFrontPageNews);

注意getOrElseUpdate 在 EhCache 中不是原子操作,而是作为 get 后跟从 Callable 计算值,然后是 set 来实现。这意味着如果多个线程同时调用 getOrElse,则可能多次计算值。

要从缓存中删除项目,请使用 remove 方法

CompletionStage<Done> result = cache.remove("item.key");

要从缓存中删除所有项目,请使用 removeAll 方法

CompletionStage<Done> resultAll = cache.removeAll();

removeAll() 仅在 AsyncCacheApi 上可用,因为删除缓存中的所有元素很少是您想同步执行的操作。预期是,删除缓存中的所有项目仅在特殊情况下作为管理员操作需要,而不是应用程序正常运行的一部分。

请注意,SyncCacheApi 具有相同的 API,只是它直接返回值,而不是使用 future。

§访问不同的缓存

可以通过名称定义和使用具有不同配置的不同缓存。要访问不同的缓存,在注入它们时,请在您的依赖项上使用 NamedCache 限定符,例如

import javax.inject.Inject;
import play.cache.*;
import play.mvc.*;

public class Application extends Controller {

  @Inject
  @NamedCache("session-cache")
  SyncCacheApi cache;

  // ...
}

如果您想访问多个不同的缓存,则需要在application.conf中告诉 Play 绑定它们,如下所示

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 响应

您可以使用标准的Action组合轻松创建智能缓存操作。

提示:Play HTTP Result 实例可以安全地缓存并在以后重用。

Play 为标准情况提供了一个默认的内置帮助程序

@Cached(key = "homePage")
public Result index() {
  return ok("Hello world");
}

§自定义实现

可以提供缓存 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 依赖项,并且您必须在 application.conf 中禁用 EhCache 模块自动绑定它。

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

下一步: 使用 Play WS 调用 REST API


在本文档中发现错误?此页面的源代码可以在 此处 找到。在阅读 文档指南 后,请随时贡献拉取请求。有疑问或建议要分享?前往 我们的社区论坛 与社区进行交流。