搭建Tomcat集羣&經過Redis緩存共享session的一種流行方案

爲何要共享session?

咱們使用單臺Tomcat的時候不會有共享sesssion的疑慮,只要使用Tomcat的默認配置便可,session便可存儲在Tomcat上。html

可是隨着業務的擴大,增長Tomcat節點構成Tomcat集羣大勢所趨,分佈式帶來了增長更大規模併發請求的優點,可是也隨之到來了一個問題,每一個Tomcat只存儲來訪問本身的請求產生的session,若是Tomcat-A已經爲客戶端C建立了會話session,那麼Tomcat-B並不知道客戶端已與集羣中的Tomcat-A產生了會話,在訪問時就會爲C再建立一份session,若是是基於session的驗證會話權限的接口(如用戶登陸認證後纔可訪問的數據接口),將會致使在訪問集羣中不一樣節點的時候重複認證。session的不共享致使原來的會話管理機制在Tomcat集羣中沒法工做。前端

因此,若是有一個Tomcat集羣都能訪問的公共session存取區就行了,基於這個概念,咱們想到了使用Redis來作這個session公共存取區,這樣子的話就有一個統一管理回話的地方了。回看咱們上文提到的例子,若是Tomcat-A已經爲客戶端C建立了會話session,這個session信息會直接存儲在公共的Redis裏面,那麼Tomcat-B就能夠到公共session存儲區裏得到已爲C產生的session,這樣的結果是集羣的公共session存取區在邏輯上就像一個tomcat的內部session存取區同樣了。nginx

怎麼作呢?

有了上述基本的概念,咱們就要開始真正施行了。git

1. 持久化Tomcat Session到Redis中

Tomcat提供了一個開放的session管理和持久化的org.apache.catalina.session.ManagerBase,繼承這個抽象類並作一些簡單的配置,便可讓你的session管理類接管Tomcat的session讀取和持久化。固然,咱們在這裏使用了一個流行的開源項目:
https://github.com/jcoleman/tomcat-redis-session-manager
,它已經爲咱們準備好了這樣的一個管理類,只要將這個管理類配置在Tomcat中便可發揮功能。它能夠幫助咱們將tomcat的session存入咱們指定的redis,甚至支持redis在sentinel模式調度的redis集羣,稍後咱們也將詳述這樣的redis集羣該如何部署。github

使用這個項目很是簡單,若是在Tomcat6或Tomcat7下部署,直接使用項目release出的jar文件到Tomcat的lib下便可,準確來講還須要引入它的其餘幾個依賴(括號中爲建議的):redis

tomcat-redis-session-manager-VERSION.jar(v1.2)
commons-pool2-VERSION.jar(v2.2)
jedis-VERSION.jar(v2.5.2)

引入後須要在tomcat下修改conf/context.xmlapache

<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
         host="{redis.host}"
         port="{redis.port}"
         database="{redis.dbnum}"
         maxInactiveInterval="60"/>

這樣一來咱們的tomcat便可把session的管理交由咱們配置的redis來處理。api

須要注意的是,若是在Tomcat8下按照上述部署,會在啓動時報錯,筆者查看過緣由,是tomcat-redis-session-manager最後更新的年代相隔較久,代碼中使用的Tomcat api出現了過期刪去的狀況,在Tomcat8下會出現問題,若是想在Tomcat8下使用,須要自行對過期的api進行修改,替換成新的Tomcat api。筆者本身修改了未經嚴格驗證的一個版本,可供使用Tomcat8的讀者試用:
https://github.com/jinhaoplus/tomcat-redis-session-manager瀏覽器

2. nginx反向代理的負載均衡

