1. 程式人生 > >Shiro效能優化:解決Session頻繁讀寫問題

Shiro效能優化:解決Session頻繁讀寫問題

[TOC] ## 背景 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 ```java @Repository public class ShiroSessionDAO extends AbstractSessionDAO { private static final String SESSION_REDIS_KEY_PREFIX = "session:"; @Autowired private Redis