如何使用Spring+redis實(shí)現(xiàn)對(duì)session的分布式管理
在Spring中實(shí)現(xiàn)分布式 session管理
本文主要是在Spring中實(shí)現(xiàn)分布式session,采用redis對(duì)session進(jìn)行持久化管理,這樣當(dāng)應(yīng)用部署的時(shí)候,不需要在Resin、Tomcat等容器里面進(jìn)行分布式配置,方便加入新的節(jié)點(diǎn)服務(wù)器進(jìn)行集群擴(kuò)容,session不依賴各節(jié)點(diǎn)的服務(wù)器,可直接從redis獲取。下面是功能的核心代碼:
一、首先在web.xml里面配置
加入攔截器:
<!-- 分布式session start -->
<filter>
<filter-name>distributedSessionFilter</filter-name>
<filter-class>DistributedSessionFilter</filter-class>
<init-param>
<!-- 必填,密鑰.2種方式,1對(duì)應(yīng)為bean,格式為bean:key。2字符串,格式如:afffrfgv-->
<param-name>key</param-name>
<param-value>xxxxxxxx</param-value>
</init-param>
<init-param>
<!-- 必填,redis對(duì)應(yīng)的bean,格式為bean:xx-->
<param-name>cacheBean</param-name>
<param-value>bean:redisPersistent</param-value>//DistributedBaseInterFace,對(duì)應(yīng)于此接口,進(jìn)行session的持久化操作
</init-param>
<init-param>
<!-- 必填, -->
<param-name>cookieName</param-name>
<param-value>TESTSESSIONID</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>distributedSessionFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<!-- 分布式session end -->
二、攔截器的實(shí)現(xiàn),核心代碼如下
主要有以下的幾個(gè)類:
- DistributedSessionFilter,
- DistributedSessionManager,
- DistributedHttpSessionWrapper,
- DistributedHttpServletRequestWrapper
1、DistributedSessionFilter實(shí)現(xiàn)Filter:
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
public class DistributedSessionFilter implements Filter {
private static final Logger log = LoggerFactory.getLogger(DistributedSessionFilter.class);
private String cookieName;
//主要是對(duì)session進(jìn)行管理的操作
private DistributedSessionManager distributedSessionManager;
private String key;
}
容器啟動(dòng)時(shí)候的初始化方法:
@Override
public void init(FilterConfig config) throws ServletException {
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(config
.getServletContext());
String key = config.getInitParameter("key");
String cookieName = config.getInitParameter("cookieName");
String cacheBean = config.getInitParameter("cacheBean");
// 獲取bean的名稱,配置是"bean:"
String redisBeanStr = cacheBean.substring(5);
DistributedBaseInterFace distributedCache = (DistributedBaseInterFace) wac.getBean(redisBeanStr);
// 獲取key,有2種配置方式,1對(duì)應(yīng)為bean,格式為bean:key。2字符串
if (key.startsWith("bean:")) {
this.key = (String) wac.getBean(key.substring(5));
} else {
this.key = key;
}
this.cookieName = cookieName;
this.distributedSessionManager = DistributedSessionManager.getInstance(distributedCache);
//異常處理省略。。。
}
進(jìn)行實(shí)際的請(qǐng)求攔截:
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws ServletException, IOException {
DistributedHttpServletRequestWrapper distReq = null;
try {
//請(qǐng)求處理
distReq = createDistributedRequest(servletRequest, servletResponse);
filterChain.doFilter(distReq, servletResponse);
} catch (Throwable e) {
//省略。。。
} finally {
if (distReq != null) {
try {
//處理完成request后,處理session(主要是保存session會(huì)話)
dealSessionAfterRequest(distReq.getSession());
} catch (Throwable e2) {
//省略。。。
}
}
}
}
//分布式請(qǐng)求
private DistributedHttpServletRequestWrapper createDistributedRequest(ServletRequest servletRequest,
ServletResponse servletResponse) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String userSid = CookieUtil.getCookie(cookieName, request);
String actualSid = distributedSessionManager.getActualSid(userSid, request, key);
if (StringUtil.isBlank(actualSid)) {
if (StringUtil.isNotBlank(userSid)) {
log.info("userSid[{}]驗(yàn)證不通過", userSid);
}
// 寫cookie
String[] userSidArr = distributedSessionManager.createUserSid(request, key);
userSid = userSidArr[0];
CookieUtil.setCookie(cookieName, userSid, request, response);
actualSid = userSidArr[1];
}
actualSid = "sid:" + actualSid;
DistributedHttpSessionWrapper distSession = null;
try {
Map<String, Object> allAttribute = distributedSessionManager.getSession(actualSid, request.getSession()
.getMaxInactiveInterval());
distSession = new DistributedHttpSessionWrapper(actualSid, request.getSession(), allAttribute);
} catch (Throwable e) {
// 出錯(cuò),刪掉緩存數(shù)據(jù)
log.error(e.getMessage(), e);
Map<String, Object> allAttribute = new HashMap<String, Object>();
distSession = new DistributedHttpSessionWrapper(actualSid, request.getSession(), allAttribute);
distributedSessionManager.removeSession(distSession);
}
DistributedHttpServletRequestWrapper requestWrapper = new DistributedHttpServletRequestWrapper(request,
distSession);
return requestWrapper;
}
// request處理完時(shí)操作session
private void dealSessionAfterRequest(DistributedHttpSessionWrapper session) {
if (session == null) {
return;
}
if (session.changed) {
distributedSessionManager.saveSession(session);
} else if (session.invalidated) {
distributedSessionManager.removeSession(session);
} else {
distributedSessionManager.expire(session);
}
}
2、DistributedSessionManager,主要處理分布式session,核心代碼:
class DistributedSessionManager {
protected static final Logger log = LoggerFactory.getLogger(DistributedSessionManager.class);
private static DistributedSessionManager instance = null;
//redis處理session的接口,自己根據(jù)情況實(shí)現(xiàn)
private DistributedBaseInterFace distributedBaseInterFace;
private static byte[] lock = new byte[1];
private DistributedSessionManager(DistributedBaseInterFace distributedBaseInterFace) {
this.distributedBaseInterFace = distributedBaseInterFace;
}
public static DistributedSessionManager getInstance(DistributedBaseInterFace redis) {
if (instance == null) {
synchronized (lock) {
if (instance == null) {
instance = new DistributedSessionManager(redis);
}
}
}
return instance;
}
//獲取session
public Map<String, Object> getSession(String sid,int second) {
String json = this.distributedBaseInterFace.get(sid,second);
if (StringUtil.isNotBlank(json)) {
return JsonUtil.unserializeMap(json);
}
return new HashMap<String, Object>(1);
}
//保存session
public void saveSession(DistributedHttpSessionWrapper session) {
Map<String, Object> map=session.allAttribute;
if(MapUtil.isEmpty(map)){
return;
}
String json = JsonUtil.serializeMap(map);
this.distributedBaseInterFace.set(session.getId(), json, session.getMaxInactiveInterval());
}
//刪除session
public void removeSession(DistributedHttpSessionWrapper session) {
distributedBaseInterFace.del(session.getId());
}
public void expire(DistributedHttpSessionWrapper session) {
distributedBaseInterFace.expire(session.getId(), session.getMaxInactiveInterval());
}
/**
* 創(chuàng)建cookie的sid
*/
public String[] createUserSid(HttpServletRequest request, String key) {
//...
}
public String getActualSid(String userSid, HttpServletRequest request, String key) {
//...
}
}
3、DistributedHttpSessionWrapper 實(shí)現(xiàn)了 HttpSession,進(jìn)行分布式session包裝,核心代碼:
public class DistributedHttpSessionWrapper implements HttpSession {
private HttpSession orgiSession;
private String sid;
boolean changed = false;
boolean invalidated = false;
Map<String, Object> allAttribute;
public DistributedHttpSessionWrapper(String sid, HttpSession session, Map<String, Object> allAttribute) {
this.orgiSession = session;
this.sid = sid;
this.allAttribute = allAttribute;
}
@Override
public String getId() {
return this.sid;
}
@Override
public void setAttribute(String name, Object value) {
changed = true;
allAttribute.put(name, value);
}
@Override
public Object getAttribute(String name) {
return allAttribute.get(name);
}
@Override
public Enumeration<String> getAttributeNames() {
Set<String> set = allAttribute.keySet();
Iterator<String> iterator = set.iterator();
return new MyEnumeration<String>(iterator);
}
private class MyEnumeration<T> implements Enumeration<T> {
Iterator<T> iterator;
public MyEnumeration(Iterator<T> iterator) {
super();
this.iterator = iterator;
}
@Override
public boolean hasMoreElements() {
return iterator.hasNext();
}
@Override
public T nextElement() {
return iterator.next();
}
}
@Override
public void invalidate() {
this.invalidated = true;
}
@Override
public void removeAttribute(String name) {
changed = true;
allAttribute.remove(name);
}
@Override
public long getCreationTime() {
return orgiSession.getCreationTime();
}
@Override
public long getLastAccessedTime() {
return orgiSession.getLastAccessedTime();
}
@Override
public int getMaxInactiveInterval() {
return orgiSession.getMaxInactiveInterval();
}
@Override
public ServletContext getServletContext() {
return orgiSession.getServletContext();
}
@Override
public Object getValue(String arg0) {
return orgiSession.getValue(arg0);
}
@Override
public String[] getValueNames() {
return orgiSession.getValueNames();
}
@Override
public boolean isNew() {
return orgiSession.isNew();
}
@Override
public void putValue(String arg0, Object arg1) {
orgiSession.putValue(arg0, arg1);
}
@Override
public void removeValue(String arg0) {
orgiSession.removeValue(arg0);
}
@Override
public void setMaxInactiveInterval(int arg0) {
orgiSession.setMaxInactiveInterval(arg0);
}
@Override
public HttpSessionContext getSessionContext() {
return orgiSession.getSessionContext();
}
4、DistributedHttpServletRequestWrapper 實(shí)現(xiàn)了 HttpServletRequestWrapper,包裝處理過的session和原始request,核心代碼:
public class DistributedHttpServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper {
private HttpServletRequest orgiRequest;
private DistributedHttpSessionWrapper session;
public DistributedHttpServletRequestWrapper(HttpServletRequest request, DistributedHttpSessionWrapper session) {
super(request);
if (session == null){
//異常處理。。
}
if (request == null){
//異常處理。。
}
this.orgiRequest = request;
this.session = session;
}
public DistributedHttpSessionWrapper getSession(boolean create) {
orgiRequest.getSession(create);
return session;
}
public DistributedHttpSessionWrapper getSession() {
return session;
}
}
5、另外,定義DistributedBaseInterFace接口,用來處理session入redis進(jìn)行持久化操作:
public interface DistributedBaseInterFace {
/**
* 根據(jù)key獲取緩存數(shù)據(jù)
* @param key
* @param seconds
*/
public String get(String key,int seconds);
/**
* 更新緩存數(shù)據(jù)
* @param key
* @param json
* @param seconds
*/
public void set(String key, String json,int seconds);
/**
* 刪除緩存
* @param key
*/
public void del(String key);
/**
* 設(shè)置過期數(shù)據(jù)
* @param key
* @param seconds
*/
public void expire(String key,int seconds);
注:本文只是在Spring中采用redis的方式對(duì)session進(jìn)行管理,還有其他諸多的實(shí)現(xiàn)方式,比如在容器里面配置等,設(shè)計(jì)路由算法讓session依賴于集群中的各個(gè)節(jié)點(diǎn)服務(wù)器,,,,,,但redis這種方式在實(shí)際應(yīng)用中還是比較廣泛的,LZ公司主要就是采用此方式。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java基于NIO實(shí)現(xiàn)群聊系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java基于NIO實(shí)現(xiàn)群聊系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
Java動(dòng)態(tài)代理分析及簡單實(shí)例
這篇文章主要介紹了 Java動(dòng)態(tài)代理分析及簡單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-02-02
Spring?IOC?xml方式進(jìn)行工廠Bean操作詳解
這篇文章主要介紹了Spring?IOC?xml方式進(jìn)行工廠Bean操作,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-01-01
SpringBoot開發(fā)實(shí)戰(zhàn)系列之動(dòng)態(tài)定時(shí)任務(wù)
在我們?nèi)粘5拈_發(fā)中,很多時(shí)候,定時(shí)任務(wù)都不是寫死的,而是寫到數(shù)據(jù)庫中,從而實(shí)現(xiàn)定時(shí)任務(wù)的動(dòng)態(tài)配置,下面這篇文章主要給大家介紹了關(guān)于SpringBoot開發(fā)實(shí)戰(zhàn)系列之動(dòng)態(tài)定時(shí)任務(wù)的相關(guān)資料,需要的朋友可以參考下2021-08-08
Mac OS X 下 IntelliJ IDEA、jEdit 等 Java 程序中文標(biāo)點(diǎn)輸入無效的完美解決方法
Mac OS X 下基于 Java 的程序會(huì)出現(xiàn)中文標(biāo)點(diǎn)輸入無效的問題,在中文輸入法狀態(tài),可以輸入中文字,但輸入中文標(biāo)點(diǎn)最后上去的是英文標(biāo)點(diǎn).這篇文章主要介紹了Mac OS X 下 IntelliJ IDEA、jEdit 等 Java 程序中文標(biāo)點(diǎn)輸入無效的完美解決方法,需要的朋友可以參考下2016-10-10
SpringBoot不讀取bootstrap.yml/properties文件問題
這篇文章主要介紹了SpringBoot不讀取bootstrap.yml/properties文件問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
spring boot啟動(dòng)時(shí)mybatis報(bào)循環(huán)依賴的錯(cuò)誤(推薦)
今天小編抽時(shí)間給大家分享spring boot啟動(dòng)時(shí)mybatis報(bào)循環(huán)依賴的錯(cuò)誤,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-12-12
Springboot整合PageOffice 實(shí)現(xiàn)word在線編輯保存功能
這篇文章主要介紹了Springboot整合PageOffice 實(shí)現(xiàn)word在線編輯保存,本文以Samples5 為示例文件結(jié)合示例代碼給大家詳細(xì)介紹,需要的朋友可以參考下2021-08-08

