基於Redis實現分布式Session
1、概述
我們可以自己實作類別似Session的機制,採用 Redis 等分布式緩衝中介軟體來實現。Redis是獨立於應用伺服器的,基於Redis實現的Session機制自動具備了分布式屬性。Redis可以很方便地做叢集配置,則Session避免了單點故障。
2、實現
實現代碼極其簡單,如下所示。
/**
* @author liuhailong2008#foxmail
*/
public class ApiSession implements Serializable {
private static final long serialVersionUID = 1055965810150154404L;
/**Session ID*/
private final String id;
/**Session建立時間*/
private long creationTime;
/**Session最後一次訪問時間*/
private long lastAccessedTime;
/**Session的最大空閑時間間隔*/
private int maxInactiveInterval;
/**是否是建立Session*/
private boolean newSession;
private static final String SESSION_KEY_PREFIX = "SESS_";
//private Set<String> attrNameSet = Collections.synchronizedSet(new HashSet<String>());
private final String sessionKey ;
/**
* 建立新的Session。
* @param maxIdleSeconds
*/
public ApiSession(int maxIdleSeconds){
id = StringUtil.getUUID();
long now = System.currentTimeMillis();
creationTime = now;
lastAccessedTime = now;
this.maxInactiveInterval = maxIdleSeconds;
newSession = true;
//this.attrNameSet.clear();
sessionKey = SESSION_KEY_PREFIX + id;
CacheBlock cb = CacheManager.getBlock(Const.CACHE_BLOCK_INDEX);
CacheElement ce = new CacheElement(sessionKey,this);
ce.setTimeToIdleSeconds(this.getMaxInactiveInterval());
cb.put(ce);
}
/**
* 通過Session id擷取已經存在的Session,如果沒有,返回null。
* @return
*/
public static ApiSession get(String id){
String sessionKey = SESSION_KEY_PREFIX + id;
CacheBlock cb = CacheManager.getBlock(Const.CACHE_BLOCK_INDEX);
ApiSession ret = (ApiSession) cb.get(sessionKey);
if(ret!=null){
ret.newSession = false;
ret.refresh();
}
return ret;
}
/**
* 更新 lastAccessedTime 。
*/
public void refresh() {
this.lastAccessedTime = System.currentTimeMillis();
CacheBlock cb = CacheManager.getBlock(Const.CACHE_BLOCK_INDEX);
CacheElement ce = new CacheElement(sessionKey,this);
ce.setTimeToIdleSeconds(this.getMaxInactiveInterval());
cb.put(ce);
}
/**
* 是否逾時到期。
*
* @param session
* @return
*/
public boolean isExpired() {
CacheBlock cb = CacheManager.getBlock(Const.CACHE_BLOCK_INDEX);
ApiSession _this = (ApiSession) cb.get(this.sessionKey);
// 先查看緩衝層面的逾時控制
if(_this==null){
return false;
}
long now = System.currentTimeMillis();
long last = this.getLastAccessedTime();
long interal = now - last;
if(interal>this.getMaxInactiveInterval()){
this.invalidate();
return true;
}else{
return false;
}
}
/**
* 強制Session立即失效。
*/
public synchronized void invalidate() {
CacheBlock cb = CacheManager.getBlock(Const.CACHE_BLOCK_INDEX);
cb.remove(this.sessionKey);
}
/**
* 移除屬性。
*
* @param attrName
* @return
*/
public synchronized Object removeAttribute(String attrName){
this.refresh();
String attrSessionKey = getAttrSessionKey(attrName);
CacheBlock cb = CacheManager.getBlock(Const.CACHE_BLOCK_INDEX);
Object ret = cb.remove(attrSessionKey);
return ret;
}
/**
* 設定屬性。
* @param attrName
* @param attrValue
*/
public synchronized void setAttribute(String attrName,Object attrValue){
this.refresh();
String attrSessionKey = getAttrSessionKey(attrName);
CacheBlock cb = CacheManager.getBlock(Const.CACHE_BLOCK_INDEX);
CacheElement ce = new CacheElement(attrSessionKey,attrValue);
ce.setTimeToIdleSeconds(this.getMaxInactiveInterval());
cb.put(ce);
}
/**
* 擷取屬性的值。
* @param attrName
* @return
*/
public Object getAttribute(String attrName){
this.refresh();
String attrSessionKey = getAttrSessionKey(attrName);
CacheBlock cb = CacheManager.getBlock(Const.CACHE_BLOCK_INDEX);
Object retObject = cb.get(attrSessionKey);
return retObject;
}
private String getAttrSessionKey(String attrName){
String attrSessionKey = sessionKey + attrName;
return attrSessionKey;
}
public int getMaxInactiveInterval() {
if(maxInactiveInterval==-1){
maxInactiveInterval = 3600;
}
return maxInactiveInterval;
}
public void setMaxInactiveInterval(int maxInactiveInterval) {
this.maxInactiveInterval = maxInactiveInterval;
}
public String getId() {
return id;
}
public long getCreationTime() {
return creationTime;
}
public long getLastAccessedTime() {
return lastAccessedTime;
}
public boolean isNewSession() {
return newSession;
}
}
3、用法
3.1、建立Session
// 建立Session
int maxIdleSeconds = 60 * 20 ;
ApiSession session = new ApiSession( maxIdleSeconds );
String sessId = session.getId();
session.setAttribute("CURRENT_USER", user);1
3.2、讀取Session
// 讀取Session
ApiSession session = ApiSession.get(tokenToBeChecked);
if(session==null){
logger.debug(String.format("會話逾時啦,token:%s。", tokenToBeChecked));
return false;
}
// 檢查是否逾時
boolean isExpired = session.isExpired();
if(isExpired){
logger.debug(String.format("會話逾時啦,token:%s。", tokenToBeChecked));
return false;
}
// 從Sesion中取出token
String token = (String)session.getAttribute(Const.TOKEN_SESSION_KEY);
if(StringUtils.isEmpty(token)){
return false;
}
// 同調用方提交的比較
if(token.equalsIgnoreCase(tokenToBeChecked)){
session.refresh();
return true;
}
4、最佳化點
以上只是提供了實現範例。進一步的最佳化點包括:
Redis儲存規劃,設計Session Id 、Attr的儲存方式。
採用自己的持久化方式,提高持久化效率。
提供更多工具方法,讓Session更易用。
進一步實現Session的其他介面。
等等。
未盡事宜,歡迎留言討論。
Ubuntu 14.04下Redis安裝及簡單測試
Redis叢集明細文檔
Ubuntu 12.10下安裝Redis(圖文詳解)+ Jedis串連Redis
Redis系列-安裝部署維護篇
CentOS 6.3安裝Redis
Redis安裝部署學習筆記
Redis設定檔redis.conf 詳解
Redis 的詳細介紹:請點這裡
Redis 的:請點這裡
本文永久更新連結地址: