利用Redis 實現多tomcat 同一使用者同一環境單一線上
在實際開發中,我們需要控制一個使用者在pc和app的同類型環境下只能登入一個,最多可以pc和app同時線上。
在單一的tomcat環境中,我們可以通過建立一個全域性的map,存放使用者名稱(id)+登入環境(APP或PC)和對應的session。按如下方法實現:
1、session裡儲存使用者名稱id和登入方式。
2、每次使用者登入的時候判斷是否有重複的登入,有的話就將原來的session失效,沒有就儲存到map裡。
3、監聽session,在session destroy事件處理裡負責根據session裡的值找到對應的map裡面的key-value鍵值對並將其刪除。
但在多tomcat環境下,我們可以通過redis 實現tomcat的session的共享:
配置很簡單,:
1、在context.xml裡配置(注意不要在server.xml裡配置)
<ValveclassName="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<ManagerclassName="com.orangefunction.tomcat.redissessions.RedisSessionManager"
host="localhost"
port="6379"
database="0"
maxInactiveInterval="60"/>
2、將下列jar放到tomcat的lib下面
tomcat-redis-session-manager-2.0.0
jredis-2.8
commons-pool2-2.3
3、注意不要在webapp對應的應用的lib裡包含tomcat-redis-session-manager-2.0.0 包,否則會報錯。當時同事用的maven,打包的時候把omcat-redis-session-manager-2.0.0 包一併打進去,出現錯誤,找了半天才找到。
原來想法很簡單,只有把對應的全域性的map儲存到redis裡就可以了,但發現在應用層HttpSession物件根本不支援序列化,無法儲存到redis裡。
同時發現tomcat的session在redis裡是把sessionId作為key來儲存的,但同樣對應的value,取出後該物件不是HttpSession,沒法按照HttpSession的方法將session失效(呼叫session.invalidate()方法)
解決辦法如下:
1、不使用全域性的map,直接將存放使用者名稱(id)+登入環境(APP或PC)作為鍵值,對應的sessionid作為value儲存到redis裡。(注意這裡不儲存session)
2、每次使用者登入的時候判斷是否有重複的登入,有的話就將原來的session失效,失效的方法是直接將sessionid鍵值從redis裡刪除。如果沒有就按照1的方式儲存到redis裡。
3、監聽session,在session destroy事件處理裡負責根據session裡的值找到對應的redis裡面的key-value鍵值對並將其刪除。
用上面的方式實現了功能,但同時發現還遺漏了一個問題,就是session是有過期的,他在redis裡tomcat設定了對應的有效時間,而我們的使用者登入資訊的生命週期應該和其對應session同步。因此我們需要進行如下改造:
1、儲存資訊到redis裡的時候同時設定有效時間。
2、增加一個filter,對所有資訊進行攔截,filter裡對該session對應的儲存在redis裡的登入資訊設定有效時間。
至此,所有的問題都解決。