Redis實現分散式session功能的共享
阿新 • • 發佈:2018-12-25
最近專案設計叢集,實現了一下session的共享功能,其原理是將session儲存到分散式快取資料庫中如:redis, memcache等,然後多個伺服器tomcat 每次請求都通過NoSql資料庫查詢,如果存在,則獲取值;反之存放值。 我是通過redis來實現session的共享,其主要有一下兩種方法: 1、通過tomcat伺服器的拓展功能實現 這種方式比較簡單,主要是通過繼承session的ManagerBase類,實現重寫session相關的方法,這種比較簡單, 參考原始碼連結(http://download.csdn.net/detail/fengshizty/9417242)。 2、通過filter攔截request請求實現 下面主要介紹這樣實現方式: (1)寫HttpSessionWrapper實現HttpSession介面,實現裡面session相關的方法。 (2)寫HttpServletRequestWrapper繼承javax.servlet.http.HttpServletRequestWrapper類,重寫對於session 相關的方法。 (3)寫SessionFilter攔截配置的請求url,過去cookie中 的sessionId,如果為空,對此次請求重寫生成一個新的sessionId,在sessionId構造新的HttpServletRequestWrapper物件。 (4)寫SessionService實現session到redis的儲存和過去,其key為sessionId,value為session對於的Map。 3、程式碼實現 (1)HttpSessionWrapper
/**
* 建立時間:2016年1月21日 下午7:55:41
*
* @author andy
* @version 2.2
*/
public class HttpSessionWrapper implements HttpSession {
private String sid = "";
private HttpSession session;
private HttpServletRequest request;
private HttpServletResponse response;
private Map<String, Object> map = null ;
private SessionService sessionService = (SessionService) SpringContextHolder.getBean("sessionService");
public HttpSessionWrapper() {
}
public HttpSessionWrapper(HttpSession session) {
this.session = session;
}
public HttpSessionWrapper(String sid, HttpSession session) {
this (session);
this.sid = sid;
}
public HttpSessionWrapper(String sid, HttpSession session,
HttpServletRequest request, HttpServletResponse response) {
this(sid, session);
this.request = request;
this.response = response;
}
private Map<String, Object> getSessionMap() {
if (this.map == null) {
this.map = sessionService.getSession(this.sid);
}
return this.map;
}
@Override
public Object getAttribute(String name) {
if (this.getSessionMap() != null) {
Object value = this.getSessionMap().get(name);
return value;
}
return null;
}
@Override
public void setAttribute(String name, Object value) {
this.getSessionMap().put(name, value);
sessionService.saveSession(this.sid, this.getSessionMap());
}
@Override
public void invalidate() {
this.getSessionMap().clear();
sessionService.removeSession(this.sid);
CookieUtil.removeCookieValue(this.request,this.response, GlobalConstant.JSESSIONID);
}
@Override
public void removeAttribute(String name) {
this.getSessionMap().remove(name);
sessionService.saveSession(this.sid, this.getSessionMap());
}
@Override
public Object getValue(String name) {
return this.session.getValue(name);
}
@SuppressWarnings("unchecked")
@Override
public Enumeration getAttributeNames() {
return (new Enumerator(this.getSessionMap().keySet(), true));
}
@Override
public String[] getValueNames() {
return this.session.getValueNames();
}
@Override
public void putValue(String name, Object value) {
this.session.putValue(name, value);
}
@Override
public void removeValue(String name) {
this.session.removeValue(name);
}
@Override
public long getCreationTime() {
return this.session.getCreationTime();
}
@Override
public String getId() {
return this.sid;
}
@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 boolean isNew() {
return this.session.isNew();
}
}
(2)HttpServletRequestWrapper實現
/**
* 建立時間:2016年1月22日 下午7:52:29
*
* @author andy
* @version 2.2
*/
public class HttpServletRequestWrapper extends
javax.servlet.http.HttpServletRequestWrapper {
private HttpSession session;
private HttpServletRequest request;
private HttpServletResponse response;
private String sid = "";
public HttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
public HttpServletRequestWrapper(String sid, HttpServletRequest request) {
super(request);
this.sid = sid;
}
public HttpServletRequestWrapper(String sid, HttpServletRequest request,
HttpServletResponse response) {
super(request);
this.request = request;
this.response = response;
this.sid = sid;
if (this.session == null) {
this.session = new HttpSessionWrapper(sid, super.getSession(false),
request, response);
}
}
@Override
public HttpSession getSession(boolean create) {
if (this.session == null) {
if (create) {
this.session = new HttpSessionWrapper(this.sid,
super.getSession(create), this.request, this.response);
return this.session;
} else {
return null;
}
}
return this.session;
}
@Override
public HttpSession getSession() {
if (this.session == null) {
this.session = new HttpSessionWrapper(this.sid, super.getSession(),
this.request, this.response);
}
return this.session;
}
}
(3)SessionFilter攔截器的實現
public class SessionFilter extends OncePerRequestFilter implements Filter {
private static final Logger LOG = Logger.getLogger(SessionFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
//從cookie中獲取sessionId,如果此次請求沒有sessionId,重寫為這次請求設定一個sessionId
String sid = CookieUtil.getCookieValue(request, GlobalConstant.JSESSIONID);
if(StringUtils.isEmpty(sid) || sid.length() != 36){
sid = StringUtil.getUuid();
CookieUtil.setCookie(request, response, GlobalConstant.JSESSIONID, sid, 60 * 60);
}
//交給自定義的HttpServletRequestWrapper處理
filterChain.doFilter(new HttpServletRequestWrapper(sid, request, response), response);
}
}
(4)SessionService實現session從redis的讀寫儲存
public class SessionService {
private final static Logger LOG = Logger.getLogger(SessionService.class);
private JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer();
@Autowired
private RedisTemplate<Serializable, Serializable> redisTemplate;
@SuppressWarnings("unchecked")
public Map<String, Object> getSession(String sid) {
Map<String, Object> session = new HashMap<String, Object>();
try {
Object obj = redisTemplate.opsForValue()
.get(RedisKeyUtil.SESSION_DISTRIBUTED_SESSIONID + sid);
if(obj != null){
obj = jdkSerializer.deserialize((byte[])obj);
session = (Map<String, Object>) obj;
}
} catch (Exception e) {
LOG.error("Redis獲取session異常" + e.getMessage(), e.getCause());
}
return session;
}
public void saveSession(String sid, Map<String, Object> session) {
try {
redisTemplates.opsForValue()
.set(RedisKeyUtil.SESSION_DISTRIBUTED_SESSIONID + sid,
jdkSerializer.serialize(session), RedisKeyUtil.SESSION_TIMEOUT,
TimeUnit.MINUTES);
} catch (Exception e) {
LOG.error("Redis儲存session異常" + e.getMessage(), e.getCause());
}
}
public void removeSession(String sid) {
try {
redisTemplates.delete(
RedisKeyUtil.SESSION_DISTRIBUTED_SESSIONID + sid);
} catch (Exception e) {
LOG.error("Redis刪除session異常" + e.getMessage(), e.getCause());
}
}
}
(5)Session的攔截配置,一般的我們只需要攔截我們定義的攔截請求攔截,而不需要所有的都需要攔截。在web.xml中配置SessionFilter。
<filter>
<filter-name>sessionFilter</filter-name>
<filter-class>org.andy.shop.session.SessionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sessionFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
附:設計到的工具類
1、StringUtil工具類
/**
* String工具類
*
* @author andy
* @date 2015-5-16 下午4:04:22
*
*/
public class StringUtil {
private StringUtil() {
super();
}
/**
* 出去null和""
* @param src
* @return
*/
public static String formatNull(String src) {
return (src == null || "null".equals(src)) ? "" : src;
}
/**
* 判斷字串是否為空的正則表示式,空白字元對應的unicode編碼
*/
private static final String EMPTY_REGEX = "[\\s\\u00a0\\u2007\\u202f\\u0009-\\u000d\\u001c-\\u001f]+";
/**
* 驗證字串是否為空
*
* @param input
* @return
*/
public static boolean isEmpty(String input) {
return input == null || input.equals("") || input.matches(EMPTY_REGEX);
}
public static boolean isNotEmpty(String input){
return !isEmpty(input);
}
public static String getUuid() {
return UUID.randomUUID().toString();
}
}
2、Cookie管理CookieUtil工具類
/**
* 建立時間:2016年1月22日 下午8:33:56
*
* @author andy
* @version 2.2
*/
public class CookieUtil {
private static final String KEY = "jkdflsffff()kldkjapfdY=::$B+DUOWAN";
private HttpServletRequest request;
private HttpServletResponse response;
private static String domain = "andy.com";
public CookieUtil(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
/**
* 儲存cookie
* @param request
* @param response
* @param name cookie名稱
* @param value cookie值
* @param seconds 過期時間(單位秒) -1代表關閉瀏覽器時cookie即過期
*/
public static void setCookie(HttpServletRequest request,
HttpServletResponse response, String name, String value, int seconds) {
if (StringUtils.isEmpty(name) || StringUtils.isEmpty(value))
return;
Cookie cookie = new Cookie(name, value);
//cookie.setDomain(domain);
cookie.setMaxAge(seconds);
cookie.setPath("/");
response.setHeader("P3P",
"CP='IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT'");
response.addCookie(cookie);
}
/**
* 過去cookie中儲存的值
* @param name
* @return
* @throws UnsupportedEncodingException
*/
public String getCookieValue(String name)
throws UnsupportedEncodingException {
Cookie cookies[] = request.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
if (name.equalsIgnoreCase(cookies[i].getName())) {
return cookies[i].getValue();
}
}
}
return "";
}
/**
* 設定加密的cookie
* @param name cookie名稱
* @param value cookie值
* @param seconds 過期時間 -1代表關閉瀏覽器時cookie即過期
*/
public void setCheckCodeCookie(String name, String value, int seconds) {
if (StringUtils.isEmpty(name) || StringUtils.isEmpty(value)) {
return;
}
String md5Value = MD5Utils.getMD5(KEY + value);
Cookie cookie = new Cookie(name, md5Value);
//cookie.setDomain(domain);
cookie.setMaxAge(seconds);
cookie.setPath("/");
response.addCookie(cookie);
}
/**
* 校驗加密的cookie
* @param name
* @param value
* @return
*/
public boolean checkCodeCookie(String name, String value) {
if (StringUtils.isEmpty(name) || StringUtils.isEmpty(value)) {
return false;
}
boolean result = false;
String cookieValue = getCookieValue(request, name);
if (MD5Utils.getMD5(KEY + value).equalsIgnoreCase(
cookieValue)) {
result = true;
}
return result;
}
/**
* 獲取cookie值
* @param request
* @param name
* @return
*/
public static String getCookieValue(HttpServletRequest request, String name) {
try {
Cookie cookies[] = request.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
if (name.equalsIgnoreCase(cookies[i].getName())) {
return cookies[i].getValue();
}
}
}
} catch (Exception e) {
}
return "";
}
/**
* 移除客戶端的cookie
* @param request
* @param response
* @param name
*/
public static void removeCookieValue(HttpServletRequest request,
HttpServletResponse response, String name) {
try {
Cookie cookies[] = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
if (name.equalsIgnoreCase(cookie.getName())) {
cookie = new Cookie(name, null);
cookie.setMaxAge(0);
cookie.setPath("/");
//cookie.setDomain(domain);
response.addCookie(cookie);
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、redis鍵key設定類RedisKeyUtil
/**
* 建立時間:2015年9月22日 下午4:51:11
*
* @author andy
* @version 2.2
*/
public class RedisKeyUtil {
public static final String SESSION_DISTRIBUTED_SESSIONID = "session:distributed:"; //分散式session sessionid -- sessionvalue
public static final Integer SESSION_TIMEOUT = 2; //session 失效時間2小時
}
4、分散式session常量設定類GlobalConstant
/**
* 建立時間:2016年1月23日 上午11:16:56
*
* 分散式session常量
*
* @author andy
* @version 2.2
*/
public class GlobalConstant {
public static final String USER_SESSION_KEY = "user_session_key";//使用者session資訊
public static final String JSESSIONID = "YHMJSESSIONID"; //jsessionid
}
分散式session在redis執行結果: