1. 程式人生 > >一個完整的負載均衡的例子

一個完整的負載均衡的例子

原理:tomcat 做個WEB伺服器有它的侷限性,處理能力低,效率低。承受併發小(1000左右)。但目前有不少網站或者頁面是JSP的。並採用了tomcat做為WEB,因此只能在此基礎上調優。 目前採取的辦法是Apache + Mod_JK + tomcat 來解決一部分請求,使用者訪問的是apache,但有jsp頁面的時候才會去請求tomcat。如果量一大,那麼tomcat無法承受,那麼只能做tomat叢集,Apache + Mod_JK 就是負載均衡器了。 Mod_JK2負載均衡 可以把不同的jsp請求轉發到不同的tomcat伺服器,還可以偵測伺服器存活。如果有條件可以給Mod_JK2做一個HA因為做完集群后壓力就在JK上了。 簡單拓僕圖: 鎷撲粏鍥�

準備工作

相關文件:

web server how to:

安裝路徑:

httpd:             D:\Server\Apache httpd2_2

tomcat D:\Server\tomcat7-1 tomcat7-2 tomcat7-3

JK  D:\Server\Apache httpd2_2\modules\mod_jk-1.2.31-httpd-2.2.3.so

step 1: 新增並配置JK

D:\Server\Apache httpd2_2\conf\httpd.conf檔案最後加上,意思是把這個配置載入進來

Xml程式碼  
收藏程式碼
  1. include conf\mod_jk.conf  

新建mod_jk.conf檔案,內容如下:

