Scala Play 建議使用 EHCache 作為 cache solution,這在 java 領域是一個常見的套件,目前先看看官方的 cache 方式,畢竟已經整合到 play framework 中,未來再看看有沒有辦法改用 redis。
設定 cache
build/sbt 中增加 cache 這個 libraryDependencies
libraryDependencies ++= Seq(
cache,
...
)
app/controllers/CacheApplication.scala
- cache.set 新增 cache item
- cache.get 取得 cache item
- cache.remove 移除 cache item
- cache.getOrElse 取得 cache item,如果找不到就新增
package controllers
import java.util.concurrent.TimeoutException
import javax.inject.Inject
import akka.actor.ActorSystem
import akka.pattern.after
import models.{Project, ProjectRepo, TaskRepo}
import play.api.Logger
import play.api.cache.CacheApi
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.mvc.{Action, Controller}
import scala.concurrent.Future
import scala.concurrent.duration._
class CacheApplication @Inject()(cache: CacheApi)
extends Controller {
def newCache(name: String) = Action {
val result = s"Add new project ${name} to cache..\n"
val project: Project = Project(999, name)
cache.set("project."+name, project)
Ok(result)
}
def newCache2(name: String) = Action {
val result = s"Add new project ${name} to cache with 5 minuts..\n"
val project: Project = Project(888, name)
cache.set("project."+name, project, 5.minutes)
// 移除 cache item
//cache.remove("project."+name)
Ok(result)
}
def getCache(name:String) = Action {
val project: Option[Project] = cache.get[Project]("project."+name)
val result = project match {
// 以 Some 測試 name 有沒有存在
case Some(pj) => s"get project from cache: ${pj.name}"
case None => s"can't get project from cache.."
}
Ok(result)
}
def getCache2(name:String) = Action {
val project: Project = cache.getOrElse[Project]("project."+name) {
// 如果 cache 中找不到 project.name 就產生一個新的,存到 cache 中
Project(777, name)
}
val result = s"getOrElse project from cache: ${project.name}"
Ok(result)
}
}
conf/routes
GET /cache/:name controllers.CacheApplication.newCache(name:String)
GET /cache2/:name controllers.CacheApplication.newCache2(name:String)
GET /getcache/:name controllers.CacheApplication.getCache(name:String)
GET /getcache2/:name controllers.CacheApplication.getCache2(name:String)
測試
$ curl 'http://localhost:9000/cache/cproject'
Add new project cproject to cache..
$ curl 'http://localhost:9000/getcache/cproject'
get project from cache: cproject
$ curl 'http://localhost:9000/cache2/cproject'
Add new project cproject to cache with 5 minuts..
$ curl 'http://localhost:9000/getcache2/cproject'
getOrElse project from cache: cproject
預設的 cache store
預設緩存叫 play, 並可以利用 ehcache.xml 來設定新的 cache store。
調整 application.conf
play.cache {
# If you want to bind several caches, you can bind the individually
bindCaches = ["db-cache", "user-cache", "session-cache"]
}
在 controller/Application.scala 中使用新的 cache store
import play.api.cache._
import play.api.mvc._
import javax.inject.Inject
class Application @Inject()(
@NamedCache("session-cache") sessionCache: CacheApi
) extends Controller {
}
Caching HTTP responses
可以將 HTTP response 放進 cache
首先利用 cached: Cached 來 cache actions
import play.api.cache.Cached
import javax.inject.Inject
class Application @Inject() (cached: Cached) extends Controller {
}
例如這個方式,就是將 home 放入 cache
def cacheAction = cached("homePage") {
Action {
Ok("Hello World")
}
}
也可以搭配 Authenticated,為每一個 user 暫存不同的 result
def userProfile = Authenticated {
user =>
cached(req => "profile." + user) {
Action {
Ok(views.html.profile(User.find(user)))
}
}
}
可以選擇只要 cache 200 OK 的 response
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
}
}
}
}
Custom Cache API
如果要自己實作新的 Cache API
要先在 application.conf disable EhCacheModule
play.modules.disabled += "play.api.cache.EhCacheModule"
然後用新的 cache API implementation,並 reuse NamedCache 綁定該 implementation。
沒有留言:
張貼留言