在多个tomcat集群中,session共享就是必须,不然前端nginx转发过来不知道之前请求在哪台,就找不到session,导致请求失败。
几种实现方案
1.粘性session
使用Nginx的ip_hash特性做粘性会话,可以让用户每次访问都转发到该用户第一次访问的web容器上,但是当该web容器上的应用挂了,nginx不会将该用户的请求转发到别的web容器,体验不好。
2.服务器session复制
即每次session发生变化时,创建或者修改,就广播给所有集群中的服务器,使所有的服务器上的session相同。
3.session共享
缓存session,使用redis, memcached。
4.session持久化
将session存储至数据库中,如mysql。
基于redis的实现方式
自定义Request与Session,并使用过滤器过滤对应路径的请求,引入自定义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 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等方法。(主要实现从redis读取数据封装session信息,暂未实现。)
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 配置过滤器
mySessionFilter
com.liabc.poc.server.config.SessionFilter
mySessionFilter
/*