雖然這不是本文的重點,可是使用負載均衡在搭建集羣的過程當中重要性不言而喻,使用nginx默認的輪詢機制,咱們能夠將前端的瀏覽器請求轉發到不一樣的Tomcat實例上。
首先來說講正向代理和反向代理,一言以蔽之:正向代理幫助內網client訪問外網server用,反向代理未來自外網client的請求f轉發到到內網server。
最實際的區別是使用兩者時正向代理須要用戶主動配置,而反向代理對用戶透明,不須要用戶作主動配置。
「代理」是指代人理事,即代理服務器是爲其餘人或機器服務的。
正向代理是替內網中的用戶訪問外網服務的(即代替用戶去訪問外網),用戶和外網之間的溝通所有交由正向代理服務器完成,用戶的請求不發給外網服務器而發給代理服務器讓其代爲處理,這個過程是隱藏用戶的。
反向代理是爲真正的服務節點機器服務的(即代替真正的服務節點機器去提供服務),代理服務器接收來自外界的請求,並將請求轉給真正的服務節點機器,用戶不與真正的機器打交道
,這個過程是隱藏真正的服務實例機器的。tomcat

nginx能夠做爲高效的反向代理服務器,同時起到了負載均衡的做用。若是想要使用反向代理Tomcat集羣的負載,方法也很是簡單,只須要在其配置nginx.conf中將負載的Tomcat集羣的實際地址加入upstream,並將locate導向配好的upstream便可:

http{
    ...
    upstream tomcats {
        server      <tomcat1_ip>:<tomcat1_port>;
        server      <tomcat2_ip>:<tomcat2_port>;
        ...
        server      <tomcatn_ip>:<tomcatn_port>;
      }
    ...
    server {
        listen       80;
        ...
        location / {
            root   html;
            index  index.html index.htm;
            proxy_pass      http://tomcats;
        }
      }
}

默認的輪詢機制將每次請求都發至不一樣的Tomcat實例上,以此實現負載均衡。

3. 基於sentinel的redis集羣搭建

上文介紹的方法其實已經能夠搭建一個完整的Tomcat集羣了,若是系統想要一個更安全可靠的環境,那麼nginx其實也能夠作集羣,這裏略去不說,咱們想要說的是redis集羣。

上面咱們已經說到redis是session的公共存儲區,若是redis不幸掛掉的話將會致使致命的問題,由於無session源可取,Tomcat中有session讀取的接口會直接報錯。因此搭建一個redis集羣仍是頗有必要的,幸虧redis對分佈式HA的搭建支持得很好,原生即有一套sentinel哨兵機制便可用。

以sentinel模式啓動的redis實例起到了監控者的做用,redis集羣以master-slave的模式啓動,消息再也不直接發給redis實例,而是發給sentinel,由sentinel同步至全部的redis實例,若是出現redismaster實例掛掉的狀況,會由sentinel發現,根據配置還能夠由sentinel本身組成的集羣去選舉產生新的master,新的master將會承擔起做用,起到了災難自動回恢復的做用。

這裏來講一下sentinel集羣的配置:
咱們使用兩個redis實例來組成master-slave,須要三個sentinel組成哨兵集羣來監控兩個redis實例,在master出現問題的時候選舉產生新的master。
路徑假設以下:
redis1
redis2
sentinel1
sentinel2
sentinel3

配置redis1/redis.conf爲master:

bind 127.0.0.1
    port 6379

配置redis2/redis.conf爲redis1的slave:

bind 127.0.0.1
    port 6379
    slaveof <redis1_ip> 6379

配置sentinel1/redis-sentinel.conf

port 26379    
    sentinel monitor mymaster <redis1_ip> 6379 2

指定redis1爲master,slave信息會在啓動後被sentinel監聽到並自動寫入到配置文件中,所以不須要手動寫入,最後的quorum表示當有2個sentinel判斷master掛掉的時候便可選舉slave爲新的master。

sentinel2sentinel3配置相同。

這樣以後啓動redis和sentinel集羣,便可構建一個高可用的redis集羣。能夠嘗試一下把redis1這個master掛掉,sentinel就會探查到而且在2個sentinel都探查到的時候即會選舉產生新的master:

# +monitor master mymaster <redis1-ip> 6379 quorum 2
# +sdown master mymaster <redis1-ip> 6379

同時咱們的Tomcat配置也將改成使用sentinel集羣的版本:

<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
    <Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
         sentinelMaster="mymaster"
         sentinels="<sentinel1-ip>:26379,<sentinel2-ip>:26379,<sentinel3-ip>:26379"
         maxInactiveInterval="60"/>

這個集羣搭建完成後,咱們的系統將會更爲穩定:

相關文章
相關標籤/搜索