Play framework 1.2.3 使用緩存、Memcached集成

  • play框架包含一個緩存lib,這個lib是用來和Memcached集成做分佈式緩存用的。 

如果不配置Memcached,play框架將會使用單獨的緩存(EhCache),其數據存儲在JVM的堆中。把數據存儲在JVM的堆中違反了play框架“不共享任何東西”的原則,這也導致了你不能把應用程序同時部署到多個機器,即不能在多個機器上負載均衡以保證應用的可用性、高性能(即使在多個機器部署了相同的應用,但是由於不同機器的JVM是相互獨立的,會導致不同機器的緩存數據不同,即不同機器對同一請求的響應可能不同!不一致性!) 

理解緩存的應用環境是很重要的:當你把數據放到緩存中,你不能期望數據在緩存中一直存在。實際上,你不應該這樣想。緩存中存取數據很快,但是緩存是有有效期的、並且緩存數據是存放在內存中(並沒有做持久化備份)。 

因此最佳使用緩存的方式是當發現緩存的數據不存在時,直接從數據源重新抓取。 
Java代碼  
  1. public static void allProducts() {  
  2.     List<Product> products = Cache.get("products", List.class);  
  3.     if(products == null) {  
  4.         products = Product.findAll();  
  5.         Cache.set("products", products, "30mn");  
  6.     }  
  7.     render(products);  
  8. }  

緩存API: 
play框架中的緩存類play.cache.Cache,其包含了從緩存中取值,設值,替換值的方法。 
例子: 
Java代碼  
  1. public static void showProduct(String id) {  
  2.     Product product = Cache.get("product_" + id, Product.class);  
  3.     if(product == null) {  
  4.         product = Product.findById(id);  
  5.         Cache.set("product_" + id, product, "30mn");  
  6.     }  
  7.     render(product);  
  8. }  
  9.    
  10. public static void addProduct(String name, int price) {  
  11.     Product product = new Product(name, price);  
  12.     product.save();  
  13.     showProduct(product.id);  
  14. }  
  15.    
  16. public static void editProduct(String id, String name, int price) {  
  17.     Product product = Product.findById(id);  
  18.     product.name = name;  
  19.     product.price = price;  
  20.     Cache.set("product_" + id, product, "30mn");  
  21.     showProduct(id);  
  22. }  
  23.    
  24. public static void deleteProduct(String id) {  
  25.     Product product = Product.findById(id);  
  26.     product.delete();  
  27.     Cache.delete("product_" + id);  
  28.     allProducts();  
  29. }  

上面的delete, set等標準方法都是非阻塞的。也就是說,當你調用 
Java代碼  
  1. Cache.delete("product_" + id);  

時,delete方法不等緩存對象真的被刪除就直接返回。所以,如果在刪除緩存對象時發生錯誤(譬如IO錯誤),那麼緩存對象是仍然存在的。 
sateDelete方法:阻塞的,在緩存對象真的被刪除後,方法纔會返回,方法有bool型的返回值來表明操作是否成功。 

注意: 
  • 由於是阻塞方法,使用safe前綴的方法會降低應用的響應速度。因此,只在需要時才那麼使用。
  • 當指定參數expiration == "0s"(0秒)時,緩存實際的失效時間可能由於不同的緩存實現相差很大。

不要把Session當緩存使用! 
當你用過基於內存Session實現的框架的話、如Servlet中的HTTP  Session(play框架中的session是以Cookie形式存儲在客戶端的),你可能會對play框架中只允許在session中以String的形式存儲少量數據感到不習慣。在play框架中,session並不是你存儲應用數據的地方。 

基於內存的Session,使用例子: 
Java代碼  
  1. httpServletRequest.getSession().put("userProducts", products);  
  2. ...  
  3. // and then in subsequent requests  
  4. products = (List<Product>)httpServletRequest.getSession().get("use  
  5. rProducts");  

play框架等效實現: 
Java代碼  
  1. Cache.set(session.getId(), products);  
  2. ...  
  3. // and then in subsequent requests  
  4. 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
valmaybeUser:Option[User] =Cache.getAs[User]("item.key")

當緩存數據不存在時,還有一個幫助方法在你獲取或設置緩存數據給你帶來了便利:

1
2
3
valuser:User =Cache.getOrElseAs[User]("item.key") {
  User.findById(connectedUser)
}

緩存 HTTP 響應數據

你可以用標準的 Action 組合簡單的創建一個聰明的具體緩存特性的 Action.

注: Play HTTP Result 實例可安全的被緩存並被重用.

Play 提供了默認, 內建的方式來應對標準的情況:

1
2
3
4
5
defindex =Cached("homePage") {
  Action {
    Ok("Hello world")
  }
}

或者甚至是這樣:

1
2
3
4
5
6
7
defuserProfile =Authenticated { user =>
  Cached(req ="profile."+ user) {     
    Action {
      Ok(views.html.profile(User.find(user)))
    }  
  }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章