一、前言
在單一的服務器上執行WEB應用程序有一些重大的問題,當網站成功建成並開始接受大量請求時,單一服務器終究無法滿足需要處理的負荷量,所以就有點顯得有 點力不從心了。另外一個常見的問題是會產生單點故障,如果該服務器壞掉,那麼網站就立刻無法運作了。不論是因爲要有較佳的擴充性還是容錯能力,我們都會想 在一臺以上的服務器計算機上執行WEB應用程序。所以,這時候我們就需要用到集羣這一門技術了。
在進入集羣系統架構探討之前,先定義一些專門術語:
1. 集羣(Cluster):是一組獨立的計算機系統構成一個松耦合的多處理器系統,它們之間通過網絡實現進程間的通信。應用程序可以通過網絡共享內存進行消息傳送,實現分佈式計算機。
2. 負載均衡(Load Balance):先得從集羣講起,集羣就是一組連在一起的計算機,從外部看它是一個系統,各節點可以是不同的操作系統或不同硬件構成的計算機。如一個提供Web服務的集羣,對外界來看是一個大Web服務器。不過集羣的節點也可以單獨提供服務。
3. 特點:在現有網絡結構之上,負載均衡提供了一種廉價有效的方法擴展服務器帶寬和增加吞吐量,加強網絡數據處理能力,提高網絡的靈活性和可用性。集羣系統(Cluster)主要解決下面幾個問題:
高可靠性(HA):利用集羣管理軟件,當主服務器故障時,備份服務器能夠自動接管主服務器的工作,並及時切換過去,以實現對用戶的不間斷服務。
高性能計算(HP):即充分利用集羣中的每一臺計算機的資源,實現複雜運算的並行處理,通常用於科學計算領域,比如基因分析,化學分析等。
負載平衡:即把負載壓力根據某種算法合理分配到集羣中的每一臺計算機上,以減輕主服務器的壓力,降低對主服務器的硬件和軟件要求。
總體來說,在負載均衡的思路下,多臺服務器爲對等方式,每臺服務器都具有同等的地位,可以單獨對外提供服務而無須其他服務器的輔助。通過負載分擔技術,將外部發送來的請求按一定規則分配到對稱結構中的某一臺服務器上,而接收到請求的服務器都獨立迴應客戶機的請求。
提供服務的一組服務器組成了一個應用服務器集羣(cluster),集羣下的對等多機環境可以增加系統的併發處理能力,和單臺機器出現故障系統的錯誤冗餘能力;同時實現了負載均衡和系統高可靠性。
二、常用負載均衡技術
1. 基於DNS的負載均衡
通過DNS服務中的隨機名字解析來實現負載均衡,在DNS服務器中,可以爲多個不同的地址配置同一個名字,而最終查詢這個名字的客戶機將在解析這個名字時 得到其中一個地址。因此,對於同一個名字,不同的客戶機會得到不同的地址,他們也就訪問不同地址上的Web服務器,從而達到負載均衡的目的。
2. 反向代理負載均衡 (如Apache+JK2+Tomcat這種組合)
使用代理服務器可以將請求轉發給內部的Web服務器,讓代理服務器將請求均勻地轉發給多臺內部Web服務器之一上,從而達到負載均衡的目的。這種代理方式 與普通的代理方式有所不同,標準代理方式是客戶使用代理訪問多個外部Web服務器,而這種代理方式是多個客戶使用它訪問內部Web服務器,因此也被稱爲反 向代理模式。
3. 基於NAT(Network Address Translation)的負載均衡技術 (如Linux Virtual Server,簡稱LVS)
網絡地址轉換爲在內部地址和外部地址之間進行轉換,以便具備內部地址的計算機能訪問外部網絡,而當外部網絡中的計算機訪問地址轉換網關擁有的某一外部地址 時,地址轉換網關能將其轉發到一個映射的內部地址上。因此如果地址轉換網關能將每個連接均勻轉換爲不同的內部服務器地址,此後外部網絡中的計算機就各自與 自己轉換得到的地址上服務器進行通信,從而達到負載分擔的目的。
三、Apache+JK2實現Tomcat集羣與負載均衡
客戶系統一般採用Apache httpd作爲web服務器,即作爲Tomcat的前端處理器,根據具體情況而定,有些情況下是不需要Apache httpd作爲 web 服務器的,如系統展現沒有靜態頁面那就不需要Apache httpd,那時可以直接使用Tomcat作爲web 服務器來使用。使用Apache httpd主要是它在處理靜態頁面方面的能力比Tomcat強多了。
爲了實現這個原理我們就需要解決兩個問題:
1:如何實現多應用服務器間的session共享:(一臺服務器崩潰,另外一臺服務器可以繼續支持)
2:如何分發請求到各個應用服務器實現壓力分解:(這裏的解決方案是用apache做 web服務器)
下面我們就是實際行動來看看如何實現這種實現。
環境配置:
App應用服務器apache-tomcat-7.0.52-1
web服務器:apache的apache 2.0.55
Java環境:jdk1.6及以上
3、web服務器配置
首先安裝apache的web服務器:
apache服務器和tomcat的連接方法其實有三種:mod_JK、http_proxy和ajp_proxy。
3.1、軟件環境:
3.1.1、 Apache: apache 2.0.55 (由http://httpd.apache.org/進入下載)(點擊下載apache 2.0.55)
3.1.2、 Tomcat: apache-tomcat-7.0.52-1
3.1.3、 mod_jk: 在頁面 http://tomcat.apache.org/Download 標題下找到 Tomcat Connectors 鏈接進入(點擊下載mod_jk-apache-2.0.55.so),看起來像是個Unix/Linux下的動態庫,實際應是個Win32 的 DLL 動態庫,大概是爲保持不同平臺配置的一致性,才用了這個擴展名。
3.2、負載均衡:
用Apache進行分流,把請求按照權重以及當時負荷分tomcat1,tomcat2...去處理
3.2.1、 安裝apache,tomcat
我把Apache安裝在D:\Program Files\Apache Group\Apache2,Tomcat解壓兩分Tomcat, 分別在 D:\Program Files\Apache Group\apache-tomcat-7.0.72-1,D:\Program Files\Apache Group\apache-tomcat-7.0.72-1,我的結構如下:
3.2.2、修改Apache配置文件http.conf
在apache安裝目錄下conf目錄中找到http.conf,在文件最後加上下面一句話就可以了:
- include conf/mod_jk.conf
3.3.3、 http.conf 同目錄下新建mod_jk.conf文件,內容如下:
- #加載mod_jk Module
- LoadModule jk_module modules/mod_jk-apache-2.0.55.so
- #指定 workers.properties文件路徑
- JkWorkersFile conf/workers.properties
- #指定那些請求交給tomcat處理,"controller"爲在workers.propertise裏指定的負載分配控制器
- JkMount /*.jsp controller
配置說明:
如果還要指定*.do也進行分流就再加一行
JkMount /*.do controller
如果你想對所有的請求進行分流只需要寫成
JkMount /* controller
3.3.4、 在http.conf同目錄下新建 workers.properties文件:
- worker.list = controller,tomcat1,tomcat2 #server 列表(tomcat1,tomcat2分別指tomcat的具體路徑)
- #========tomcat1========
- worker.tomcat1.port=8019 #ajp13 端口號,在tomcat下server.xml配置,默認8009
- worker.tomcat1.host=localhost #tomcat的主機地址,如不爲本機,請填寫ip地址
- worker.tomcat1.type=ajp13
- worker.tomcat1.lbfactor = 1 #server的加權比重,值越高,分得的請求越多
- #========tomcat2========
- worker.tomcat2.port=8029 #ajp13 端口號,在tomcat下server.xml配置,默認8009
- worker.tomcat2.host=localhost #tomcat的主機地址,如不爲本機,請填寫ip地址
- worker.tomcat2.type=ajp13
- worker.tomcat2.lbfactor = 2 #server的加權比重,值越高,分得的請求越多
- #========controller,負載均衡控制器========
- worker.controller.type=lb
- worker.controller.balanced_workers=tomcat1,tomcat2 #指定分擔請求的tomcat
- worker.controller.sticky_session=1
3.3.5、 修改tomcat配置文件server.xml:
如果你是水平集羣,即在不同電腦上安裝tomcat,tomcat的安裝數量爲一個,可以不必修改tomcat配置文件。我這裏是在同一臺電腦上安裝兩個tomcat,實現的是垂直集羣方式,所以必須修改其中一個的設置,以避免端口衝突,按照參考文章是把原來以9開頭的端口號改爲以9開頭端口號,但是在我機器上如果以9開頭的端口號,例如9080、9082會與我的WebSphere Application Server配置衝突,所以我這裏採取的策略是把原來端口號的第三位改爲1,如8080改爲8180。
打開tomcat2/conf/server.xml文件
3.3.5.1、將關閉Tomcat的監聽端口改成由8005改爲8105
即把
<Server port="8005" shutdown="SHUTDOWN">
改爲
<Server port="8105" shutdown="SHUTDOWN">
3.3.5.2、把http服務端口號由8080改爲8180
找到
<!-- Define a non-SSL HTTP/1.1 Connector on port 8080 -->
<CONNECTOR port="8080"
把這裏的8080改爲8180
3.3.5.3、 把AJP端口號由8009改爲8109
找到
<!-- Define an AJP 1.3 Connector on port 8009 -->
<CONNECTOR port="8009"
把這裏的8009改爲8109
3.3.5.4、 把 HTTP 代理端口從8082改爲8182(這個配置默認是被註釋掉的,可跳過這一步)
找到
<CONNECTOR port="8082"
把這裏的8082改爲8182
3.3.5.5、 編寫一個測試 jsp
建立一個目錄TestCluster,裏面新建一個test.jsp,內容爲:
- <%
- System.out.println("=============hello yangwenxue!!!==============");
- %>
把TestCluster放到tomcat1,tomcat2的webapps下
3.3.5.6、啓動apache,tomcat1,tomcat2,進行測試:
通過 http://localhost/TestCluster/test.jsp 訪問,多刷新幾次頁面,查看Tomcat1和Tomcat2的窗口,你將可以看到打印了一行行“=============hello yangwenxue!!!==============”,並且從統計上來說,大約在tomcat2打印的數量是在Tomcat1中的兩倍,可以看到請求會被tomcat1,tomcat2按照不同的權重分流處理,實現了負載均衡,如下圖:
3.3.6、集羣(session複製):
在workers.properties把tomcat1和tomcat2的權重改爲一樣的,使請求較平均分配,將有便於看到實驗的效果。
首先配置web應用服務器配置apache-tomcat-7.0.72-1配置
3.3.6.1、修改tomcat的server.xml文件增加如下內容(在<Engine>或<Host>元素下添加以下內容均可):
- <Cluster className="org.apache.catalina.cluster.tcp.SimpleTcpCluster"
- managerClassName="org.apache.catalina.cluster.session.DeltaManager"
- expireSessionsOnShutdown="false"
- useDirtyFlag="true"
- notifyListenersOnReplication="true">
- <Membership
- className="org.apache.catalina.cluster.mcast.McastService"
- mcastAddr="228.0.0.4"
- mcastPort="45564"
- mcastFrequency="500"
- mcastDropTime="3000"/>
- <Receiver
- className="org.apache.catalina.cluster.tcp.ReplicationListener"
- tcpListenAddress="auto"
- tcpListenPort="4001"
- tcpSelectorTimeout="100"
- tcpThreadCount="6"/>
- <Sender
- className="org.apache.catalina.cluster.tcp.ReplicationTransmitter"
- replicationMode="pooled"
- ackTimeout="15000"
- waitForAck="true"/>
- <Valve className="org.apache.catalina.cluster.tcp.ReplicationValve"
- filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>
- <Deployer className="org.apache.catalina.cluster.deploy.FarmWarDeployer"
- tempDir="/tmp/war-temp/"
- deployDir="/tmp/war-deploy/"
- watchDir="/tmp/war-listen/"
- watchEnabled="false"/>
- <ClusterListener className="org.apache.catalina.cluster.session.ClusterSessionListener"/>
- </Cluster>
按照apache tomcat官方文檔上面的說法,對於tomcat6要做集羣的話,只需要將<Engine>元素下的
- <!--
- <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
- -->
的註釋符號去掉,啓用這句配製就可以正常使用集羣了。不過由於我搭建的測試環境2個tomcat是在同一臺機子上面,因此只啓用這個<Cluster>的話,這個元素下的默認Receiver port就會衝突。因此必須補全這些配置。
另外,按照官方文檔裏說明的<Cluster>的默認配置,<Membership>元素下的address屬性默認值爲228.0.0.4 . 這個配置在我的系統上始終會出現2個tomcat無法交換數據包的問題。需改成224.0.0.1才能正常使用。
需要而外注意的是,2個tomcat裏添加到上面這段<Cluster>配置,其<Receiver>下的Port元素必需配置成不同的。如其中一個是4001,一個是4002.(tomcat默認可以檢測到4000~4100之間的端口)。
3.3.6.2、修改<Engine>的屬性。
將原來的:
改爲:
tomcat2對應修改爲tomcat2。
2個tomcat, jvmRoute分別配置成tomcat1和tomcat2,即和apache/conf裏worker.properites配置文件中配置的worker名稱對應。
3.3.6.3、項目集羣配置及測試:
a)、用eclipse新建一個web工程,並修改項目的web.xml,添加 <Context distributable="true" />,所有需要集羣的web項目,其web.xml中都必須添加
- <distributable/>
這個定義,如下圖:
b)、在該web工程的WebContext目錄下新建一個test.jsp文件,編輯內容如下:
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <%@ page import="java.util.*"%>
- <html>
- <head><title>Cluster app test</title></head>
- <body>
- Server info:
- <%
- out.print(request.getLocalAddr() + ":" + request.getLocalPort() + "<br>");
- %>
- <%
- out.print("<br>ID " + session.getId() + " <br>");
- //如果有新的session屬性設置
- String dataName = request.getParameter("dataName");
- if(dataName != null && dataName.length() > 0){
- String dataValue = request.getParameter("dataValue");
- session.setAttribute(dataName,dataValue);
- }
- out.print("<b>session列表</b>");
- Enumeration e = session.getAttributeNames();
- while(e.hasMoreElements()){
- String name = (String)e.nextElement();
- String value = session.getAttribute(name).toString();
- out.print(name + " = " + value + "<br>");
- System.out.println(name + " = " + value);
- }
- %>
- <form action="test.jsp" method="POST">
- 名稱:<input type="text" size=20 name="dataName"><br>
- 值:<input type="text" size=20 name="dataValue"><br>
- <input type="submit">
- </form>
- </body>
- </html>
c)、打包發佈到兩個tomcat的webapps下。
d)、啓動測試:
啓動第一個tomcat,先啓動哪個都行:
啓動tomcat2:
啓動過程和tomcat1一樣,值得注意的是,在tomcat2啓動的時候,tomcat1會打印出集羣中加入member的提示:
e)、打開瀏覽器,輸入地址:http://localhost/Test/test.jsp,該請求分配給了tomcat1;添加幾條測試session,瀏覽器和tomcat的請求情況:
f)、換個瀏覽器,輸入:http://localhost/Test/test.jsp,改請求分配給了tomcat2,添加幾條測試session:
以上就是集羣中的對tomcat的分流,負載均衡。
現在我們來測試如果一臺服務器宕機後session是否能達到共享:
g)、關閉tomcat1,tomcat2會有集羣中服務關閉的提示:
tomcat1關閉後,原來在tomcat1的session能否複製到tomcat2呢?
刷新ie瀏覽器,在tomcat2的請求中確實收到了原來tomcat1的session:
刷新兩個瀏覽器,tomcat2的請求結果:
這樣,其中一個tomcat服務宕掉了,原來的session的信息不會丟失,工作交給其他tomcat來工作,這就是集羣的好處。
說明:
1、本篇文章參考了幾篇tomcat的集羣,tomcat的session共享,及官網文檔,並親自搭建成功,總結每個實現步驟。
2、爲了尊重前人鋪路成果,所以本篇文章標爲轉載。
3、在此也聲明:轉載請註明出處:http://blog.csdn.net/yangwenxue_admin/article/details/72845360
相關文章:
Tomcat集羣應用部署的實現機制
https://blog.csdn.net/wangyangzhizhou/article/details/52167894