欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Play 2.6 在Play中使用缓存

程序员文章站 2022-05-17 20:05:30
...

使用缓存

https://playframework.com/documentation/2.6.x/JavaCache

对数据进行缓存是一种很常见的优化方式,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来绑定你的实现。