Java程式碼  收藏程式碼
  1. LoadModule jk_module modules/mod_jk-1.2.31-httpd-2.2.3.so  
  2. JkWorkersFile conf/workers.properties  
  3. #指定那些請求交給tomcat處理,"controller"為在workers.propertise裡指定的負載分配控制器名  
  4. JkMount /*.jsp controller  

Step 2: 配置worker

新建並編輯workers.properties檔案,內容如下

Java程式碼  收藏程式碼
  1. #server  
  2. worker.list = controller  
  3. #========tomcat1========  
  4. worker.tomcat1.port=11009  
  5. worker.tomcat1.host=localhost  
  6. worker.tomcat1.type=ajp13  
  7. worker.tomcat1.lbfactor = 1  
  8. #========tomcat2========  
  9. worker.tomcat2.port=12009  
  10. worker.tomcat2.host=localhost  
  11. worker.tomcat2.type=ajp13  
  12. worker.tomcat2.lbfactor = 1
      
  13. #========tomcat3========  
  14. worker.tomcat3.port=13009  
  15. worker.tomcat3.host=192.168.0.80 //在我的虛擬機器中的,可以算遠端的吧  
  16. worker.tomcat3.type=ajp13  
  17. worker.tomcat3.lbfactor = 1  
  18. #========controller,負載均衡控制器========  
  19. worker.controller.type=lb  
  20. worker.controller.balanced_workers=tomcat1,tomcat2,tomcat3  
  21. worker.controller.sticky_session=false  
  22. worker.controller.sticky_session_force=1  
  23. #worker.controller.sticky_session=1  

如果三個tomcat不在同一臺機器上,那麼下面改埠的事情就可以省很多力氣,不過因為要單機做負載均衡,所以要更改三個tomcat的8005,8080的埠,確保都不一樣,不然tomcat是沒辦法同時啟動三個的。

測試的時候我三個tomcat都放在本地,因為要同時啟動三個tomcat,所以需要更改三個tomcat中的Connector埠號,將三個tomcat的的protocol="HTTP/1.1" 的connector的port改為10080,11080,12080。(原來是8080)

同時講原來8005的埠分別改成10005,11005,12005(這個是關閉tomcat的埠號)

tomcat7-1

Xml程式碼  收藏程式碼
  1. <Connector port="10080" protocol="HTTP/1.1" connectionTimeout="20000"   
  2.            redirectPort="8443" />  

tomcat7-2

Java程式碼  收藏程式碼
  1. <Connector port="11080" protocol="HTTP/1.1" connectionTimeout="20000"   
  2.            redirectPort="8443" />  

tomcat7-3

Java程式碼  收藏程式碼
  1. <Connector port="12080" protocol="HTTP/1.1" connectionTimeout="20000"   
  2.            redirectPort="8443" />  

除了更改server.xml中 原來的8080的埠(protocol="HTTP/1.1" 的埠號)

還需要配置AJP13的埠號,同時開啟預設註釋掉的<Cluster>標籤,對應的<Engine>的jvmRoute改成workers.property裡面對應的名字,配置如下(刪除了註釋)

tomcat7-1

Xml程式碼  收藏程式碼
  1. <Connector port="11009" protocol="AJP/1.3" redirectPort="8443" />  
  2.     <Engine name="Catalina" defaultHost="localhost"          
  3.            jvmRoute="tomcat1">    
  4.     <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>  

tomcat7-2

Java程式碼  收藏程式碼
  1. <Connector port="12009" protocol="AJP/1.3" redirectPort="8443" />  
  2.     <Engine name="Catalina" defaultHost="localhost"          
  3.            jvmRoute="tomcat2">    
  4.     <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>  

tomcat7-3

Java程式碼  收藏程式碼
  1. <Connector port="13009" protocol="AJP/1.3" redirectPort="8443" />  
  2.     <Engine name="Catalina" defaultHost="localhost"          
  3.            jvmRoute="tomcat3">    
  4.     <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>  

OK,這時候可以成功啟動三個tomcat7了,當tomcat7-2啟動後,tomcat7-1會打印出replication的資訊

類似於

Xml程式碼  收藏程式碼
  1. 2011-9-20 14:12:18 org.apache.catalina.ha.tcp.SimpleTcpCluster memberAdded  
  2. 資訊: Replication member added:org.apache.catalina.tribes.membership.MemberImpl[tcp://{172, 16, 10, 96}:4001,{172, 16, 10, 96},4001, alive=1000securePort=-1, UDP Port=-1, id={109 112 -14 -8 -44 98 79 85 -89 -48 -33 -127 -47 -30  
  3. 26 -75 }, payload={}, command={}, domain={}, ]  
Step3: 建立測試專案 在專案的web.xml中新增<distributable/> testlb.jsp: Html程式碼  收藏程式碼
  1. <%@ page contentType="text/html; charset=GBK"%>  
  2. <%@ page import="java.util.*"%>  
  3. <html>  
  4.     <head>  
  5.         <title>Cluster App Test</title>  
  6.     </head>  
  7.     <body>  
  8.         Server Info:  
  9.         <%  
  10.         out.println(request.getLocalAddr() + " : " + request.getLocalPort() + "<br>");  
  11.         %>  
  12.         <%  
  13.             out.println("<br> ID " + session.getId() + "<br>");  
  14.             // 如果有新的 Session 屬性設定  
  15.             String dataName = request.getParameter("dataName");  
  16.             if (dataName != null && dataName.length() > 0) {  
  17.                 String dataValue = request.getParameter("dataValue");  
  18.                 session.setAttribute(dataName, dataValue);  
  19.             }  
  20.             out.println("<b>Session 列表</b><br>");  
  21.             System.out.println("============================");  
  22.             Enumeration e = session.getAttributeNames();  
  23.             while (e.hasMoreElements()) {  
  24.                 String name = (String) e.nextElement();  
  25.                 String value = session.getAttribute(name).toString();  
  26.                 out.println(name + " = " + value + "<br>");  
  27.                 System.out.println(name + " = " + value);  
  28.             }  
  29.         %>  
  30.         <form action="testlb.jsp" method="POST">  
  31.             名稱:  
  32.             <input type=text size=20 name="dataName">  
  33.             <br>  
  34.             值:  
  35.             <input type=text size=20 name="dataValue">  
  36.             <br>  
  37.             <input type=submit>  
  38.         </form>  
  39.     </body>  
  40. </html>  
多次重新整理頁面的sessionID看是同一個ID,說明session是複製成功了。那麼session中的儲存的東西呢,在輸入框中分別輸入1、1,2、2,3、3後,顯示結果如下圖: 

(以下為原文摘抄,我真的比較lazy)

以上的測試說明,叢集中的session已經共享,每個叢集對於同一訪問均有相同的session,而且session中儲存的變數也複製了。

節點插拔測試

插拔意思是應該保證當執行的叢集中某節點中關閉或者啟動時,叢集正常工作並且節點能夠正常工作。

下面描述測試過程了,貼圖太佔地方了。

關閉Tomcat2,重新整理頁面,則不斷訪問Tocmat1和Tomcat3,再關閉Tomcat1後,則只訪問一個Tomcat3,說明節點關閉時執行正常。

如果重啟Tomcat2,無論怎麼重新整理,始終訪問Tomcat3,難道Apache不能將請求轉發給中途啟動的Tomcat2?。。。這時利用另外臺機器訪問頁面,發現Tomcat2正常,然後在刷本地頁面,又可以訪問Tomcat2了。

從上面可以看出Apache的負載均衡時的演算法了,對於每個新來的session,Apache按照節點配置中的lbfactor比重選擇訪問節點,如果某節點node1不能訪問,則尋找下一可訪問節點,並且將此node1就在該訪問session的訪問黑名單中,以後該session的訪問直接不考慮node1,即使node1又可以訪問了。而新來的session是無黑名單的,如果新的session能夠訪問到node1了,則會將node1在其他所有session訪問的黑名單刪除,這樣其他session就又能訪問node1節點了。以上只是個人經過測試後的猜想。

經過以上測試,說明Tomcat叢集和負載均衡已經實現了。

關於叢集我還有些疑問,所以又測試了下,直接把結論寫出來:

1.叢集下的相同的應用可以名稱不同(好像沒必要啊),只要配置server.xml中host下的context具有相同的path即可。

2. 如果應用名稱可以不同,那麼應用下內容是否可以不同呢(這裡考慮將不同應用通過叢集看起來是一個應用,並且共享session),然後叢集下不同應用對映為相同的訪問path,具有相同的路徑則負載,如果某路徑只某個應用具有,則一直訪問該應用。可現實很骨幹啊,答案是否定的,至少我以上的配置不能實現。如果訪問只有某應用具有的特別路徑,那麼只有負載到該應用才可以訪問,否則直接路徑未找到的錯誤頁面了。

 如果您看過網上其他Apache+Tomcat的叢集配置,您可能有的疑問?

1.網上大部分的文章配置2個tocmat的叢集,有的將workers.properties下的worker.controller.sticky_session=1,
然後tomcat1中的server.xml中的jvmRoute設定為tomcat2,將tomcat2中的jvmRoute設定為tocmat1,當然我這樣設定
也成功了,但是如果3個或者更多tocmat呢,怎麼設定每個tomcat的jvmRoute,我不會所以才考慮現在的配置

2.server.xml中的Cluster配置問題,網上大部分都是使用BackupManager方式,即Cluster下又貼上了一堆配置。其實
只要將其中註釋掉的<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>去掉註釋就完成session的叢集
複製了。只是這倆種複製採用的方式不同而已。http://tomcat.apache.org/tomcat-6.0-doc/cluster-howto.html
這頁面已經說的挺清楚了,叢集的session複製預設是DeltaManager,是all to all的複製,意思是將叢集下1個tomcat應用下的session
對所有的叢集中的節點進行復制,即使那個節點沒有釋出應用。顯然是不好的方式,但這在小規模的叢集下並沒神馬問題。
而採用BackupManager,就是眾多網上配置那樣,對於需要複製的節點設定BackupManager自然也沒問題,
但是它的效能並沒有DeltaManager 好使“ Downside of the BackupManager: not quite as battle tested as the delta manager”。
因此,具體怎麼設定就看大家了,通常說如果不是大規模叢集,就預設就好了。反正我自己翻譯的就是這個意思了,希望沒有誤導大家。

最後一個比較全的關於session 同步使用jdbc方式的帖子