在多個tomcat集群中,session共享就是必須违帆,不然前端nginx轉(zhuǎn)發(fā)過來不知道之前請求在哪臺浙巫,就找不到session,導(dǎo)致請求失敗刷后。
幾種實現(xiàn)方案
1.粘性session
使用Nginx的ip_hash特性做粘性會話的畴,可以讓用戶每次訪問都轉(zhuǎn)發(fā)到該用戶第一次訪問的web容器上,但是當該web容器上的應(yīng)用掛了惠险,nginx不會將該用戶的請求轉(zhuǎn)發(fā)到別的web容器苗傅,體驗不好抒线。
2.服務(wù)器session復(fù)制
即每次session發(fā)生變化時班巩,創(chuàng)建或者修改,就廣播給所有集群中的服務(wù)器嘶炭,使所有的服務(wù)器上的session相同抱慌。
3.session共享
緩存session,使用redis眨猎, memcached抑进。
4.session持久化
將session存儲至數(shù)據(jù)庫中,如mysql睡陪。
基于redis的實現(xiàn)方式
自定義Request與Session,并使用過濾器過濾對應(yīng)路徑的請求寺渗,引入自定義Request
- 封裝自定義Request,主要目的重寫getSession方法返回自定義的session對象兰迫。
package com.liabc.poc.server.config;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
public class HttpServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper {
private String sid;
public HttpServletRequestWrapper(HttpServletRequest request, String sid) {
super(request);
this.sid = sid;
}
@Override
public HttpSession getSession(boolean create) {
return new HttpSessionSidWrapper(null, sid);
}
@Override
public HttpSession getSession() {
return new HttpSessionSidWrapper(null, sid);
}
}
- 封裝自定義Session
package com.liabc.poc.server.config;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionContext;
import java.util.Enumeration;
public class HttpSessionWrapper implements HttpSession {
private HttpSession session;
public HttpSessionWrapper(HttpSession session) {
this.session = session;
}
@Override
public long getCreationTime() {
return this.session.getCreationTime();
}
@Override
public String getId() {
return this.session.getId();
}
@Override
public long getLastAccessedTime() {
return this.session.getLastAccessedTime();
}
@Override
public ServletContext getServletContext() {
return this.session.getServletContext();
}
@Override
public void setMaxInactiveInterval(int interval) {
this.session.setMaxInactiveInterval(interval);
}
@Override
public int getMaxInactiveInterval() {
return this.session.getMaxInactiveInterval();
}
@Override
public HttpSessionContext getSessionContext() {
return this.session.getSessionContext();
}
@Override
public Object getAttribute(String name) {
return this.session.getAttribute(name);
}
@Override
public Object getValue(String name) {
return this.session.getValue(name);
}
@Override
public Enumeration<String> getAttributeNames() {
return this.session.getAttributeNames();
}
@Override
public String[] getValueNames() {
return this.session.getValueNames();
}
@Override
public void setAttribute(String name, Object value) {
this.session.setAttribute(name, value);
}
@Override
public void putValue(String name, Object value) {
this.session.putValue(name, value);
}
@Override
public void removeAttribute(String name) {
this.session.removeAttribute(name);
}
@Override
public void removeValue(String name) {
this.session.removeValue(name);
}
@Override
public void invalidate() {
this.session.invalidate();
}
@Override
public boolean isNew() {
return this.session.isNew();
}
}
- 繼承并重寫getAttribute信殊,setAttribute等方法。(主要實現(xiàn)從redis讀取數(shù)據(jù)封裝session信息汁果,暫未實現(xiàn)涡拘。)
package com.liabc.poc.server.config;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
public class HttpSessionSidWrapper extends HttpSessionWrapper {
private String sid;
private Map map = new HashMap();
public HttpSessionSidWrapper(HttpSession session, String sid) {
super(session);
this.sid = sid;
}
@Override
public Object getAttribute(String name) {
map.put("test","Hello Session!");
return this.map.get(name);
}
@Override
public void setAttribute(String name, Object value) {
this.map.put(name, value);
}
@Override
public String getId() {
return this.sid;
}
}
- 在過濾器里引入自定義Request和Session
package com.liabc.poc.server.config;
import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SessionFilter implements Filter {
private static final long serialVersionUID = 8928219999641126613L;
private String cookieDomain = "";
private String cookiePath = "/";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.cookiePath = filterConfig.getInitParameter("cookiePath");
if (this.cookiePath == null || this.cookiePath.length() == 0) {
this.cookiePath = "/";
}
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String sessionId = "XmlBeanDefinitionReaderSid";
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
Cookie cookies[] = request.getCookies();
Cookie sCookie = null;
String sid = "";
if (cookies != null && cookies.length > 0) {
for (int i = 0; i < cookies.length; i++) {
sCookie = cookies[i];
if (sCookie.getName().equals(sessionId)) {
sid = sCookie.getValue();
}
}
}
if (sid == null || sid.length() == 0) {
sid = java.util.UUID.randomUUID().toString();
Cookie mycookies = new Cookie(sessionId, sid);
mycookies.setMaxAge(-1);
mycookies.setPath("/");
mycookies.setHttpOnly(true);
response.addCookie(mycookies);
}
HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(request, sid);
filterChain.doFilter(requestWrapper, response);
}
}
- web.xml 配置過濾器
<!-- session共享過濾器 -->
<filter>
<filter-name>mySessionFilter</filter-name>
<filter-class>com.liabc.poc.server.config.SessionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>mySessionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>