集羣環境下的Session共享

1、Cookie機制和Session機制回顧
1)定義:Session成爲「會話」,具體是指一個終端用戶與交互系統進行通訊的時間間隔,一般指從註冊進入系統到註銷退出系統之間所通過的時間。Session其實是一個特定的時間概念。
 
2)HTTP協議與狀態保持:HTTP 協議自己是無狀態的,這與HTTP協議原本的目的是相符的,客戶端只須要簡單的向服務器請求下載某些文件,不管是客戶端仍是服務器都沒有必要紀錄彼此過去的行爲,每一次請求之間都是獨立的,比如一個顧客和一個自動售貨機或者一個普通的(非會員制)大賣場之間的關係同樣。
 
cookie的做用就是爲了解決HTTP協議無狀態的缺陷。至於後來出現的session機制則是又一種在客戶端與服務器之間保持狀態的解決方案。
 
3)Cookie和Session機制的區別和聯繫(幾個有趣的例子):
一、該店的店員很厲害,能記住每位顧客的消費數量,只要顧客一走進咖啡店,店員就知道該怎麼對待了。這種作法就是協議自己支持狀態。
 
二、發給顧客一張卡片,上面記錄着消費的數量,通常還有個有效期限。每次消費時,若是顧客出示這張卡片,則這次消費就會與之前或之後的消費相聯繫起來。這種作法就是在客戶端保持狀態。(Cookie原理)
 
三、發給顧客一張會員卡,除了卡號以外什麼信息也不紀錄,每次消費時,若是顧客出示該卡片,則店員在店裏的紀錄本上找到這個卡號對應的紀錄添加一些消費信息。這種作法就是在服務器端保持狀態。(Session原理)
 
因爲HTTP協議是無狀態的,cookie機制採用的是在客戶端保持狀態的方案,而session機制採用的是在服務器端保持狀態的方案。同時咱們也看到,因爲採用服務器端保持狀態的方案在客戶端也須要保存一個標識,因此session機制可能須要藉助於cookie機制來達到保存標識的目的,但實際上它還有其餘選擇。
 
2、集羣下實現Session共享的幾種方案
 
1.請求精肯定位:基於IP地址的Hash策略,將同一用戶的請求都集中在一臺服務器上,這臺服務器上保存了該用戶的Session信息。缺點:單點部署發生宕機時,Session丟失。
 
2.Session複製共享:好比能夠用Tomcat自帶的插件進行Session同步,使得多臺應用服務器之間自動同步Session,保持一致。若是一臺發生故障,負載均衡會遍歷尋找可用節點,Session也不會丟失。缺點:必須是Tomcat和Tomcat之間,Session的複製也會消耗系統 的性能,使得同步給成員時容易形成內網流量瓶頸。
 
3.基於cache DB緩存的Session共享(推薦,Spring-Session也是一樣的原理,同自定義的JRedis一塊兒配置能夠實現目的):使用Redis存取Session信息,應用服務器發生故障時,當Session不在內存中時就會去CacheDB中查找(要求Redis支持持久化),找到則複製到本機,實現Session共享和高可用。
 
分佈式Session配置原理圖
 

 

 
其餘方式:利用公共的NFS服務器作共享服務器、徹底利用Cookie(將Session數據全放在Cookie中)等。
 
3、基於Redis的Session共享實現(核心代碼)
 
