使用redis實現頁面快取
阿新 • • 發佈:2018-12-21
本文是<<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 = null; static 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你不會麼?