天上網查了查資料,之前使用apache的jk模塊做負載均衡。後來覺得jk的負載配置有點死板,只能按照負載權重值來進行請求的分發,沒有做到比較智能的負載平衡,並且使用mod_jk訪問頁面發現確實比較慢。可能是jk路由到真正的Node Server上比較費時間吧。結合筆者提出的jk的缺點,今天使用mod_proxy來進行負載均衡和路由選擇。
之前提出了jk相關的缺點
1):負載均衡權重是在配置文件中寫死的。不能根據實際的運行時機器的環境來決定負載均衡的策略,顯得比較死板
2):雖然在apache中配置了session共享,但是實際上session並沒有在node上進行共享傳遞。如果一臺機器掛了,那麼這臺機器的客戶session也就消失了,容錯性比較差
筆者的環境如下:
OS:Windows7
HttpServer:Apache Http Server2.2.17
Tomcat:apache-tomcat-6.0.29
下面來看如何加載mod_proxy模塊
1. 加載相關apache的模塊
在配置文件httpd.conf中放開註釋
#加載mod_proxy
- LoadModule proxy_module modules/mod_proxy.so
- LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
- LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
- LoadModule proxy_connect_module modules/mod_proxy_connect.so
- LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
- LoadModule proxy_http_module modules/mod_proxy_http.so
因爲在apache2.2.x版本中自動會有這些模塊,所以直接打開註釋即可。
修改<IfModule dir_module>內容如下
- DirectoryIndex index.html index.jsp
- </IfModule>
在此配置文件的末尾加上如下內容
- ServerAdmin [email]weijie@126.com[/email]
- ServerName localhost
- ServerAlias localhost
- ProxyPass / balancer://mycluster/ stickysession=JSESSIONID nofailover=Off
- ProxyPa***everse / balancer://mycluster/
- ErrorLog "logs/error.log"
- CustomLog "logs/access.log" common
- </VirtualHost>
其中VirtualHost *:8011代表筆者本機的http server端口。
ProxyPass / balancer://mycluster/代表所有的請求都會重定向到balancer://mycluster/處理。balancer是內置負載。
ProxyPa***everse / balancer://mycluster/是反向代理,也就是將所有的請求反向代理到負載均衡後的應用url路徑中。
stickysession=JSESSIONID nofailover=Off是做Session複製用的。
之後再將此配置文件末尾加上如下內容,配置Node
- <proxy balancer://mycluster>
- BalancerMember ajp://127.0.0.1:18009 loadfactor=1 route=tomcat7_node1
- BalancerMember ajp://127.0.0.1:28009 loadfactor=1 route=tomcat7_node2
- # status=+H爲配置熱備,當所有機器都over時,纔會請求該機器
- #BalancerMember http://192.168.1.218:8009 status=+H
- #按照請求次數均衡(默認)
- #ProxySet lbmethod=byrequests
- #按照權重
- #ProxySet lbmethod=bytraffic
- #按負載量,也就是往負載少的派發新請求
- #ProxySet lbmethod=bybusyness
- ProxySet lbmethod=bybusyness
- </proxy>
這裏不僅配置了2個tomcat的node節點,還配置了相關的負載算法策略。ProxySet lbmethod即是負載均衡算法策略。此處使用的是按照負載量,吞吐量少Node的之後可要小心嘍,分配到你的任務可就多了。而byrequests策略更偏重的是次數。
這裏還要說明的就是<proxy balancer://mycluster>,和上面的ProxyPass要對應上。
2. 之後準備2個Tomcat,進行Node的配置
筆者的Tomcat版本是apache-tomcat-6.0.29,本來是要用apache-tomcat-7.0.6的,這個版本有點問題,後來看官方網站說,確實此7.0.6版本有些問題。
首先來看apache-tomcat-6.0.29.2-node1的配置文件
修改關閉端口
修改http服務端口
- connectionTimeout="20000"
- redirectPort="18443" />
修改AJP協議端口
這個端口實際上就是與Apache Http Server通訊的通道,Apache會通過AJP協議與Tomcat進行通訊,所以在Apache中配置的Node端口就是此處的端口。
增加jvmRoute名字
之後最重要的將Tomcat的集羣配置放開,內容如下
- 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="228.0.0.4"
- mcastBindAddress="127.0.0.1"
- port="45564"
- frequency="500"
- dropTime="3000"/>
- <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
- address="auto"
- tcpListenAddress="127.0.0.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=""/>
- <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
- <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
- tempDir="/tmp/war-temp/"
- deployDir="/tmp/war-deploy/"
- watchDir="/tmp/war-listen/"
- watchEnabled="false"/>
- <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
- <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
- </Cluster>
之後apache-tomcat-6.0.29.2-node2的配置和它差不多,只有一些端口的差異,差異如下
- ………………
- <Connector port="28080" protocol="HTTP/1.1"
- connectionTimeout="20000"
- redirectPort="28443" />
- ………………
- <Connector port="28009" protocol="AJP/1.3" redirectPort="28443" />
- ………………
- <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat7_node2">
- ………………
- <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
- address="auto"
- tcpListenAddress="127.0.0.1"
- port="4001"
- autoBind="100"
- selectorTimeout="5000"
- maxThreads="6"/>
這樣Tomcat算是完成
3. 寫一個Web項目測試
測試頁面代碼如下
- 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.*"%>
- <%@page import="java.net.InetAddress;"%>
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>Cluster App Test</title>
- </head>
- <body>
- <%
- InetAddress ip = InetAddress.getLocalHost();
- //out.println(ip.getHostAddress());
- %>
- This is responsed by
- <font color="red"> <%=ip.getHostAddress()%></font>
- <br>
- Host Name :
- <font color="red"><%=ip.getHostName()%></font>
- <br>
- Time :
- <font color="red"><%=new Date()%></font>
- <br>
- <%
- ip = null;
- %>
- <br />
- <br />
- <br />
- <br />
- Server Info:
- <%
- out.println(request.getLocalAddr() + " : " + request.getLocalPort()
- + "<br>");
- %>
- <%
- out.println("<br>Session 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("<br/> <b>Session 列表</b>");
- Enumeration e = session.getAttributeNames();
- while (e.hasMoreElements()) {
- String name = (String) e.nextElement();
- String value = session.getAttribute(name).toString();
- out.println(name + " = " + value + "<br>");
- System.out.println(name + " = " + value);
- }
- %>
- <form action="index.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>
WEB-INF\web.xml如下
- <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
- http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
- <distributable/>
- <welcome-file-list>
- <welcome-file>index.jsp</welcome-file>
- </welcome-file-list>
- </web-app>
加入了<distributable/>用於Session複製。
4. Web項目測試效果
測試效果就是同一個瀏覽器IE的Session在不同的Node上共享了一個Session,其中一個Node掛了也不要緊,另一個緊跟着就複製過來了,而且在不同Node上切換也沒關係,Session不會丟失的。換一個瀏覽器FireFox建立一個新Session,互不影響。解決了之前提出的2個問題,1,負載算法可以根據實際請求壓力而分擔;2,Session不用很複雜的配置即可完成Session的複製,並且一個Node掛了也不要緊,Session在另一個節點上也可以正常使用,而且Node在請求的不同階段切換也沒關係,對用戶是透明的。