1)原理:寫一個Session過濾器攔截每一次請求,在這裏檢查由Cookie生成的SessionID,進行建立或獲取。核心是實現使用裝飾類,實現Session在Redis中的存取操做。
2)此處存取方式爲 sessionID+sessionKey做爲Redis的key  ==== sessionValue做爲Redis的value,這樣保存了每次存取都從Redis中操做,效率更高。
3)注意:序列化方式推薦使用Apache下Commons組件——SerializationUtils 或 org.springframework.util.SerializationUtils
 
 1 //定義請求通過的Session過濾器
 2 public class SessionFilter extends OncePerRequestFilter implements Filter {
 3     @Override
 4     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
 5             throws ServletException, IOException {
 6         // 從cookie中獲取sessionId,若是這次請求沒有sessionId,重寫爲此次請求設置一個sessionId
 7         String sid = CookieUtil.getCookieValue(request, GlobalConstant.JSESSIONID);
 8         if (StringUtils.isEmpty(sid) || sid.length() != 36) {
 9             sid = UUID.randomUUID().toString();
10             CookieUtil.setCookie(request, response, GlobalConstant.JSESSIONID, sid, 60 * 60);
11         }
12         // 交給自定義的HttpServletRequestWrapper處理
13         filterChain.doFilter(new HttpServletRequestWrapper(sid, request, response), response);
14     }
15 }
 1     //Cookie
 2     public static void setCookie(HttpServletRequest request,
 3             HttpServletResponse response, String name, String value, int seconds) {
 4         if (StringUtils.isEmpty(name) || StringUtils.isEmpty(value))
 5             return;
 6         Cookie cookie = new Cookie(name, value);
 7         //cookie.setDomain(domain);
 8         cookie.setMaxAge(seconds); 
 9         cookie.setPath("/");
10         response.setHeader("P3P",
11                 "CP='IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT'");
12         response.addCookie(cookie);
13     }
14 
15     public String getCookieValue(String name)
16             throws UnsupportedEncodingException {
17         Cookie cookies[] = request.getCookies();
18         if (cookies != null) {
19             for (int i = 0; i < cookies.length; i++) {
20                 if (name.equalsIgnoreCase(cookies[i].getName())) {
21                     return cookies[i].getValue();
22                 }
23             }
24         }
25         return "";
26     }
 1     //SessionService實現   sidKey == sessionID+SessionKey
 2     public Object getSession(String sidKey) {
 3         Object realValue = null;
 4         try {
 5             String key = 「SESSION_DISTRIBUTED_SESSIONID」 + sidKey;
 6             realValue = SerializeUtil.unserialize(RedisUtils.getInstance().get(key.getBytes()));
 7         } catch (Exception e) {
 8             LOG.error("Redis獲取session異常" + e.getMessage(), e.getCause());
 9         }
10         return realValue;
11     }
12 
13     public void saveSession(String sidKey, Object value) {
14         try {
15             String key = 「SESSION_DISTRIBUTED_SESSIONID」 + sidKey;
16             boolean isSetSuccess = RedisUtils.getInstance().set(key.getBytes(), SerializeUtil.serialize(value));
17             if (!isSetSuccess) {
18                 LOG.error("Redis保存session異常");
19             }
20         } catch (Exception e) {
21             LOG.error("Redis保存session異常" + e.getMessage(), e.getCause());
22         }
23     }
24 
25     public void removeSession(String sidKey) {
26         try {
27             String key =「SESSION_DISTRIBUTED_SESSIONID」+ sidKey;
28             RedisUtils.getInstance().del(key.getBytes());
29         } catch (Exception e) {
30             LOG.error("Redis刪除session的attribute異常" + e.getMessage(), e.getCause());
31         }
32     }
33 
34     public void removeAllSession(String sid) {
35         try {
36             String keyPattern =「SESSION_DISTRIBUTED_SESSIONID」 + sid + "*";
37             Set<byte[]> keys = RedisUtils.getInstance().keys(keyPattern.getBytes());
38             for (byte[] key : keys) {
39                 RedisUtils.getInstance().del(key);
40             }
41         } catch (Exception e) {
42             LOG.error("Redis刪除session異常" + e.getMessage(), e.getCause());
43         }
44     }
45 
46     public Set<String> getAllKeys(String sid) {
47         try {
48             Set<String> keysResult = new HashSet<String>();
49             String keyPattern =「SESSION_DISTRIBUTED_SESSIONID」 + sid + "*";
50             Set<byte[]> keys = RedisUtils.getInstance().keys(keyPattern.getBytes());
51 
52             for (byte[] key : keys) {
53                 keysResult.add(new String(key));
54             }
55             return keysResult;
56         } catch (Exception e) {
57             LOG.error("Redis刪除session異常" + e.getMessage(), e.getCause());
58             return null;
59         }
60     }
 1 HttpServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper
 2     private HttpSession session;
 3 
 4     private HttpServletRequest request;
 5 
 6     private HttpServletResponse response;
 7 
 8     private String sid = "";
 9 
10     public HttpServletRequestWrapper(HttpServletRequest request) {
11         super(request);
12     }
13 
14     public HttpServletRequestWrapper(String sid, HttpServletRequest request) {
15         super(request);
16         this.sid = sid;
17     }
18 
19     public HttpServletRequestWrapper(String sid, HttpServletRequest request, HttpServletResponse response) {
20         super(request);
21         this.request = request;
22         this.response = response;
23         this.sid = sid;
24         if (this.session == null) {
25             this.session = new HttpSessionWrapper(sid, super.getSession(false), request, response);
26         }
27     }
28 
29     @Override
30     public HttpSession getSession(boolean create) {
31         if (this.session == null) {
32             if (create) {
33                 this.session = new HttpSessionWrapper(this.sid, super.getSession(create), this.request, this.response);
34                 return this.session;
35             } else {
36                 return null;
37             }
38         }
39         return this.session;
40     }
41 
42     @Override
43     public HttpSession getSession() {
44         if (this.session == null) {
45             this.session = new HttpSessionWrapper(this.sid, super.getSession(), this.request, this.response);
46         }
47         return this.session;
48     }
 1     HttpSessionWrapper implements HttpSession{
 2 
 3     private String sid = "";
 4 
 5     private HttpSession session;
 6 
 7     private HttpServletRequest request;
 8 
 9     private HttpServletResponse response;
10 
11     private SessionService sessionService = (SessionService) SpringContextHolder.getBean("sessionService");
12 
13     public HttpSessionWrapper() {
14     }
15 
16     public HttpSessionWrapper(HttpSession session) {
17         this.session = session;
18     }
19 
20     public HttpSessionWrapper(String sid, HttpSession session) {
21         this(session);
22         this.sid = sid;
23     }
24 
25     public HttpSessionWrapper(String sid, HttpSession session,
26             HttpServletRequest request, HttpServletResponse response) {
27         this(sid, session);
28         this.request = request;
29         this.response = response;
30     }
31 
32 
33     @Override
34     public Object getAttribute(String name) {
35         return sessionService.getSession(this.sid+"#"+name);
36     }
37 
38     @Override
39     public void setAttribute(String name, Object value) {
40         sessionService.saveSession(this.sid+"#"+name, value);
41     }
42 
43     @Override
44     public void invalidate() {
45         sessionService.removeAllSession(this.sid);
46         CookieUtil.removeCookieValue(this.request,this.response, GlobalConstant.JSESSIONID);
47     }
48 
49     @Override
50     public void removeAttribute(String name) {
51         sessionService.removeSession(this.sid+"#"+name); 
52     }
53 
54     @Override
55     public Object getValue(String name) {
56         return this.session.getValue(name);
57     }
58 
59     @SuppressWarnings("unchecked")
60     @Override
61     public Enumeration getAttributeNames() {
62         return (new Enumerator(sessionService.getAllKeys(this.sid), true));
63     }
64 
65     @Override
66     public String getId() {
67         return this.sid;
68     }69    }
相關文章
相關標籤/搜索