步驟:
- 安裝Apache基本模塊
- 後臺監控
- 負載均衡簡單測試
- 配置Tomcat相關模塊(AJP)
- 保持Session惟一,粘性會話
- Tomcat集羣,Session複製
1.安裝Apache相關模塊
負載均衡須要的主要是代理模塊!
通過幾回Apache配置嘗試,在Ubuntu下配置Apache實在是太容易了。加載什麼模塊、取消什麼模塊兩個命令搞定。
- #啓用模塊
- sudo a2enmod <model>
- #禁用模塊
- sudo a2dismod <model>
這裏,咱們須要讓Apache提供代理服務,其中又包含基於http、ftp、ajp等等協議的代理功能,同時還須要負載均衡模塊。咱們能夠經過命令逐個加載:
- #代理核心模塊
- sudo a2enmod proxy
- #代理AJP模塊
- sudo a2enmod proxy_ajp
- #代理負載均衡模塊
- sudo a2enmod proxy_balancer
- #代理HTTP模塊
- sudo a2enmod proxy_http
- #代理FTP模塊
- sudo a2enmod proxy_ftp
完成上述操做後,系統會提示重啓Apache!
先不着急重啓,現學現賣,瞭解下Apache的目錄結構。在Ubuntu下配置Apache主要是在
/etc/apache2目錄下:
分述:
- apache2.conf核心配置文件,通常不須要修改!
- conf.d目錄,裏面包含了一些字符集設置,文檔等設置!
- dav_svn.authz和dav_svn.passwd是前面作SVN時,相關權限、密碼文件。
- envvars定義了運行時的用戶身份——www-data。
- httpd.conf是Apache留給咱們本身折騰的配置文件,默認爲空。apache2.conf會加載這個文件。
- ports.conf端口默認配置。apache2.conf會加載這個文件。
- magic爲mod_mime_magic模塊服務。
- mods-enabled和mods-available mods-enabled會被apache2.conf加載,裏面包含*.load和*.conf文件。*.load文件中是加載相應的模塊(位於/usr/lib/apache2/modules/中),而*.conf中是對應的基本配置。但這些文件其實都是連接到mods-available中相應的文件上。當咱們經過a2enmod操做時,實際上正是操做了這些軟連接。
- sites-available和sites-enabled 與 mods-enabled和mods-available的關係相似,只是其中包含的是站點內容。
羅嗦了一堆,下面配置負載均衡部分。
執行修改:
- sudo vi /etc/apache2/mods-available/proxy.conf
上圖紅框中的內容是原始內容,白框中的內容是我新加的部分。
注意紅框中的配置:
- <Proxy *>
- AddDefaultCharset off
- Order deny,allow
- #Deny from all
- Allow from localhost ip6-localhost
- </Proxy>
在默認配置中,Deny from all處於可用狀態。當咱們配置其餘代理節點時,將致使杜絕訪問!使用
Allow from localhost ip6-localhost 限制僅容許本機訪問!
再說,白框中的內容:
- <Proxy balancer://zlex>
- BalancerMember http://localhost:8080/
- BalancerMember http://192.168.49.1:8080/
- </Proxy>
這裏,我配置了一個負載均衡節點
balancer://zlex,其中包含了兩個服務
http://localhost:8080/和
http://192.168.49.1:8080/,一個是虛擬機上的Tomcat、一個是真機上的Tomcat。這裏使用的Http的轉發方式,固然,使用AJP何嘗不可,稍後詳述!
這裏的節點次序會有一個前後關係,Apache會將請求按照FIFO的方式調度順次分配到各個節點上!若是其中有一個節點掛掉,將跳過該節點順次尋找可用節點。
再說代理和反向代理:
- ProxyPass /zlex balancer://zlex
- ProxyPassReverse /zlex balancer://zlex
這裏配置,若是要訪問
/zlex路徑時,將跳轉到
balancer://zlex上,也就是享受負載均衡!
2.後臺監控
咱們如何知道某個節點負載多少,響應時間多久,服務是否正常呢?Apache提供了負載均衡監控平臺:
http://localhost/balancer-manager。可是,這個服務默認是不存在。
除了用於負載均衡配置、監控的
balancer-manager還有
http://localhost/server-status和
http://localhost/server-info
咱們須要添加info模塊:
- #系統信息模塊
- sudo a2enmod info
同時,爲了可以使用
balancer-manager,咱們須要配置:
- <Location /balancer-manager>
- SetHandler balancer-manager
- Order Deny,Allow
- #Deny from all
- Allow from localhost ip6-localhost
- </Location>
把這段代碼放到哪?因爲它同屬系統信息配置,我把它放到了
info.conf中,說白了就是照貓畫虎:
- sudo vi /etc/apache2/mods-available/info.conf
注意,這段代碼放到了
<IfModule mod_info.c>和
</IfModule>之間!
如今,咱們重啓Apache:
- sudo /etc/init.d/apache2 restart
來看看管理界面
http://localhost/balancer-manager:
咱們再來看看服務器基本信息
http://localhost/server-info:
上述兩個服務須要加載info模塊,而服務器狀態(server-status)不須要
http://localhost/server-status:
3.負載均衡簡單測試
瘋狂訪問
http://localhost/zlex,直到手痠眼煩!
我這裏故意使用不一樣了Tomcat界面,來驗證本身的配置是否生效。更瘋狂的是,我甚至把節點指向了百度、搜狐,來測試負載均衡的效果。若是你細緻觀察,Apache是將請求順次分配到各個節點上的。
若是其中一個節點發生問題(例如,強行關閉一個Tomcat,或配置一個錯誤節點)Apache將會通過幾回嘗試後,繞過這個問題節點,尋找能夠成功訪問的節點。若是這個節點恢復正常使用,Apache將在該Tomcat恢復正常工做後大約1分鐘內將該節點標識爲可用!
如今,再看看如今的後臺(
http://localhost/balancer-manager)啥樣子:
若是咱們控制一個節點的狀態是否可用,該怎麼作:
涉及到負載量,session同步等等,咱們最後討論!
4.配置Tomcat相關模塊(AJP)
基於Http協議分發並不複雜,但AJP效果更好!一次詭異事件中,內網訪問正常,外網訪問屢次失敗,最後經過AJP方式完美解決了!
在Tomcat中配置AJP也很簡單,修改
server.xml開啓AJP模塊:
- sudo vi /etc/tomcat6/server.xml
開啓AJP配置:
- <Connector port="8009" protocol="AJP/1.3"
- URIEncoding="UTF-8"
- redirectPort="8443" />
注意使用的端口port="8009",字符集URIEncoding="UTF-8",這是輸入框、請求字符集亂碼的入口!
接下里就能夠經過AJP方式進行節點分發了。修改
/etc/apache2/mods-available/proxy.conf :
- sudo vi /etc/apache2/mods-available/proxy.conf
將
http改成
ajp,將
8080改成
8009:
- <Proxy balancer://zlex>
- BalancerMember ajp://localhost:8009/
- BalancerMember ajp://192.168.49.1:8009/
- </Proxy>
重啓Apache:
- sudo /etc/init.d/apache2 restart
再看看管理界面
http://localhost/balancer-manager
至此,咱們完成了基本負載均衡的基本配置!
/etc/apache2/mods-available/proxy.conf還有一些屬性:
noFailOver是否打開失敗轉移,
On|
Off,默認爲Off,添加在
ProxyPass後面,如:
- ProxyPass /zlex balancer://zlex stickySession=JSESSIONID noFailOver=On
若是這樣配置,當提供給你服務的服務器發生異常,那麼你將一直看着它返回給你503,直到系統恢復正常!
loadfactor表示後臺服務器負載到由Apache發送請求的權值,默認值爲1添加在
BalancerMember後面:
- <Proxy balancer://zlex>
- BalancerMember ajp://localhost:8009/
- BalancerMember ajp://192.168.49.1:8009/
- </Proxy>
能夠實現三種策略:
- 輪詢均衡策略的配置
- 按權重分配均衡策略的配置
- 權重請求響應負載均衡策略的配置
5.Session惟一,粘性會話
Apache已經能夠輕鬆將內容處理的工做分配給各個Tomcat了!
固然,這還不夠,Session仍是個問題!
WHY?
咱們來作一系列修改,來檢測Session到底出現了什麼問題!
先來改造Tomcat,修改
server.xml:
- sudo vi /etc/tomcat6/server.xml
修改
<Engine />節點,增長
jvmRoute屬性:
- <Engine
- name="Catalina"
- defaultHost="localhost"
- jvmRoute="tomcat1">
另外一個Tomcat設置改成
- <Engine
- name="Catalina"
- defaultHost="localhost"
- jvmRoute="tomcat2">
經過
jvmRoute,指定了Tomcat惟一標識!
而後修改
/etc/apache2/mods-available/proxy.conf
- sudo vi /etc/apache2/mods-available/proxy.conf
以下:
- <Proxy balancer://zlex>
- BalancerMember ajp://localhost:8009/zlex route=tomcat1
- BalancerMember ajp://192.168.49.1:8009/zlex route=tomcat2
- </Proxy>
-
- ProxyPass /zlex balancer://zlex
- ProxyPassReverse /zlex balancer://zlex
這裏須要經過修改
route屬性,將Apache與Tomcat關聯起來!
注意,Tomcat中定義的jvmRoute須要與Apache定義的route相對應!
咱們來看一下
http://localhost/balancer-manager發生了什麼變化:
咱們注意到route字段有了新的標識,固然,咱們也能夠經過這個配置界面修改這些信息,但當前修改不會真的修改/etc/apache2/mods-available/proxy.conf文件,Apache重啓後將丟失。
爲了更細緻的對比進過複雜均衡的結果,這裏增長了zlex應用!主要是監控Session的變化!
只看核心代碼:
- <b>當前SessionID:</b>
- <br />
- <%
- String sessionID = session.getId();
- out.println(sessionID);
- System.err.println("sessionID = " + sessionID);
-
- // 若是有新的 Session 屬性設置
- String dataName = request.getParameter("dataName");
- if (dataName != null && !dataName.isEmpty()) {
- String dataValue = request.getParameter("dataValue");
- session.setAttribute(dataName, dataValue);
- }
- %>
- <br />
- <br />
- <b>Session屬性列表:</b>
-
- <br />
- <%
- Enumeration<String> e = (Enumeration<String>) session
- .getAttributeNames();
- while (e.hasMoreElements()) {
- String name = e.nextElement();
- String value = (String) session.getAttribute(name);
- out.println(name + " = " + value + "<br>");
- System.err.println(name + " = " + value);
- }
- %>
- <form method="POST">
- <ul style="list-style-type: none;">
- <li><label for="dataName">鍵:</label><input size="20" id="dataName"
- name="dataName"></li>
- <li><label for="dataValue">值:</label><input size="20"
- id="dataValue" name="dataValue"></li>
- <li><input type="submit" value="提交" /></li>
- </ul>
- </form>
將其作成一個名爲zlex的web應用,分別部署到兩個Tomcat上!
而後重啓Apache:
- sudo /etc/init.d/apache2 restart
不斷刷新
http://localhost/zlex,看看真正的結果:
第1次:
第2次:
第3次:
第4次:
仔細觀察,每次請求都按照負載均衡配置的節點次序依次請求到不一樣的Tomcat上。尤爲是當咱們經過
jvmRoute和
route作了綁定以後,信息更加準確。可是,
仔細觀察,每次請求的SessionID都是不同!對於純Web應用,尤爲是依靠SessionID區分惟一用戶的應用,這將是一場噩夢——解決了服務器壓力均衡問題,卻帶來了SessionID不惟一問題!這就須要SessionID綁定,或者說叫作「會話複製」。
若是這時候你在頁面上提交表單,將鍵值對保持在session中,在頁面刷新後,將沒法得到該信息,由於Seesion丟失了!
接着修改
/etc/apache2/mods-available/proxy.conf,讓SeesionID保持惟一:
- sudo vi /etc/apache2/mods-available/proxy.conf
增長
stickySession屬性:
- ProxyPass /zlex balancer://zlex stickySession=JSESSIONID
stickySession粘性會話,根據這一屬性,瀏覽器將經過cookie綁定SeesionID。若是這個時候再次訪問
http://localhost/zlex,你會發現,頁面不會來回跳轉了!
sticky是什麼?
引用
sticky模式
利用負載均衡器的sticky模式的方式把全部同一session的請求都發送到相同的Tomcat節點。這樣不一樣用戶的請求就被平均分配到集羣 中各個tomcat節點上,實現負載均衡的能力。這樣作的缺點是沒有災難恢復的能力。一旦一個節點發生故障,這個節點上全部的session信息所有丟 失;
同一用戶同一session只和一個webServer交互,一旦這個webserver發生故障,本次session將丟失,用戶不能繼續使用 !
提交一個Session設定看看
http://localhost/zlex:
觀察後臺日誌:
再看看返回頁面,這至關於一次頁面刷新,若是正常粘性會話,咱們將得到當前SessionID對應的一切信息:
這說明粘性會話生效了!
咱們獲得了形如
引用
50DAF14C6CDF8ACFBDC1095A5EE8E2CF.tomcat1
的SessionID。這樣,咱們就能知道當前訪問的是哪臺服務器了!
若是,換一個瀏覽器打開該頁面http://localhost/zlex,將會得到一個新的SessionID,而且,根據Apache中配置的負載均衡節點列表依次訪問下一個節點!
若是這時候負載均衡節點列表中某一節點發生異常,那麼Apache將按照慣例,跳轉該節點,並在該節點恢復正常後約1分鐘內從新將其歸入可用節點!
修改剛纔的jsp頁面,看看Http頭中都有些什麼:
- <b>Cookie信息:</b>
- <br />
- ${header["cookie"]}
- <br />
- <b>Host信息:</b>
- <br />
- ${header["host"]}
- <br />
sticky模式的根本在於瀏覽器支持cookie,若是瀏覽器不支持cookie,則須要修改server.xml文件中的<Context />節點,將cookie置爲false,關閉cookie功能,讓jsessionid顯式傳遞!
6.Tomcat集羣,Session複製
通過兩天反覆研究,兩隻互不相認的Tomcat終於在網絡上「資源共享」了——Session複製成功!
關於
Tomcat集羣以及Session複製,網上已經有不少不少,可是否真的能用?!爲了確認這一問題,週末還跑到書店翻了翻《Apache Tomcat 高級編程》,參考
Clustering/Session Replication HOW-TO(有點小錯誤),通過兩天苦戰,克服種種小問題,終於拿下!
整理概念:
引用
羣集,是包含多個服務器實例的指定集合,這些服務器實例共享相同的應用程序、資源以及配置信息。您能夠將不一樣計算機上的服務器實例分組到一個邏輯羣集中並將其做爲一個單元來管理。您可使用 DAS 輕鬆控制多機羣集的生命週期。
羣集能夠實現水平可伸縮性、負載平衡和故障轉移保護。根據定義,羣集中的全部實例都具備相同的資源和應用程序配置。當羣集中的服務器實例或計算機 出現故障時,負載平衡器檢測到該故障,會將通訊從出現故障的實例重定向至羣集中的其餘實例,並恢復用戶會話狀態。因爲羣集中全部實例上的應用程序和資源都 相同,所以一個實例能夠故障轉移至羣集中的任何其餘實例。
引用
Session複製,主要是指集羣環境下,多臺應用服務器之間同步Session,確保Session保持一致,且Session中的內容保持一致,對外透明——看起來就像是一臺應用服務器!
若是其中一臺服務器發生故障,根據負載均衡的原理,Apache會遍歷尋找可用節點,分發請求。與此同時,當前用戶Session不能發生數據丟失,其他各節點服務器應保證用戶Session數據同步。
Session複製核心內容主要是:
- Session內容序列化(serialize),會消耗系統性能。
- Session內容經過廣播同步給成員,會形成網絡流量瓶頸,即使是內網瓶頸。
所以,Session複製的這兩個潛在問題,導致複雜均衡節點最多不會超過4個。由於,當節點數大於4時,整個集羣的吞吐量將再也不上升!
爲了搭建Tomcat集羣,我將兩個Tomcat分別部署到兩臺虛擬機上,確保網段一致。(這一步很關鍵,我最初將
Tomcat1(192.168.49.132)部署在虛擬機上,將
Tomcat2(192.168.49.128)部署在本機上,結果,網絡總有問題,耽誤了不少時間。
)
因爲變換了IP,我須要修改Apache的
/etc/apache2/mods-available/proxy.conf文件:
- sudo vi /etc/apache2/mods-available/proxy.conf
修改負載均衡節點以下:
- <Proxy balancer://zlex>
- BalancerMember ajp://192.168.49.128:8009/zlex route=tomcat1
- BalancerMember ajp://192.168.49.132:8009/zlex route=tomcat2
- </Proxy>
對於windows系統,不須要考慮網絡問題,廣播地址(這裏用到
224.0.0.0和
240.0.0.0)默認開放,對於linux則須要經過命令開放地址。
Ubuntu上開放廣播地址(eth0網卡):
- sudo route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0
而後經過
-v參數查看當前開放的廣播地址:
注意,重啓後,該路由設置將丟失!
在Ubuntu下,能夠考慮修改
/etc/networks文件!
若是有必要,Windows上開放廣播地址(192.168.49.128本機地址):
- route add 224.0.0.0 mask 240.0.0.0 192.168.49.128
而後經過
print參數查看當前開放的廣播地址:
而後,修改tomcat的server.xml文件:
- sudo vi /etc/tomcat6/server.xml
在
<Engine /> 節點中加入以下內容:
- <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
- channelSendOptions="8">
-
- <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="224.0.0.0"
- port="45564"
- frequency="500"
- dropTime="3000"/>
- <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
- address="192.168.49.1"
- port="4000"
- autoBind="100"
- selectorTimeout="5000"
- 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.MessageDispatch15Interceptor"/>
- </Channel>
-
- <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
- filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>
- <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
-
- <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
- <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
- </Cluster>
這裏須要注意
<Membership />和
Receiver:
<Membership />節點的
address屬性是廣播地址;
Receiver節點的
address屬性是本地綁定地址。固然,默認爲
auto。因爲我在啓動Tomcat時,Tomcat頻頻將地址指向
127.0.0.1,無奈只好使用固定IP。
此外,爲了下降Session複製的成本,Tomcat經過
<Valve />節點,以過濾器的方式控制哪些請求能夠忽略Session複製:
- <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
- filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>
同時,在<Host>節點中加入以下內容:
- <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
- tempDir="/tmp/war-temp/"
- deployDir="/tmp/war-deploy/"
- watchDir="/tmp/war-listen/"
- watchEnabled="false"/>
在Tomcat的官方文檔(Tomcat 6)中,對於<Deployer />節點的部署位置是錯誤的,經過觀察Tomcat啓動日誌,確認該節點應當不屬於<Host />節點中!
注意:Tomcat 6與Tomcat5在上述節點中使用的類包(包中名稱由cluster變化爲ha)有所不一樣,且結構有所調整。
先別急着重啓,咱們須要修改應用中的
web.xml文件,將
<distributable />節點部署到
<web-app />節點中,開啓分佈式服務:
注意:若是沒有設置該節點,SessionID將不能保持同步,不一樣的服務器將各自創建獨立的SessionID!
監控Tomcat日誌:
- tail -f /var/lib/tomcat6/logs/catalina.out
而後重啓Tomcat1:
- sudo /etc/init.d/tomcat6 restart
觀察日誌:
注意兩處紅框:
第一處,Cluster啓動,並綁定192.168.49.132:4000上,進行TCP通信,並等待其它成員(Member)。
第二處,在管理器中註冊/zlex,綁定JvmRouteBinderValve。
至此,說明集羣設置已經生效,但不能說明集羣配置成功!
接着咱們啓動Tomcat2,觀察其日誌:
Cluster啓動,並綁定192.168.49.128:4000上並發現成員[b]192.168.49.132![/b]
再看Tomcat1的日誌:
Tomcat1發現其成員Tomcat2!這說明TCP通信已創建,Tomcat成員能夠進行Session同步!
同時,Tomcat成員直接會每隔一個時間段相互偵測/驗證其餘成員是否正常:
如今,開始訪問
http://localhost/zlex,並不斷刷新當前頁面:
除了兩處標識主機來源的tomcatX不一樣外,session是徹底一致的!
提交一次修改,並不斷刷新當前頁:
若是仔細觀察,當前SessionID在不斷交替變化,這說明負載均衡在起做用!
咱們再來看看2個Tomcat後臺日誌都作了什麼!
Tomcat1:
Tomcat2:
兩隻貓都打印了相同的內容(
a=1)不一樣的細節在於,sessionID帶有服務器標識!
若是咱們強行關閉Tomcat2:
首先,Tomcat1會很快偵測到Tomcat2離線,由於這是TCP通信,成員之間很容易檢測到其餘成員是否離線!Tomcat1後臺日誌以下:
其次,Apache會偵測到Tomcat2發生異常,將其他請求轉交給其餘節點,即交由Tomcat1處理!
繼續刷新
http://localhost/zlex當前頁面,耐心等待幾秒。你會發現,即使再次刷新頁面,sessionID仍舊綁定在標識tomcat1服務器上。
而後,咱們恢復Tomcat2服務,Tomcat1會立刻偵測到Tomcat2已經恢復正常:
最後,咱們再次刷新當前頁,Apache已經將請求分發給Tomcat2了,從後臺日誌能夠看到session信息會很快被同步了!
若是帶有tomcatX標識的sessionID有不少不便之處,能夠關閉粘性會話。簡單的講,就是取消Tomcat中[b]server.xml中
<Engine/ >節點的
jvmRoute屬性![/b]而後,重啓tomcat、apache!
頁面提交一個
b=3!
左邊爲Tomcat1,右邊爲Tomcat2!SessionID一致!
除了上述幾種方案外,還有
Terracotta模式。一種第三方集羣組件,2009年收購了緩存組件EhCache,能夠結合Tomcat、JBoss等多種服務器,提供多種負載均衡、集羣等功能實現,且當負載均衡節點超過8個時,仍然可以保持集羣吞吐量的線性增加。
Eclipse插件地址:
http://download.terracotta.org/eclipse/update
下載地址:
http://www.terracotta.org/dl/oss-download-catalog
至此,Apache + Tomcat成功完成,征服Apache系列暫告一段落!
做爲開博以來的第100帖,算是很成功了!
測試應用見附件!