由于集群下session同步性能较差,可以选择使用cookie保存session。session处理遵照以下几点
1.选择map做session的存储,在更新时写入cookie,可以保证每次请求时能正常获取上次记录的session。
2.添加子类重写HttpSession的方法,将其修改的方法实现为对map的修改,在每次修改后更新response的cookie。
自定义HttpSession
public class CsmSession implements HttpSession { private ConcurrentHashMap<String, Object> session; private String uuid; private Kryo kryo = new Kryo(); private HttpServletResponse response; private HttpServletRequest request; @SuppressWarnings("unchecked") public CsmSession(HttpServletRequest request, HttpServletResponse response, String data, String uuid) { this.request = request; this.response = response; this.uuid = uuid; DeflateSerializer deflateSerializer = new DeflateSerializer( kryo.getDefaultSerializer(ConcurrentHashMap.class)); BlowfishSerializer blowfishSerializer = new BlowfishSerializer( deflateSerializer, Constants.BLOWFISH_KEY); kryo.register(ConcurrentHashMap.class, blowfishSerializer); if (data != null) { try { Input input = new Input(Base64.decodeBase64(data)); this.session = this.kryo.readObject(input, ConcurrentHashMap.class); input.close(); } catch (Exception e) { } } if (this.session == null) { this.session = new ConcurrentHashMap<String, Object>(); this.change(); } } @Deprecated @Override public long getCreationTime() { return 0; } @Override public String getId() { return this.uuid; } @Deprecated @Override public long getLastAccessedTime() { return 0; } @Override public ServletContext getServletContext() { return this.request.getServletContext(); } @Deprecated @Override public void setMaxInactiveInterval(int interval) { } @Deprecated @Override public int getMaxInactiveInterval() { return 0; } @SuppressWarnings("deprecation") @Override public HttpSessionContext getSessionContext() { return null; } @Override public Object getAttribute(String name) { return session.get(name); } @Override public Object getValue(String name) { return session.get(name); } @Override public Enumeration<String> getAttributeNames() { return session.keys(); } @Override public String[] getValueNames() { return session.keySet().toArray(new String[0]); } @Override public void setAttribute(String name, Object value) { this.put(name, value); this.change(); } @Override public void putValue(String name, Object value) { this.put(name, value); this.change(); } @Override public void removeAttribute(String name) { session.remove(name); this.change(); } @Override public void removeValue(String name) { session.remove(name); this.change(); } @Override public void invalidate() { session.clear(); this.change(); } @Deprecated @Override public boolean isNew() { return false; } public String toCookieString() { Output output = new Output(1024, Constants.SESSION_MAX); this.kryo.writeObject(output, this.session); byte[] bytes = output.toBytes(); output.close(); return Base64.encodeBase64URLSafeString(bytes); } public void addToCookie(HttpServletResponse response, String path) { String cookieStr = this.toCookieString(); CookieUtil.setCookie(response, Constants.SESSION_DATA, cookieStr, path); } private void change() { addToCookie(response, "/"); } private void put(String name, Object value) { if (name == null) return; if (value == null) this.session.remove(name); else this.session.put(name, value); } }
更新cookie
public static void setCookie(HttpServletResponse response, final Cookie cookie) { if (response.isCommitted()) { return; } final String headername = "Set-Cookie"; String header = generateCookieString(cookie); final String startsWith = cookie.getName() + "="; Collection<String> headers = response.getHeaders(headername); String replaceItem = null; for (String item : headers) { if(item.startsWith(startsWith)){ replaceItem = item; } } if(replaceItem != null){ headers.remove(replaceItem); response.setHeader(headername, header); for (String item : headers) { response.addHeader(headername, item); } }else{ response.addHeader(headername, header); } }
3.使用filter将request包装后再继续执行,在包装的request中重写获取session、sessionid等方法,转为自己实现的session
filter处理:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String uri = ((HttpServletRequest) request).getRequestURI(); boolean isStatic = uri.matches(".*\\.(ico|png|gif|jpg|css|js)$"); //静态文件不需要session处理 if (!isStatic) { logger.info("start session filter uri="+uri); CsmServletRequest req = new CsmServletRequest( (HttpServletRequest) request, (HttpServletResponse) response); chain.doFilter(req, response); logger.info("session filter finished"); }else{ chain.doFilter(request, response); } }
request重写
public class CsmServletRequest extends HttpServletRequestWrapper { private HttpSession session; private HttpServletResponse response; public CsmServletRequest(HttpServletRequest request, HttpServletResponse response) { super(request); this.response = response; } @Override public HttpSession getSession() { return this.getSession(true); } @Override public HttpSession getSession(boolean create) { this.initSession(create); return this.session; } @Override public String getRequestedSessionId() { return this.getSession().getId(); } private void initSession(boolean create) { if (this.session == null && create) { this.session = SessionBuilder.get(this, this.response).cookie() .build(); } } }
这样,每次对session的修改就能转为cookie的写入,集群中不再需要额外对session进行共享、复制处理。
时间: 2024-11-11 13:48:41