1. 程式人生 > >nginx+tomcat+redis叢集實現負載均衡和session同步的步驟和問題處理方法

nginx+tomcat+redis叢集實現負載均衡和session同步的步驟和問題處理方法

最近在研究nginx+tomcat的負載均衡功能, 因為需要實現failover時使用者無感知的效果,所以我考慮使用tomcat的session同步方式來實現。網上能查到的東西我就直接貼連結了,我把搭建這套系統的過程,與遇到的坑的處理方式說明一下。

我使用的系統環境

Centos6.5 和 Centos7.2
Tomat7.0.29
JDK1.8.0_111-b14
Redis 3.2.6

一. 實現nginx+tomcat的負載均衡

用nginx+tomcat實現負載均衡的方法,只需要在nginx的配置檔案中配置相應的upstream設定需要負載均衡的伺服器列表就可以了。

稍微說一下可能會坑的地方

在http節點下,新增upstream節點。

upstream linuxidc {
server 10.0.6.108:7080;
server 10.0.0.85:8980; }

將server節點下的location節點中的proxy_pass配置為:http:// + upstream名稱,即“ http://linuxidc”.

location / {
root html;
index index.html index.htm;
proxy_pass }

注意一下upstream的名稱和在proxy_pass裡引用的方法

這一步做完以後,可以試驗一下在兩臺機器上部署同樣的專案,通過nginx的負載均衡的機制,重新整理頁面可以看到被指向到了不同的伺服器上。
測試failover的話,在nginx的upstream裡

upstream backend {
server 192.168.198.128:8080 weight=1;
server 192.168.198.128:8090 weight=4;
server 192.168.198.128:8091 backup;
 }

backup : marks the server as a backup server. It will be passed requests when the primary servers are unavailable.(標記為備用伺服器。當主伺服器不可用以後,請求會被傳給這些伺服器。)

當然了,比如說你就兩臺機器,那麼把這兩臺伺服器既配置成主伺服器,又配置成backup伺服器就可以互為failover了。

這個時候我遇到的一個問題:
我的專案第一個頁面是login.html 然後登入成功後會進入到index.html ,發現一直登入不進去。查了日誌才發現login.html進的是伺服器A, 結果到index.html的時候給我指到伺服器B去了,那時候又沒弄session同步,自然系統認為你沒登入,然後就login頁面無限迴圈了。

可以通過ip_hash保證同一個訪問的ip一直會指向到特定的某一個伺服器上,除非這個伺服器當了,才會切換到另外的伺服器。
配置的方式

    upstream myCluster {
          server 127.0.0.1:7085;
          server 192.168.1.234:6085;
          server 127.0.0.1:7085 backup;
          server 192.168.1.234:6085 backup;
          ip_hash;
          }

在最後加上ip_hash就可以

二. 實現session同步的方案的選擇

做完第一步以後,你可以實現nginx+tomcat的負載均衡了,並且可以failover,但是因為兩臺機器的tomcat並沒有共享session,所以雖然failover自動切換到正常執行的伺服器了,但是使用者仍然被踢回到了登入頁面,如果寫了一堆東西沒提交的話,肯定要掀桌子了。

那麼我們就要考慮一下session同步的方案的選取

因為我們用的是redis叢集,所以就把memcached方案pass掉了,有興趣的可以參考連結裡的博文部署一下。
一開始其實很想使用Terracotta的方案

Terracotta是一款由美國Terracotta公司開發的著名開源Java叢集平臺。它在JVM與Java應用之間實現了一個專門處理叢集功能的抽象層,以其特有的增量檢測、智慧定向傳送、分散式協作、伺服器映象、分片等技術,允許使用者在不改變現有系統程式碼的情況下實現單機Java應用向叢集化應用的無縫遷移。使得使用者可以專注於商業邏輯的開發,由Terracotta負責實現高效能、高可用性、高穩定性的企業級Java叢集。

看這個介紹覺得一股子高大上的感覺,使用的是3.7.7版本的Terracotta服務,但是隻支援到tomcat7並且不支援JAVA8,據說是3.7.10以後才支援JAVA8,但是一直找不到哪兒可以下載,有個4.3.3版本的服務但是配置方法和3.x的完全不一樣,結果折騰了半天也沒搞定,後來覺得還是回來使用redis的方案吧。

