1. 程式人生 > >使用redis實現頁面快取

使用redis實現頁面快取

               

本文是<<redis in action>>一書 第二章的讀書筆記

我們要快取的就是形式如下面的url

http://test.com/shwoItem?item=itemX

設計方案

第一個域,是zset型別的------key是viewed:userSessionid存放的是商品id與使用者檢視此商品時的timestamp第二個域,是zset型別的------可以是viewed:member是商品id(全域性範圍內),score是商品被使用者(全域性範圍內)瀏覽的次數(是負數)第三個域,是hash型別的------key是item:itemId存放的是商品的資訊

使用者查看了某件商品

 public void viewItem(Jedis conn, String token, String user, String item) {  long timestamp = System.currentTimeMillis() / 1000;  if (item != null) {          //使用者user在某時瀏覽了某件商品   conn.zadd("viewed:" + token, timestamp, item);   //只記錄用戶最近檢視的25件商品   conn.zremrangeByRank("viewed:" + token, 0, -26);   //有序set裡 score的越小排名越靠前
   conn.zincrby("viewed:", -1, item); //-1還是1?  } }

試著快取這個頁面

首先我們要判斷,某個url是否能快取
 /**  * 如果request 不符合規範 就不快取 返回false  * 如果商品還沒有被訪問過 自然不能快取 返回false  * 如果商品被訪問過 但是訪問的排名在10000之後 也就不快取了 返回false  * 如果商品被訪問過 且訪問的排名在10000之內 就說明可以快取 返回true  *   * @param conn  * @param request  * @return  */ public boolean canCache(Jedis conn, String request)
{  //從類似http://test.com/shwoItem?item=itemX  //這樣的字串中獲得paramter 程式碼就不贅述了  Map<String, String> params = getParams(request);  if (params == null)    return false;    //獲得itemX這個資訊  String itemId = extractItemId(params);  //params.containsKey("_") 這個條件只是示例  //使用者可以加上自己的判別條件  if (itemId == null || params.containsKey("_")) {   return false;  }  // viewed:這個有序集裡存放的是 商品的訪問次數  Long rank = conn.zrank("viewed:", itemId);  // 檢視這個商品的訪問量是否在前10000內  //如果rank==null 說明商品還沒有被訪問過  return rank != null && rank < 10000; }

實現快取request

public String cacheRequest(Jedis conn, String request, Callback callback) {  if (!canCache(conn, request)) {   return callback != null ? callback.call(request) : null;  }  String pageKey = "cache:" + hashRequest(request);  //這個只是示例 真實的情況 應該是複雜的業務邏輯產生最後的反饋頁面  String content = conn.get(pageKey);    if (content == null && callback != null) {   content = callback.call(request);   //我們可以認為這個content就是html   //我們將要顯示的資料快取5分鐘   conn.setex(pageKey, 300, content);  }  return content; }
傳入的callback如下:
  Callback callback = new Callback() {   public String call(String request) {    return "content for " + request;   }  };

我們看看測試程式碼

package redisinaction;import org.junit.BeforeClass;import org.junit.Test;import redis.clients.jedis.Jedis;import redisinaction.Chapter02.Callback;/**    * This class is used for ...    * @author  dlf([email protected]) * @version 1.0, 2016年10月18日 下午9:34:27    */public class Chapter02Test {   static Jedis conn = nullstatic Chapter02 ch2=null;  @BeforeClass         public static void initConn(){  System.out.println("test before");  ch2=new Chapter02();    conn = new Jedis("10.150.0.80");  conn.auth("dlf123123"); }    @Test public void testCacheRequest() {    Callback callback = new Callback() {   public String call(String request) {    return "content for " + request;   }  };  //必須先viewItem 否則不管呼叫幾次cacheRequest 都不能快取  //為什麼? 自己想  ch2.viewItem(conn, "dlf_session_id", "dlf", "itemX");  String url = "http://test.com/?item=itemX";  String result = ch2.cacheRequest(conn, url, callback);  System.out.println( result);    String result3 = ch2.cacheRequest(conn, url, null);  System.out.println(result3);    String result4 = ch2.cacheRequest(conn, url, null);  System.out.println(result4); }}
全域性範圍內被使用者檢視最多的n件產品(和上一章那個文章排序差不多)
    private static final int ARTICLES_PER_PAGE = 25;    public List<Map<String,String>>  getMostPopulate(Jedis conn, int page) {     int start = (page - 1) * ARTICLES_PER_PAGE;        int end = start + ARTICLES_PER_PAGE - 1;        Set<String> ids = conn.zrevrange("viewed:", start, end);                        List<Map<String,String>> items = new ArrayList<Map<String,String>>();        for (String id : ids){            Map<String,String> itemData = conn.hgetAll(id);            itemData.put("id", id);            items.add(itemData);        }        return items;    }
在viewItem裡,我們已經記錄了所有商品的瀏覽次數(負數)conn.zincrby("viewed:", -1, item);那麼經過n天后,這個viewed裡的資料項就很多了,而且其實也沒有必要一直儲存著所有商品的瀏覽次數所以
public class RescaleViewedThread implements  Runnable{  private Jedis conn;  private boolean quit;  public RescaleViewedThread(int limit) {      conn = new Jedis("10.150.0.80");         conn.auth("dlf123123");  }  public void shutDown(){   quit=true;  }    @Override  public void run() {   while (!quit) {    try {     //移除排名20000之後的商品瀏覽資訊     conn.zremrangeByRank("viewed:", 0, -20001);     //將所有商品的瀏覽數量降低一半     conn.zinterstore("viewed","viewed:0.5");     Thread.sleep(300);    } catch (InterruptedException e) {     // TODO Auto-generated catch block     e.printStackTrace();    }       }     }   }
系統啟動後就,一直執行這個執行緒。那麼怎麼做呢?怎麼整合到我的專案裡呢?我的專案是使用struts2開發的。

哥哥,您不知道有個東西叫攔截器麼?interceptor?

這個是springmvc的?struts怎麼用?

笨死!

在struts2的攔截器裡獲得request和response你不會麼?