分散式應用session會話管理-基於redis
session會話在單臺伺服器下不會出現共享問題,現在應用部署方式都是分散式,或者叢集部署,這樣必然會面臨一個問題,session共享。
session共享的解決方案也有很多,
一、web伺服器的粘性請求,比如採用nginx請求分發,使用ip_hash這種負載均衡方式,客戶端請求只會被分發到相同的後臺server,這樣可以避免session共享的問題。但是缺點也很明顯
二、基於資料庫儲存(網站使用者量大的情況下,頻繁dml資料,對db壓力大)
三、基於cookie儲存(安全問題、雖然可以加密儲存、但是我覺得永遠不能將敏感資料放在客戶端,不信任啊O(∩_∩)O哈哈~)
四、伺服器內建的session複製域(比如was下提供session複製功能、但這個損耗伺服器記憶體)
五、基於nosql(memcache、redis都可以)
http請求是無狀態的
這裡要引入一個概念sessionid,session物件當客戶端首次訪問時,建立一個新的session物件.並同時生成一個sessionId,並在此次響應中將sessionId以響應報文的方式些回客戶端瀏覽器記憶體或以重寫url方式送回客戶端,來保持整個會話
也就是說客戶端request請求時候,如果獲取了session,就預設分配一個jessionid,然後通過response響應到客戶端cookie,然後客戶端下一次請求,預設會攜帶這個jessionid請求到服務端,服務端拿到這個jessionid來區分不同的客戶端。
說清楚這些,就可以從sessionid入手了,要實現將session資訊存入redis,總結了下大概是三點:
1.實現httpsession介面,重寫我們需要用到的方法,比如set get這些。。balabala一堆......
2.繼承HttpServletRequestWrapper,這個類裡面有getSession()方法,我們需要重寫,來取自定義session
3.實現filter,用來攔截客戶端請求,獲取我們自定義的request,從而獲得session
具體實現:
1.實現httpsession介面
public class HttpSessionWrapper implements HttpSession { protected final Logger logger = LoggerFactory.getLogger(getClass()); private String sid = ""; private HttpServletRequest request; private HttpServletResponse response; private final long creationTime = System.currentTimeMillis(); private final long lastAccessedTime = System.currentTimeMillis(); private SessionMeta meta; public HttpSessionWrapper() { } public HttpSessionWrapper(String sid,SessionMeta meta, HttpServletRequest request, HttpServletResponse response) { this.sid=sid; this.request=request; this.response=response; this.meta=meta; } public Object getAttribute(String name) { logger.info(getClass()+"getAttribute(),name:"+name); Jedis jedis =null; Object obj =null; String jsonStr = null; try { jedis =JedisPoolStore.getInstance().getJedis(meta.getHost(), meta.getPort()); jsonStr = jedis.get(name); if(jsonStr!=null||StringUtils.isNotEmpty(jsonStr)){ jedis.expire(name, meta.getSeconds());// 重置過期時間 obj =JSON.parseObject(jsonStr, User.class); //反序列物件 } if (jedis != null) { JedisPoolStore.getInstance().returnJedis(jedis); } return obj; } catch (JSONException je) { logger.error(je.getMessage()); if (null != jedis) JedisPoolStore.getInstance().returnJedis(jedis); return jsonStr; } catch (Exception e) { logger.error(e.getMessage()); if (e instanceof JedisException) { if (null != jedis) JedisPoolStore.getInstance().returnBrokenJedis(jedis); } else { if (null != jedis) JedisPoolStore.getInstance().returnJedis(jedis); } throw new HttpSessionException(" session 異常 getAttribute() name:"+name); } } public void setAttribute(String name, Object value) { logger.info(getClass()+"setAttribute(),name:"+name); Jedis jedis =null; try { jedis =JedisPoolStore.getInstance().getJedis(meta.getHost(), meta.getPort()); if(value instanceof String){ String value_ =(String) value; jedis.set(name,value_);//普通字串物件 }else{ jedis.set(name, JSON.toJSONString(value));//序列化物件 } jedis.expire(name, meta.getSeconds());// 重置過期時間 if (jedis != null) { JedisPoolStore.getInstance().returnJedis(jedis); } } catch (Exception e) { logger.error(e.getMessage()); if (e instanceof JedisException) { if (null != jedis) JedisPoolStore.getInstance().returnBrokenJedis(jedis); } else { if (null != jedis) JedisPoolStore.getInstance().returnJedis(jedis); } throw new HttpSessionException(" session 異常 setAttribute() name:"+name+",value:"+value); } } /** * 不可用 * @deprecated * */ public void invalidate() { logger.info(getClass()+"invalidate()"); } public void removeAttribute(String name) { logger.info(getClass()+"removeAttribute(),name:"+name); if(StringUtils.isNotEmpty(name)){ Jedis jedis =null; try { jedis =JedisPoolStore.getInstance().getJedis(meta.getHost(), meta.getPort()); jedis.del(name); if (jedis != null) { JedisPoolStore.getInstance().returnJedis(jedis); } } catch (Exception e) { logger.error(e.getMessage()); if (e instanceof JedisException) { if (null != jedis) JedisPoolStore.getInstance().returnBrokenJedis(jedis); } else { if (null != jedis) JedisPoolStore.getInstance().returnJedis(jedis); } throw new HttpSessionException(" session 異常 removeAttribute() name:"+name); } } } /** * 不可用 * @deprecated * */ public Object getValue(String name) { return null; } /** * 不可用 * @deprecated * */ public Enumeration getAttributeNames() { return null; } /** * 不可用 * @deprecated * */ public String[] getValueNames() { return null; } /** * 不可用 * @deprecated * */ public void putValue(String name, Object value) { } /** * 不可用 * @deprecated * */ public void removeValue(String name) { } public long getCreationTime() { return creationTime; } public String getId() { logger.info(getClass()+"getId():"+sid); return sid; } public long getLastAccessedTime() { return lastAccessedTime; } /** * 不可用 * @deprecated * */ public ServletContext getServletContext() { return null; } /** * 不可用 * @deprecated * */ public void setMaxInactiveInterval(int interval) { } /** * 不可用 * @deprecated * */ public int getMaxInactiveInterval() { return 0; } /** * 不可用 * @deprecated * */ public HttpSessionContext getSessionContext() { return null; } /** * 不可用 * @deprecated * */ public boolean isNew() { logger.info(getClass()+"isNew()"); return false; } }
2.繼承HttpServletRequestWrapper
/***
*
* @author xiaoshuai
*
*/
public class DefinedHttpServletRequestWrapper extends HttpServletRequestWrapper{
protected final Logger logger = LoggerFactory.getLogger(getClass());
private HttpSessionWrapper currentSession;
private HttpServletRequest request;
private HttpServletResponse response;
private String sid = "";
private SessionMeta meta;
public DefinedHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
public DefinedHttpServletRequestWrapper(String sid, HttpServletRequest request) {
super(request);
this.sid = sid;
}
public DefinedHttpServletRequestWrapper(String sid, SessionMeta meta,HttpServletRequest request,
HttpServletResponse response) {
super(request);
this.request = request;
this.response = response;
this.sid = sid;
this.meta=meta;
}
@Override
public HttpSession getSession(boolean create) {
if(currentSession != null) {
return currentSession;
}
if(!create) {
return null;
}
currentSession = new HttpSessionWrapper(sid,meta, request, response);
return currentSession;
}
@Override
public HttpSession getSession() {
return getSession(true);
}
}
3.實現filter
public class SessionFilter implements Filter{
protected final Logger logger = LoggerFactory.getLogger(getClass());
private static SessionMeta meta = new SessionMeta();
private static final String host ="host";
private static final String port ="port";
private static final String seconds="seconds";
public void init(FilterConfig filterConfig) throws ServletException {
logger.debug("init filterConfig info");
meta.setHost(filterConfig.getInitParameter(host));
meta.setPort(Integer.parseInt(filterConfig.getInitParameter(port)));
meta.setSeconds(Integer.parseInt(filterConfig.getInitParameter(seconds)));
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//從cookie中獲取sessionId,如果此次請求沒有sessionId,重寫為這次請求設定一個sessionId
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String sid = CookieHelper.findCookieInfo(httpRequest,CookieHelper.COOKIE_DOMAIN_NAME);
if(StringUtils.isEmpty(sid) ){
try {
sid =CookieHelper.saveCookie(SessionId.doIds(), httpResponse);
} catch (Exception e) {
e.printStackTrace();
}
}
logger.info("JESSIONID:"+sid);
chain.doFilter(new DefinedHttpServletRequestWrapper(sid,meta,httpRequest, httpResponse), response);
}
public void destroy() {
}
}
3.配置web.xml
<!-- session過濾器 -->
<filter>
<filter-name>sessionFilter</filter-name>
<filter-class>cn.session.filter.SessionFilter</filter-class>
<init-param>
<param-name>host</param-name>
<param-value>10.1.193.1</param-value>
</init-param>
<init-param>
<param-name>port</param-name>
<param-value>6372</param-value>
</init-param>
<init-param>
<param-name>seconds</param-name>
<param-value>1800</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>sessionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
第一次產生sessionid訪問:
系統登入後存使用者資訊至redis,以及產生令牌:
關於安全這塊,因為不管登入系統與否,sessionid都會產生,這時候就會產生一個問題,因為cookie是可以被修改的,就會產生一個問題,撞session的分享。。。換成不同的sessionid去請求系統。。。總有一天會撞上。。
SO,我這邊是這樣處理的,
當登入成功之後,產生一個token令牌,產生規則的話自己定,一堆金鑰比如系統名字+sessionid+userid+固定字元,產生一個加密的字串,放入cookie。
這樣當我們獲取當前登入使用者時,解密token,獲取sessionid,然後取redis使用者資訊。。(切記這裡不要直接通過sessionid取使用者資訊,有風險!!!)
使用者沒有登入成功,自然也沒有這個令牌。。。
這裡又有另外一個問題,使用者如果禁用cookie呢????? so what..... 下次再說這個。。。
水平有限,如果你有更好的方式,請聯絡我,交流................................tks!!!!相關推薦
分散式應用session會話管理-基於redis
session會話在單臺伺服器下不會出現共享問題,現在應用部署方式都是分散式,或者叢集部署,這樣必然會面臨一個問題,session共享。 session共享的解決方案也有很多, 一、web伺服器的粘性請求,比如採用nginx請求分發,使用ip_hash這種負載均衡方式,
分散式Tomcat session會話Sticky Sessions問題
分散式session會話Sticky Sessions - tomcat_baby的專欄 - CSDN部落格https://blog.csdn.net/tomcat_baby/article/details/52787679 關於 tomcat 叢集中 session 共享的三種方法 - 銳洋智慧 - 部落
分散式鎖與實現(一)——基於Redis實現
概述 目前幾乎很多大型網站及應用都是分散式部署的,分散式場景中的資料一致性問題一直是一個比較重要的話題。分散式的CAP理論告訴我們“任何一個分散式系統都無法同時滿足一致性(Consistency)、可用性(Availability)和分割槽容錯性(Partit
使用shiro的會話管理和redis快取管理來構建登入模組spring+struts+hibernate(SSH)
shiro是一個很好用的安全框架,主要表現在使用者認證,許可權認證,會話管理,如果想優化還可以做Cache管理,我們不需要做太多工作在使用者身份token安全方面(記錄shiro及用redis開發的步驟及一些問題,因為網上很多資料都不給全程式碼讓小白沒法理解,這裡我
Spring-Session基於Redis管理Session
在上文Tomcat Session管理分析介紹了使用tomcat-redis-session-manager來集中式管理session,其中一個侷限性就是必須使用tomcat容器;本文介紹的spring-session也能實現session的集中式管理,並且不侷限於某種容器; spring-se
基於redis叢集實現的分散式鎖,可用於秒殺商品的庫存數量管理,有測試程式碼(何志雄)
轉載請標明出處。 在分散式系統中,經常會出現需要競爭同一資源的情況,本程式碼基於redis3.0.1+jedis2.7.1實現了分散式鎖。 可用於例如秒殺系統中的商品庫存的管理。付完整程式碼及測試用例。 package com.gaojiasoft.gaojiaRe
【redis學習之六】基於Redis的分散式session實現
在web應用中,我們經常會用session來儲存已登入使用者的相關資訊,在單機應用中,由於所有的使用者都訪問同一個應用,而session都儲存在此單機應用中所以並無不妥。但是隨著使用者併發量的上升,分散式系統勢在必行,這就導致一個使用者的訪問請求可能會分發到不同的叢集部署
分散式下會話追蹤[基於Cookie和Redis的實現]
一. 叢集遇到的問題 從使用者端來解釋,就是當一個使用者第一次訪問被負載均衡代理到後端伺服器A並登入後,伺服器A上保留了使用者的登入資訊;當用戶再次傳送請求時,根據負載均衡策略可能被代理到後端不同的伺服器,例如伺服器B,由於這臺伺服器B沒有使用者的登入資訊,所以導致使用者需要重新
Tomcat7叢集共享Session 基於redis進行統一管理(轉)
背景: 很多時候,生產環境,需要多個tomcat協作,那麼session的統一管理是一個首先需要解決的問題。session的統一管理有很多解決方案,比如儲存至資料庫、memcache、redis。那麼我想給大家介紹的是將session儲存至redis這個方案。
架構設計之Spring-Session分散式叢集會話管理
前言 通常在web開發中,會話管理是很重要的一部分,用於儲存與使用者相關的一些資料。對於JAVA開發者來說,專案中的session一般由Tomcat或者jetty容器來管理。 特點介紹 儘管使用特定的容器可以很好地實現會話管理,但是獨立容器掛掉或者由於其他原因重啟會導致使用者資訊丟失,並且無法支援分散式叢集會
基於 Redis 實現分散式應用限流
限流的目的是通過對併發訪問/請求進行限速或者一個時間視窗內的的請求進行限速來保護系統,一旦達到限制速率則可以拒絕服務 實際場景中常用的限流策略: Nginx接入層限流 按照一定的規則如帳號、
springBoot+redis 實現session共享理解,應用場景單點登入,分散式應用。
Springboot+redis 實現session共享也是利用了cookie在域名,路徑相同的情況下可以共享內容。第一次請求會將SESSION儲存在redis中,並將SESSIONID返回到瀏覽器的cookie中,第二次請求會攜帶上第一次請求的JSESSIONID。服務端拿
php 基於redis的分散式鎖應用
高併發的時候,對關鍵業務的資料保護,一般是用mysql加鎖,有表鎖行鎖共享排鎖一堆。。我選擇了redis分散式鎖,have a look at the code:composer require signe/redlock-php引入這個包之後,程式碼裡面可以這樣寫:priv
Cookie & Session【會話管理與控制】
有效期 unset font 重置 也有 姓名 本地 tro 讀取 用現實生活 類比Cookie 和 Session : 兩個關於開會的故事: 在幾十年前人們開會的時候,都需要帶上一個參會證。這個參會證上有這個人的職務、姓名、單位、照片等信息。在開會的時候,會議安保人員
為Tornado框架加上基於Redis或Memcached的session 【第三方】
clas secret () res sdh cti utf nec 安裝 Tornado 沒有session,只有cookie_secret,這在一些情況下是不利於開發的。所有我們可以給Tornado加上session的功能。 這篇文章講解的是依靠第三方包來實現。以後的文
會話管理cookie&session
getpath 什麽 響應 setvalue 每次 瀏覽器中 一段 臺電 atm 1.會話技術:從瀏覽器開始訪問服務器,到關閉瀏覽器,這期間發生了許多次請求和響應,這個過程就叫做一次會話。2.問題:如何在一次會話中保存會話相關的數據。3.Cookie:將會話相關的數據保存到
實現tomcat基於session會話保持
linux tomcat java session會話保持 nginx 實驗環境:兩臺服務器,分別實現java其中一臺作為nginx代理實驗原理圖:一、實現java環境1、實現java的運行環境 (1)安裝 JDK 可以在網上下載包 yum localinstall j
tomcat基於session會話保持以及msm會話保持
tomcat會話保持實驗一、基於tomcat集群會話保持一、實驗環境:一臺nginx服務器,兩臺tomcat集群,出於實驗的原因,我就把nginx也放在其中一臺的tomcat服務器上。也就是一共兩臺機器,都是centos7的環境。二、實驗原理:DeltaManager會話管理器是tomcat默認的集群會話管理
把php session 會話保存到redis
hand 計算機 close gpo pat div 優化 io讀寫 鎖定 php的session會話默認時以文件形式保存在php.ini配置文件設置的會話緩存目錄,文件保存會話的效率很低,每當每個用戶登錄一次就會在服務器上生成一個唯一的session_id文件,當用戶登錄
基於hi-nginx的web開發(python篇)——cookie和會話管理
class status domain 登陸 edi 模板引擎 log 怎麽辦 cache hi-nginx通過redis管理會話。 要開啟管理,需要做三件事。 第一件開啟userid: userid on;