三. nginx+tomcat+redis(非叢集) 實現session同步的方案

方案就是使用redis-session-manager
GITHUB地址
照著github上說的配置一下就可以
1 . 找到相應的jar包copy到tomcat的lib下面
- commons-pool2-2.2.jar
- jedis-2.5.2.jar
- tomcat-redis-session-manager-XXX.jar

2 . 修改tomcat的conf裡的context.xml檔案
加上

<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
         host="localhost" <!-- optional: defaults to "localhost" -->
         port="6379" <!-- optional: defaults to "6379" -->
         database="0" <!-- optional: defaults to "0" -->
         maxInactiveInterval="60" <!-- optional: defaults to "60" (in seconds) -->
         sessionPersistPolicies="PERSIST_POLICY_1,PERSIST_POLICY_2,.." <!-- optional -->
         sentinelMaster="SentinelMasterName" <!-- optional -->
         sentinels="sentinel-host-1:port,sentinel-host-2:port,.." <!-- optional --> />

3 . 啟動tomcat盯一下log

可能會遇到很多問題,比如:
(1) xxx類沒找到, 那就是扔到lib下的jar包和你在context.xml配置的RedisSessionManager路徑跟jar包裡的實際路徑不一致。網上找到的包有很多二次開發版的,反編譯一下看看是不是這裡的問題
(2) 有可能會提示一個 commons-pool的問題,java.lang.NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool$Config
網上查到的解決方案是把1.6版本的 commons-pool也放到lib下就可以了
(3)作者的官網上說暫時不支援Tomcat8,所以如果想要用tomcat8執行的話,可以參考這篇文章
tomcat8+redis實現session同步
(4)如果你連線的redis是叢集的話,那麼會出現:
redis.clients.jedis.exceptions.JedisDataException: MOVED 13102 127.0.0.1
MOVED indicates that you’re using Redis Cluster. ShardedJedis is not for Redis Cluster, so you should use JedisCluster instead. Please note that JedisCluster doesn’t have pipeline mode, so you may want to send your operation one by one.
就是ShardedJedis 不能用於Redis叢集,要用JedisCluster 代替ShardedJedis 才可以,而且JedisCluster 還沒有pipeline 模式,所以一次只能執行一個操作

那麼分散式系統想連線redis叢集的話怎麼辦呢?

四. nginx+tomcat+redis叢集 實現session同步的方案

1 . 搭建redis-cluster
搭建redis-cluster的方法在網上可以搜到很多,在此就不多說(主要是懶)

其中遇到的問題:
(1) 在編譯redis的時候出現redis出現問題:
zmalloc.h:50:31: 錯誤:jemalloc/jemalloc.h:沒有那個檔案或目錄

解決方法
make MALLOC=libc

(2)在執行redis-cli或者redis-trib時候提示
version `GLIBC_2.14’ not found (required by ./redis-cli)

問題原因:
glibc的版本太低了,

2 . redis叢集+tomcat解決方案
主要參考下文的方法
redis叢集+tomcat解決方案

升級Jedis ,換個專用的redis-session-manager包就可以了

實際使用的時候還是會有很多問題
(1) tomcat報了個錯
org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException
據說是ecj版本的問題,升級到4.2.2以後解決,把新的包扔到tomcat的lib下面去

(2)按照文章裡的配置,呼叫本地的redis-cluster沒有問題,但是呼叫遠端伺服器的redis-cluster就提示
Exception in thread “main” redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException: Too many Cluster redirections?

這個就是redis伺服器就沒連上導致的問題,查詢得知

建立redis叢集的時候使用的命令是:./redis-trib.rb create –replicas 1 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006

所以重新構建叢集使用:./redis-trib.rb create –replicas 1 192.168.0.104:7001 192.168.0.104:7002 192.168.0.104:7003 192.168.0.104:7004 192.168.0.104:7005 192.168.0.104:7006

重構叢集的時候也會有問題:
(1) redis叢集報錯Node is not empty
參考這個文章

折騰到現在,nginx+tomcat+redis叢集的方案就算是可以運行了。把技術調研的過程記錄了一下,也是繞了很多彎路,希望可以幫助到有需要的人們~