集群環境下的Session共享


一、Cookie機制和Session機制回顧
1)定義:Session成為“會話”,具體是指一個終端用戶與交互系統進行通信的時間間隔,通常指從注冊進入系統到注銷退出系統之間所經過的時間。Session實際上是一個特定的時間概念。
 
2)HTTP協議與狀態保持:HTTP 協議本身是無狀態的,這與HTTP協議本來的目的是相符的,客戶端只需要簡單的向服務器請求下載某些文件,無論是客戶端還是服務器都沒有必要紀錄彼此過去的行為,每一次請求之間都是獨立的,好比一個顧客和一個自動售貨機或者一個普通的(非會員制)大賣場之間的關系一樣。
 
cookie的作用就是為了解決HTTP協議無狀態的缺陷。至於后來出現的session機制則是又一種在客戶端與服務器之間保持狀態的解決方案。
 
3)Cookie和Session機制的區別和聯系(幾個有趣的例子):
1、該店的店員很厲害,能記住每位顧客的消費數量,只要顧客一走進咖啡店,店員就知道該怎么對待了。這種做法就是協議本身支持狀態。
 
2、發給顧客一張卡片,上面記錄着消費的數量,一般還有個有效期限。每次消費時,如果顧客出示這張卡片,則此次消費就會與以前或以后的消費相聯系起來。這種做法就是在客戶端保持狀態。(Cookie原理)
 
3、發給顧客一張會員卡,除了卡號之外什么信息也不紀錄,每次消費時,如果顧客出示該卡片,則店員在店里的紀錄本上找到這個卡號對應的紀錄添加一些消費信息。這種做法就是在服務器端保持狀態。(Session原理)
 
由於HTTP協議是無狀態的,cookie機制采用的是在客戶端保持狀態的方案,而session機制采用的是在服務器端保持狀態的方案。同時我們也看到,由於采用服務器端保持狀態的方案在客戶端也需要保存一個標識,所以session機制可能需要借助於cookie機制來達到保存標識的目的,但實際上它還有其他選擇。
 
二、集群下實現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中)等。
 
三、基於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 }

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM