redis session 超时时间_Shiro性能优化:解决Session频繁读写问题
點擊上方藍色字體,選擇“標星公眾號”
優質文章,第一時間送達
? 作者?|??張永恒
來源 |? urlify.cn/YjEZNj
背景
Shiro 提供了強大的 Session 管理功能,基于 Shiro 實現 Session 共享非常方便,只需要定制一個我們自己的SessionDAO,并將它綁定給 SessionManager 即可。在我們的 SessionDAO 中,通常會將 Session 保存到 Redis,那么 Shiro 對 Session 的增刪改查,都會直接操作 Redis。
但是由于 Shiro 對 Session 的訪問非常頻繁,用戶的一次請求,可能就會觸發幾十次的 Session 訪問操作,在 Session 共享的場景下,如果每次都訪問 Redis,勢必會影響性能。
應對思路
本地緩存 Session
將 Session 對象緩存于本地內存中,能夠有效減少從 Redis 中讀取 Session 的次數。
最簡單的方案,就是將 Session 對象保存到 request 域中,那么在一次請求內,只需要從 Redis 中獲取一次,之后就可以直接從當前 request 域中獲取,并且當請求結束后緩存會自動銷毀,不用擔心內存泄漏。
避免不必要的 Session 更新
ShiroFilter 對每個請求都會檢查 Session 是否存在,如果存在,則調用 SessionManager 的 touch() 方法,將 Session 的 lastAccessTime 屬性值更新為當前時間,并調用 SessionDAO 的 update() 方法保存更新。
由此可見,當 Session 被創建出來之后,用戶的每個請求都會使 SessionDAO 的 update() 方法至少被調用一次。
那么 Session 的 lastAccessTime 屬性是干嘛用的呢?有必要每個請求都去更新一下嗎?
lastAccessTime 屬性記錄的是用戶的上次訪問時間,它主要用于驗證 Session 是否超時,當用戶訪問系統時,如果本次訪問的時間距離上次訪問時間超過了 timeout 閾值,則判定 Session 超時。如果 lastAccessTime 的值不斷更新,那么 Session 就有可能永不超時。因此,更新 lastAccessTime 屬性值的操作可以認為是給 Session “續命”。
既然是“續命”,沒必要每次都“續”(除非命真的很短)。我們可以重寫 SessionManager 的 touch() 方法,在更新過 lastAccessTime 屬性的值后,先不急著保存更新,而是計算一下兩次訪問的時間間隔,只有當它大于某個閾值時,才去主動調用 SessionDAO 的 update() 方法來保存更新。這樣也就大大降低了 Session 更新的頻率。
代碼實現
ShiroSessionDAO.java
@Repositorypublic?class?ShiroSessionDAO?extends?AbstractSessionDAO?{
????private?static?final?String?SESSION_REDIS_KEY_PREFIX?=?"session:";
????@Autowired
????private?RedisTemplate?redisTemplate;
????@Override
????protected?Serializable?doCreate(Session?session)?{
????????Serializable?sessionId?=?generateSessionId(session);
????????assignSessionId(session,?sessionId);
????????redisTemplate.boundValueOps(SESSION_REDIS_KEY_PREFIX?+?session.getId().toString()).set(session);return?sessionId;
????}
????@Override
????public?void?update(Session?session)?throws?UnknownSessionException?{
????????redisTemplate.boundValueOps(SESSION_REDIS_KEY_PREFIX?+?session.getId().toString()).set(session);
????}
????@Override
????public?void?delete(Session?session)?{
????????redisTemplate.delete(SESSION_REDIS_KEY_PREFIX?+?session.getId().toString());
????????HttpServletRequest?request?=?getRequest();if?(request?!=?null)?{?//?一定要進行空值判斷,因為SessionValidationScheduler的線程也會調用這個方法,而在那個線程中是不存在Request對象的
????????????request.removeAttribute(session.getId().toString());
????????}
????}
????@Override
????protected?Session?doReadSession(Serializable?sessionId)?{
????????HttpServletRequest?request?=?getRequest();if?(request?!=?null)?{
????????????Session?sessionObj?=?(Session)?request.getAttribute(sessionId.toString());if?(sessionObj?!=?null)?{return?sessionObj;
????????????}
????????}
????????Session?session?=?(Session)?redisTemplate.boundValueOps(SESSION_REDIS_KEY_PREFIX?+?sessionId).get();if?(session?!=?null?&&?request?!=?null)?{
????????????request.setAttribute(sessionId.toString(),?session);
????????}return?session;
????}
????@Override
????public?Collection?getActiveSessions()?{
????????Set?keys?=?redisTemplate.keys(SESSION_REDIS_KEY_PREFIX?+?"*");if?(keys?!=?null?&&?!keys.isEmpty())?{
????????????List?sessions?=?redisTemplate.opsForValue().multiGet(keys);if?(sessions?!=?null)?{return?sessions.stream().map(o?->?(Session)?o).collect(Collectors.toList());
????????????}
????????}return?Collections.emptySet();
????}
????private?HttpServletRequest?getRequest()?{
????????ServletRequestAttributes?requestAttributes?=?(ServletRequestAttributes)?RequestContextHolder.getRequestAttributes();return?requestAttributes?!=?null???requestAttributes.getRequest()?:?null;
????}
}
ShiroConfig.java
@Configurationpublic?class?ShiroConfig?{
????@Bean
????public?SessionManager?sessionManager(SessionDAO?sessionDAO)?{
????????DefaultWebSessionManager?sessionManager?=?new?DefaultWebSessionManager()?{
????????????@Override?//?重寫touch()方法,降低Session更新的頻率
????????????public?void?touch(SessionKey?key)?throws?InvalidSessionException?{
????????????????Session?session?=?doGetSession(key);
????????????????if?(session?!=?null)?{
????????????????????long?oldTime?=?session.getLastAccessTime().getTime();
????????????????????session.touch();?//?更新訪問時間
????????????????????long?newTime?=?session.getLastAccessTime().getTime();
????????????????????if?(newTime?-?oldTime?>?300000)?{?//?如果兩次訪問的時間間隔大于5分鐘,主動持久化Session
????????????????????????onChange(session);
????????????????????}
????????????????}
????????????}
????????};
????????
????????sessionManager.setSessionDAO(sessionDAO);?//?綁定SessionDAO
????????SimpleCookie?sessionIdCookie?=?new?SimpleCookie("sessionId");
????????sessionIdCookie.setPath("/");
??????? sessionIdCookie.setMaxAge(8 * 60?* 60);?//?單位:秒數
????????sessionManager.setSessionIdCookie(sessionIdCookie);?//?綁定Cookie模版
????????
????????sessionManager.setSessionIdUrlRewritingEnabled(false);
????????sessionManager.setGlobalSessionTimeout(60?*?60?*?1000);
????????sessionManager.setSessionValidationSchedulerEnabled(true);
????????sessionManager.setSessionValidationInterval(2?*?60?*?60?*?1000);
????????sessionManager.setDeleteInvalidSessions(true);
????????
????????return?sessionManager;
????}
????...?略?...
}
@Configuration
public?class?ShiroConfig?{
????@Bean
????public?SessionManager?sessionManager(SessionDAO?sessionDAO)?{
????????DefaultWebSessionManager?sessionManager?=?new?DefaultWebSessionManager()?{
????????????@Override?//?重寫touch()方法,降低Session更新的頻率
????????????public?void?touch(SessionKey?key)?throws?InvalidSessionException?{
????????????????Session?session?=?doGetSession(key);
????????????????if?(session?!=?null)?{
????????????????????long?oldTime?=?session.getLastAccessTime().getTime();
????????????????????session.touch();?//?更新訪問時間
????????????????????long?newTime?=?session.getLastAccessTime().getTime();
????????????????????if?(newTime?-?oldTime?>?300000)?{?//?如果兩次訪問的時間間隔大于5分鐘,主動持久化Session
????????????????????????onChange(session);
????????????????????}
????????????????}
????????????}
????????};
????????
????????sessionManager.setSessionDAO(sessionDAO);?//?綁定SessionDAO
????????SimpleCookie?sessionIdCookie?=?new?SimpleCookie("sessionId");
????????sessionIdCookie.setPath("/");
??????? sessionIdCookie.setMaxAge(8 * 60?* 60);?//?單位:秒數
????????sessionManager.setSessionIdCookie(sessionIdCookie);?//?綁定Cookie模版
????????
????????sessionManager.setSessionIdUrlRewritingEnabled(false);
????????sessionManager.setGlobalSessionTimeout(60?*?60?*?1000);
????????sessionManager.setSessionValidationSchedulerEnabled(true);
????????sessionManager.setSessionValidationInterval(2?*?60?*?60?*?1000);
????????sessionManager.setDeleteInvalidSessions(true);
????????
????????return?sessionManager;
????}
????...?略?...
}
新款SpringBoot在線教育平臺開源了
精品帖子大匯總
一把“樂觀鎖”輕松搞定高并發下的冪等性問題(附視頻教程)
一文搞懂Java8 Lambda表達式(附視頻教程)
感謝點贊支持下哈?
總結
以上是生活随笔為你收集整理的redis session 超时时间_Shiro性能优化:解决Session频繁读写问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mxnet基础到提高(15)--梯度与反
- 下一篇: C指针原理(8)-C内嵌汇编