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

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 的配置文件。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章