磕磕碰碰一天,百度了好幾篇,終於搞定了Session共用的問題。以前只是聽說將Session存入第三方來解決Session共用問題,可一直沒有親自動手實現過,還記得以前面試時被一道怎麼用Redis解決Session共用問題給懟得老慘,現在再問到,雖然可能還是會被懟,但多多少少能說上幾句了。此文純粹是記錄我這一天解決這問題的過程,要想面試打敗面試官,多去找找其他博文。
問題起源:伺服器端Tomcat叢集,負載策略採用的Ip-Hash方式,不存在Session共用問題,後因Jmeter壓測,在無權更改測試機為多IP模擬IP欺騙的情況下,將負載策略更改為輪詢方式,然後,就有活幹了。
負載策略可參考:https://blog.csdn.net/qq_35119422/article/details/81505732
需要用到Redis Session Manager for Apache Tomcat專案,https://github.com/jcoleman/tomcat-redis-session-manager ,但是官方說的了,暫時不支援tomcat8,上Github上逛了一圈,無私奉獻的大神挺多的,因為我專案用到的是tomcat8.5,找了好幾個才找到個簡單可用的(沒有試是否支援其他版本)。
原始碼我用的這哥們的:https://github.com/cc-chen/tomcat8.5-redis-session-manager.git,萬分感謝!
省事的朋友們,可跳過原始碼,所需jar包我打好了,需要的自行下載:
下載地址:https://pan.baidu.com/s/1DuY-S9GHCgvWmTn1DvvvOg
提取碼:cg6g
解壓後為這三個jar包,
jedis-2.5.2.jar,
commons-pool2-2.2.jar
tomcat8.5-redis-session-manager.jar
將這三個包放在tomcat的lib目錄下,然後修改config目錄下context.xml檔案,新增如下設定(大多數博主通用設定):
<Valve className="com.s.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.s.tomcat.redissessions.RedisSessionManager"
host="127.0.0.1"
port="6379"
database="0"
password="123456"
maxInactiveInterval="60" />
引數說明:
通用設定
className:tomcat8.5-redis-session-manager.jar中類全路徑,有木有發現,與網上其他大多數部落格寫的名稱不一樣,是因為該jar包有修改原官網專案包名。
host:redis master Ip地址
port:redis master 埠
password:redis密碼
database:資料庫下標(redis預設從0到15 16個分割區)
maxInactiveInterval:session超時時間,單位min(測試無效,該怎麼設定和失效原因後續會說明)
預設Redis主從叢集已經設定好,測試結果如下,(由於是本地,所以Tomcat,Redis均為偽叢集,也就是IP一樣埠不一樣)
在各個tomcat/webapp/ROOT 目錄下的jsp頁面合適位置加上IP:Port-Session Id : <%= request.getSession().getId() %>(我選擇的第51行),啟動tomcat8086,8087,tomcat啟動前保證Redis主從叢集執行正常,否則tomcat啟動會報錯,啟動成功後,存取tomcat主頁,在同一瀏覽器輸入:127.0.0.1:8086,127.0.0.1:8087,檢視頁面的sessionId以及reids中sessionId,如果如圖所示,恭喜你,成功一半了:
如果redis中存上了sessionId,且兩個tocat服務sessionId一樣,重複重新整理,sessionId不變,驗證成功。
然後刪除Redis中的sessionId,再次重新整理。
此時sessionId變了,但是兩個服務的sessionId仍然一樣,驗證成功。
大多數部落格到這就完了,但真的成功了麼?NO!
上面設定是大多數部落格都寫到的設定,不知道大家發現個問題沒,上面host設定只有一個地址,既主節點地址。當redis為單機時,或者叢集模式為主從模式時,這樣設定才好使,因為我們只需設定主節點連線資訊就可以了,但主節點宕了,不就崩了麼?現在大多數用到redis的服務,為保證其高可用,幾乎都會選擇哨兵模式或者Redis-Cluster模式吧。那麼問題來了,當主節點宕了後,當其他從節點升級為主節點後,節點連線資訊變了,那上面的設定是不是得改了?
不用驚訝,因為當初找到的幾篇部落格都大同小異,沒有提到這種場景,官網看了下,奈何全是英文,就自個研究了下原始碼,這就是傳說中的自己坑自己吧!後來也有看到別的博文介紹哨兵叢集時tomcat conf.xml的設定,去官網上也看到了相關的設定說明。不過我弄的時候沒看到啊,暈!塞翁失馬焉知非福,雖然也不知道研究了下原始碼得到了啥收穫。。。。
要了解多個的設定,首先看看原始碼中怎麼讀取單個host,port等設定引數的,找找看:
719行,新建JedislPool用到了咱們組態檔裡的引數,看看else另一個條件,那個sentinel單詞不就是哨兵麼,看看裡面相關的sentinelMaster,sentinelSet的get、set方法,
sentinelMaster需要傳個master,sentinelSet需要傳個sentinels,顧名思義,master是主節點名稱,sentinels是各個節點的連線資訊,抱著試一試的態度,在context.xml組態檔中又加上倆引數。
同樣:這裡預設Redis叢集模式已經更改為哨兵模式且正常執行(Redis哨兵叢集這裡就不贅述了)。
sentinelMaster="mymaster"
sentinels="127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381"
新增引數說明:
sentinelMaster:master名稱
sentinels:哨兵叢集時地址設定,IP:port,用英文逗號隔開
然後重新啟動兩個tomcat,接下來繼續驗證:驗證方式和上面一樣,當上面兩個步驟驗證完後,將當前主節點手工關掉,然後再次重新整理瀏覽器驗證,發現sessionId依然有效,至此,已經成功百分之九十了。
剩下的百分之十,就是session過期的問題了。在啟動tomcat時,會有個警告
說明那個maxInactiveInterval引數設定有點問題。出問題了,那就原始碼裡看看啥情況,找到原始碼中設定session超時時間的地方,在RedisSessionManager類中,377和545行還有632行,這個時間值都用到的getMaxInactiveInterval()方法,如下
再看看getMaxInactiveInterval()方法
看到這兒似乎明白了,本專案中並不是根據我們的組態檔中引數賦值,這個時間設定的值取的是Tomcat容器的session-timeout 節點(單位min),更改tomcat conf/web.xml設定,約595行
<session-config>
<session-timeout>10</session-timeout>
</session-config>
去掉組態檔中的maxInactiveInterval引數,更改web.xml 中session-timeout時間為2分鐘,重新啟動Tomcat測試session失效場景,驗證成功,至此Tomcat8.5整合Redis 解決Session對談共用問題算是大功告成!
謹記,官方檔案很重要!很重要!很重要!仔細看看會少走很多彎路!
以上內容均為個人對別人成果的學習和總結,有理解不到位的地方,還請各路大神指點!
原創不易,看了覺得有幫助的朋友們,記得點個贊喲,謝謝!