注意這幾點,輕輕鬆鬆配置 Nginx + Tomcat 的叢集和負載均衡

wskwbog發表於2019-05-28

Tomcat 叢集是當單臺伺服器達到效能瓶頸,通過橫向擴充套件的方式提高整體系統效能的有效手段。Nginx 是一個高效能的 HTTP 和反向代理 web 伺服器,可以通過簡單的配置實現 Tomcat 叢集的負載均衡。

本文使用的 Tomcat 是 8.5.35 版本,Nginx 是 1.14.2 版本。接下來看下配置的過程以及可能會遇到的問題,首發於微信公眾號「頓悟原始碼」。

1. 概述

對於 Web 應用來說,叢集最大的問題就是 Session 資訊的共享,一般有以下解決方法:

  • 使用粘性會話,比如,使用 IP Hash 的負載均衡策略,將當前使用者的請求都集中到一臺伺服器上;缺點是單點故障,會話丟失
  • 使用 Session 複製,使用 Tomcat 自帶的 Session 複製策略,將會話資訊同步到叢集的各個節點;缺點是消耗更多記憶體和頻寬,適用於小型叢集
  • 使用第三方快取中介軟體快取整個叢集會話資訊,比如 Redis 快取,可由應用程式控制與 Session 的關聯,也可以適配 Tomcat
  • 當然了,也可以把會話資訊存到共享檔案系統或者資料庫

在配置 Nginx 的過程中,可能會遇到以下問題:

  • 配置 upstream 名稱時不能使用下劃線,比如 tomcat_ha,否則 Tomcat 會丟擲 The character [_] is never valid in a domain name 的異常
  • 在 windows 上殺掉所有的 nginx.exe 程式,taskkill /fi "imagename eq nginx.exe" /f
  • 在 windows 上有個 pid 為 4 的系統程式會佔用 80 埠,所以這裡將 nginx 改為了 8000

在配置 Tomcat 叢集的過程中,需要注意的問題:

  • 確保 web.xml 配置了 <distributable/> 元素
  • 確保 Context 的 Manager 別被替換成了標準會話管理器
  • Receiver.address 不要配置成 auto,因為預設可能會繫結 127.0.0.1;Receiver.port 可改也可不改,Tomcat 會自行檢測 4000-4100 範圍內的可用埠,自動處理衝突
  • 如果在不同伺服器上,需要關閉防火牆或開埠,還有時間同步

2. Nginx 核心配置

Nginx 使用的是預設配置,新增和修改的核心配置如下:

http {
  ...
  #gzip  on;
  
  #設定負載均衡的伺服器列表和權重
  upstream tomcat-ha {
      #ip_hash; 
      server 172.31.1.41:8080 weight=1;
      server 172.31.1.42:8080 weight=1;
  }
  
  server {
      listen       8000;
      server_name  localhost;

      #charset koi8-r;
      #access_log  logs/host.access.log  main;

      location / {
          root   html;
          index  index.html index.htm;
          #轉發請求
          proxy_pass http://tomcat-ha;
          proxy_set_header Host $http_host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
      }
      ...
  }
}

3. Tomcat 叢集配置

啟用叢集配置,在 <Engine> 元素中新增以下配置:

<!-- channelSendOptions=6 同步複製 -->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="6">
  <!-- 叢集 Session 管理器 -->
  <Manager className="org.apache.catalina.ha.session.BackupManager"
             expireSessionsOnShutdown="false"
             notifyListenersOnReplication="true"
             mapSendOptions="6"/>
  <!--
  <Manager className="org.apache.catalina.ha.session.DeltaManager"
           expireSessionsOnShutdown="false"
           notifyListenersOnReplication="true"/>
  -->
  <!-- 叢集內部通訊配置 -->
  <Channel className="org.apache.catalina.tribes.group.GroupChannel">
    <Membership className="org.apache.catalina.tribes.membership.McastService"
                address="228.0.0.4"
                port="45564"
                frequency="500"
                dropTime="3000"/>
    <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
              address="192.168.10.2"
              port="5000"
              selectorTimeout="100"
              maxThreads="6"/>
    <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
      <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
    </Sender>
    <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
    <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
    <Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>
  </Channel>
  <!-- 此 vavle 攔截請求,並將 Session 資訊發給內部節點 -->
  <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
         filter=".*\.gif|.*\.js|.*\.jpeg|.*\.jpg|.*\.png|.*\.htm|.*\.html|.*\.css|.*\.txt"/>
  <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>

簡單描述下工作原理:

  1. nginx 將請求轉發給 Tomcat1,請求登入認證,建立會話,生成 Cookie,在響應返回之前,將 Session 資訊複製到 Tomcat2
  2. 再次請求時,nginx 將帶著會話 Cookie 的請求轉發給了 Tomcat2,Tomcat2 發現內部 Session 池中有關聯的已認證成功的 Session 物件,不再認證返回請求資源

4. 驗證負載均衡和 Session 複製

4.1 測試環境

  1. 使用兩臺 PC 部署 Tomcat,對應關係是:172.31.1.41-Tomcat1,172.31.1.42-Tomcat2
  2. 部署基於使用 Tomcat 自帶的 SessionExample 程式,編寫了一個 tomcat-benchmark 的 web 應用
  3. 結合 Tomcat 自帶的 Manager 應用,檢視已部署應用內部 Session 池

4.2 負載均衡

修改 tomcat-benchmark 部署描述符檔案中的 context-param 為 "I'm Tomcat 1/2" 用於區分兩個 Tomcat,啟動 Nginx 和 Tomcat,在瀏覽器訪問 172.31.1.42:8080 可以看到請求在兩個伺服器間切換:

req-balance

4.3 Session 複製

為了方便理解,這裡先把 Nginx 的負載均衡策略設定成 ip_hash:

  1. 假設 Nginx 始終將請求定位到 Tomcat1 上,然後在 Tomcat1 上建立會話,往會話中新增一些屬性
  2. 關閉 Tomcat1 模擬故障,此時 Nginx 會帶著之前的會話 Cookie 將請求轉發到 Tomcat2,上
  3. 檢視 Tomcat2 上是否存在與 Cookie(JSESSIONID) 關聯的 Session 資訊,若有表示複製成功

整個過程如下:

session-copy

動圖正好與上述描述的相反,可以看到 Session 資訊從 Tomcat2 複製到了 Tomcat1 中。

5. 小結

搜尋微訊號「頓悟原始碼」,回覆「Tomcat」後,可獲取本文測試使用的工程以及 Nginx 和 Tomcat 的配置檔案。

相關文章