- play框架包含一個緩存lib,這個lib是用來和Memcached集成做分佈式緩存用的。
如果不配置Memcached,play框架將會使用單獨的緩存(EhCache),其數據存儲在JVM的堆中。把數據存儲在JVM的堆中違反了play框架“不共享任何東西”的原則,這也導致了你不能把應用程序同時部署到多個機器,即不能在多個機器上負載均衡以保證應用的可用性、高性能(即使在多個機器部署了相同的應用,但是由於不同機器的JVM是相互獨立的,會導致不同機器的緩存數據不同,即不同機器對同一請求的響應可能不同!不一致性!)
理解緩存的應用環境是很重要的:當你把數據放到緩存中,你不能期望數據在緩存中一直存在。實際上,你不應該這樣想。緩存中存取數據很快,但是緩存是有有效期的、並且緩存數據是存放在內存中(並沒有做持久化備份)。
因此最佳使用緩存的方式是當發現緩存的數據不存在時,直接從數據源重新抓取。
- public static void allProducts() {
- List<Product> products = Cache.get("products", List.class);
- if(products == null) {
- products = Product.findAll();
- Cache.set("products", products, "30mn");
- }
- render(products);
- }
緩存API:
play框架中的緩存類play.cache.Cache,其包含了從緩存中取值,設值,替換值的方法。
例子:
- public static void showProduct(String id) {
- Product product = Cache.get("product_" + id, Product.class);
- if(product == null) {
- product = Product.findById(id);
- Cache.set("product_" + id, product, "30mn");
- }
- render(product);
- }
- public static void addProduct(String name, int price) {
- Product product = new Product(name, price);
- product.save();
- showProduct(product.id);
- }
- public static void editProduct(String id, String name, int price) {
- Product product = Product.findById(id);
- product.name = name;
- product.price = price;
- Cache.set("product_" + id, product, "30mn");
- showProduct(id);
- }
- public static void deleteProduct(String id) {
- Product product = Product.findById(id);
- product.delete();
- Cache.delete("product_" + id);
- allProducts();
- }
上面的delete, set等標準方法都是非阻塞的。也就是說,當你調用
時,delete方法不等緩存對象真的被刪除就直接返回。所以,如果在刪除緩存對象時發生錯誤(譬如IO錯誤),那麼緩存對象是仍然存在的。
sateDelete方法:阻塞的,在緩存對象真的被刪除後,方法纔會返回,方法有bool型的返回值來表明操作是否成功。
注意:
- 由於是阻塞方法,使用safe前綴的方法會降低應用的響應速度。因此,只在需要時才那麼使用。
- 當指定參數expiration == "0s"(0秒)時,緩存實際的失效時間可能由於不同的緩存實現相差很大。
不要把Session當緩存使用!
當你用過基於內存Session實現的框架的話、如Servlet中的HTTP Session(play框架中的session是以Cookie形式存儲在客戶端的),你可能會對play框架中只允許在session中以String的形式存儲少量數據感到不習慣。在play框架中,session並不是你存儲應用數據的地方。
基於內存的Session,使用例子:
- httpServletRequest.getSession().put("userProducts", products);
- ...
- // and then in subsequent requests
- products = (List<Product>)httpServletRequest.getSession().get("use
- rProducts");
play框架等效實現:
- Cache.set(session.getId(), products);
- ...
- // and then in subsequent requests
- List<Product> products = Cache.get(session.getId(), List.class)
這裏我們使用唯一的UUID來作爲每個用戶緩存的key。注意:和session不同的是,緩存並不會綁定到任何一個用戶中,緩存在整個應用中是共享的。
集成Memcached緩存配置(application.conf文件):
memcached:時候啓用Memcached緩存,如果不配置,那麼play默認使用單獨的緩存EhCache,其數據存儲在JVM的堆中。可能值:disabled(默認),enabled
memcached.host:啓用Memcached時,指定memcached host地址,默認值:127.0.0.1:11211。
memcached.host=127.0.0.1:11211
多個host做分佈式緩存時,如下配置:
memcached.1.host=127.0.0.1:11211
memcached.2.host=127.0.0.1:11212
Unmi 注: EhCachePlugin 的註冊方式可以查看 play_x.x.x.jar(比如 $PLAY_HOME/repository/local/play/play_2.9.1/2.0.4/jars/play_2.9.1.jar) 包中的 play.plugins 中的內容:
1
2
3
4
5
6
7
8
|
100:play.api.i18n.MessagesPlugin 200:play.api.db.BoneCPPlugin 300:play.db.ebean.EbeanPlugin 400:play.db.jpa.JPAPlugin 500:play.api.db.evolutions.EvolutionsPlugin 600:play.api.cache.EhCachePlugin 1000:play.api.libs.concurrent.AkkaPlugin 10000:play.api.GlobalPlugin |
這是 Play2.0.4 默認註冊的插件,其中就包含了 600:play.api.cache.EhCachePlugin 插件。
使用下面簡單的 API,你就可以往緩存中存數據了:
1
|
Cache.set( "item.key" ,
connectedUser) |
之後獲取它:
1
|
val maybeUser : Option[User] = Cache.getAs[User]( "item.key" ) |
當緩存數據不存在時,還有一個幫助方法在你獲取或設置緩存數據給你帶來了便利:
1
2
3
|
val user : User = Cache.getOrElseAs[User]( "item.key" )
{ User.findById(connectedUser) } |
緩存 HTTP 響應數據
你可以用標準的 Action 組合簡單的創建一個聰明的具體緩存特性的 Action.
注: Play HTTP
Result
實例可安全的被緩存並被重用.
Play 提供了默認, 內建的方式來應對標準的情況:
1
2
3
4
5
|
def index = Cached( "homePage" )
{ Action
{ Ok( "Hello
world" ) } } |
或者甚至是這樣:
1
2
3
4
5
6
7
|
def userProfile = Authenticated
{ user = > Cached(req = > "profile." +
user) { Action
{ Ok(views.html.profile(User.find(user))) } } } |