from http://blog.sina.com.cn/s/blog_4c925dca0100qh2l.html
因為公司需要做apache+jboss的負載平衡和叢集方面的要求,但阿堂原來沒有這方面的經驗,便開始了兩天的拓荒之旅,其間的痛苦就不用說了,因為網上的文章,基本上大同小異,估計很多都是互相轉載的,作了一些變動,都基本上有些錯務,加上網上介紹的jboss版本都基本與我的的版本不一致,造成網上提到的jboss目錄,我這裡有可能沒有,這樣就更增加了一些麻煩,而向網上一些哥們尋找協助的過程中,有的要麼沒做過,有的就只是做了 apaceh+tomcat的負載平衡,這樣就增加阿堂解決問題的難度!最後總算解決了!先不說最終的實施效果是否如期望的那樣,但這個方案能正常實施,可以說成功了90%了,至於apache等的調優等,就另當別論了!阿堂就不想在此說了!有感於自己配置的麻煩,就寫了這篇總結文章和朋友們來一起分享了,希望能對其它網友有所協助或借簽,阿堂就很開心了!
布署環境
(1)兩台Linux伺服器 192.168.80.13 192.168.80.14
(2)JBOSS版本 4.2.3.GA(在上述兩台Linux伺服器分別裝有此版本的JBoss伺服器)
(3) Apache 2.2.17版本 (httpd-2.2.17.tar.gz可從官方網站下載此檔案),Apache負載平衡伺服器安裝在192.168.80.13上
Apache -Http server
(4)Mod_jk版本1.2.3 (版本是 mod_jk-1.2.31-httpd-2.2.x.so,可從官方網站下載) -
Apache Tomcat - Tomcat Connectors
|
特別說明
(1)Mod_jk版本必須與Apache版本配套,否則,可能安裝好了,apache能正常運行,但是負載平衡還是不會起作用的,如
mod_jk-1.2.31-httpd-2.2.x.so (如前面的紅色文字是mod_jk的版本,後面的紅色文字是Apache Server的版本)
部署測試結果
(1)Apache負載平衡已經正常使用,比如說,
http://192.168.80.13/test/index.jsp
,前面192.168.80.13地址實際上是Apache伺服器的地址,它會攔截隨機調用叢集中的兩個結點192.168.80.13 和
192.168.80.14對應的JBoss伺服器
如下所示
(2)JBoss的叢集功能已經生效。叢集中的兩個JBoss結點能進行Ajp叢集通訊了
本項目負載平衡方式:
目前是選擇用的基於使用者的負載平衡,因為基於request的負載平衡與我們目前需要的多並發,高頻操作的情況不適應. (兩種情況參照下面的說明)
1、基於request的負載平衡
該種方式下,負載平衡器 (load balancer)會根據各個node的狀況,把每個http request進行分發。使用這樣的均衡策略,就必須在多個node之間複製使用者的session,即時保持整個cluster的使用者狀態同步,這種操作被稱為session複製(session
replication)。Jboss的實現原理是使用攔截器(interceptor),根據使用者的同步策略攔截request,做同步處理後再交給
server產生響應。
該方法的優點是客戶不會被綁定都具體的node,只要還有一個node存活,使用者狀態都不會丟失,cluster都能夠繼續工作。缺點是node之間通訊頻繁,響應速度有影響,多並發、高頻操作的情況下效能下降比較厲害。
2、基於使用者的負載平衡
該種方式下,當使用者發出第一個request後,負載平衡器動態把該使用者指派到某個節點,並記錄該節點的jvm路由,以後該使用者的所有request
都會被綁定這個jvm路由,使用者只會與該server發生互動,這種策略被稱為粘性session(session sticky)。
該方法的優點是響應速度快,多個節點之間無須通訊。缺點也很明顯,某個node死掉以後,它負責的所有使用者都會丟失session。
配置JBoss
- 修改虛擬機器的參數run..conf 檔案中的jvm堆大小,如果是用Sun或HP的JDK需要設定MaxPermSize
set JAVA_OPTS=%JAVA_OPTS% -Xms256m -Xmx1024m -XX:MaxPermSize=128m
2. 應用部署將EJB的jar包和WEB的 war 複製到$JBOSS_HOME/server/default/deplay
(後面這句話,我感覺好象沒有用什麼用的)如果是叢集環境則複製到$JBOSS_HOME/server/default/farm
3. 部署JBOSS叢集服務
用 $JBOSS_HOME/server/all的配置來部署叢集則不需這一步,只有下面1、2兩步是default配置要多做的工作。
(1).將$JBOSS_HOME/server/all/deploy 下的cluster-service.xml和jboss-web-cluster.sar複製到$JBOSS_HOME /server/default/deploy
(2). 將$JBOSS_HOME/server/all/lib 下的jgroups.jar、jbossha.jar、jboss-cache.jar複製到$JBOSS_HOME /server/default/lib
jbossha.jar(載入org.jboss.ha.framework.server.ClusterPartition)
jgroups.jar(JBoss叢集底層通訊協定)
jboss-cache.jar(載入org.jboss.cache.aop.TreeCacheAop)
4. 配置Jboss節點
session複製配置
jboss session複製是jboss session同步的一種實現。原理是在各Jboss節點間建立橫向聯絡,每個節點都將本節點的session變化同步到其他所有節點上。
jboss的session複製與HTTP叢集是相互配合、相互獨立的兩個系統。session複製是節點間的橫向聯絡,HTTP叢集是負載平衡器與節點的縱向聯絡。
$JBOSS_HOME/ server/default/deploy/jboss-web-cluster.sar /META-INF/jboss-service.xml和 $JBOSS_HOME/ server/default/deploy/cluster-service.xml
注意:叢集的各節點需要在同一網段.
兩個檔案都要修改,前者是web session複製的,後者是jboss EJB等叢集
找到<config><udp,將><config>到</config>全部注釋掉. jboss session複製有UDP和TCP兩種方式.UDP採用多播方式,但問題比較多,所以採用TCP方式。
找到<config> <tcp,將><config>到</config>生效.並對該部分進行以下修改:
將全部down_thread和up_thread的false都改為true.
在<tcp bind_addr=” ${jboss.bind.address}”>
在<tcpping initial_hosts=”後填入本機和叢集其他全部Jboss節點的IP[7810],比如<tcpping initial_hosts=” ${jboss.bind.address} [7810],192.168.80.14[7810]”></tcpping>
$JBOSS_HOME/ server/default/deploy/jboss-web-cluster.sar /META-INF/jboss-service.xm中的其他參數
ClusterName是叢集名稱在同一區域網路內,可以存在多個jboss叢集,根據叢集名稱區分它們.所以,叢集中各節點配置的叢集名稱必須一致(實際項目中我配置的叢集名稱是wenshi),而機器IP則沒有特殊要求,只要它們能 相互連通. 理論上,可以在一台機器上安裝多個Jboss執行個體,分屬於不同的叢集.但這會極大地增加複雜度,是不好的配置方式.嚴重不建議給自己找麻煩。
IsolationLevel是隔離等級. 可選值包括:SERIALIZABLE, REPEATABLE_READ, READ_COMMITTED, READ_UNCOMMITTED, 和 NONE。這裡的隔離等級和資料庫的隔離等級有同樣的含義,對於大多數WEB應用程式來講通常設定為REPEATABLE_READ。
CacheMode是緩衝模式。由於session複製是通過緩衝實現的,所以實際上是複製模式.可選值包括:REPL_SYNC 和REPL_ASYNC,確定改變是應該同步還是非同步複製。預設值是REPL_ASYNC.使用同步複製,確保在請求完成之前傳播改變,session同步沒有滯後,但效率低。
如
<config>
<TCP bind_addr="172.16.80.13" start_port="7810" loopback="true"/>
<TCPPING initial_hosts="172.16.80.13[7810],172.16.80.14[7810]" port_range="3" timeout="3500"
num_initial_members="3" up_thread="true" down_thread="true"/>
<MERGE2 min_interval="5000" max_interval="10000"/>
<FD shun="true" timeout="2500" max_tries="5" up_thread="true" down_thread="true" />
<VERIFY_SUSPECT timeout="1500" down_thread="false" up_thread="false" />
<pbcast.NAKACK down_thread="true" up_thread="true" gc_lag="100"
retransmit_timeout="3000"/>
<pbcast.STABLE desired_avg_gossip="20000" down_thread="false" up_thread="false" />
<pbcast.GMS join_timeout="5000" join_retry_timeout="2000" shun="false"
print_local_addr="true" down_thread="true" up_thread="true"/>
<pbcast.STATE_TRANSFER up_thread="true" down_thread="true"/>
</config>
5. 配置應用程式
在應用程式的/%jboss%/server/default/deploy/jboss-web.deployer/ROOT.war/web.xml檔案中的<web-app>段中增加<distributable />。
在jboss-web.xml(這個檔案在哪個目錄,我一直不是很清楚,網上均沒有說明是哪個目錄中的檔案,最後是新產生一個jboss-web.xml,放在與上面的web.xml同目錄下)中增加以下內容:
<?xml version="1.0"?>
<jboss-web>
<context-root>trms</context-root>
<replication-config>
<replication-trigger>SET_AND_NON_PRIMITIVE_GET</replication-trigger>
<replication-granularity>SESSION</replication-granularity>
<replication-field-batch-mode>true</replication-field-batch-mode>
</replication-config>
</jboss-web>
Apache負載平衡配置
(1)下載且安裝。(安裝過程略)
Apache Server
http://httpd.apache.org/download.cgi
Mod_jk
http://www.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/linux/jk-1.2.31/i386/
(2)修改%apache%/conf/httpd.conf
在檔案末尾添加: Include conf/mod_jk.conf
(3) 在%apache%/conf下建立檔案mod_jk.conf
檔案內容如下
# Load mod_jk module
# Specify the filename of the mod_jk lib
LoadModule jk_module modules/mod_jk.so
# Where to find workers.properties
JkWorkersFile conf/workers.properties
# Where to put jk logs
JkLogFile logs/mod_jk.log
# Set the jk log level [debug/error/info]
JkLogLevel info
# Select the log format
JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"
# JkOptions indicates to send SSK KEY SIZE
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
# JkRequestLogFormat
JkRequestLogFormat "%w %V %T"
# Mount your applications
JkMount /* loadbalancer
# You can use external file for mount points.
# It will be checked for updates each 60 seconds.
# The format of the file is: /url=worker
# /examples/*=loadbalancer
JkMountFile conf/uriworkermap.properties
# Add shared memory.
# This directive is present with 1.2.10 and
# later versions of mod_jk, and is needed for
# for load balancing to work properly
JkShmFile logs/jk.shm
# Add jkstatus for managing runtime data
<Location /jkstatus/>
JkMount status
Order deny,allow
Deny from all
Allow from 127.0.0.1
</Location>
其中JkMount /* router的意思是,把所有的請求都發給router處理。可以通過修改url來控制發送某些request。
(4) 在%apache%/conf下建立檔案workers.properties
其內容為:
# Define list of workers that will be used
# for mapping requests
worker.list=loadbalancer,node1,node2
# Define Node1
# modify the host as your host IP or DNS name.
worker.node1.port=8009
worker.node1.host=192.168.80.13
worker.node1.type=ajp13
worker.node1.lbfactor=1
worker.node1.redirect=node2
# Define Node2
# modify the host as your host IP or DNS name.
worker.node2.port=8009
worker.node2.host= 192.168.80.14
worker.node2.type=ajp13
worker.node2.lbfactor=1
worker.node2.redirect=node1
# Load-balancing behaviour
worker.loadbalancer.type=lb
worker.loadbalancer.balance_workers=node1,node2
worker.loadbalancer.sticky_session=0
#worker.list=loadbalancer
# Status worker for managing load balancer
worker.status.type=status
其中對於node的命名規則是worker.節點名.xxxx。所以上述檔案定義了兩個節點:node1和node2。8009連接埠是jboss 預設的ajp連接埠,另外需要注意的是worker.node2.lbfactor參數,它是節點的負載加權,它的值越大,獲得負載的機會就越大。可以根據 node的硬體效能進行調整。worker. router.sticky_session參數是指定是否使用粘性session。配置了http session複製就可以不需要粘性session。上面配置的是不使用粘性session。worker.server1.redirect=為
failover轉移到哪個worker
(5) 配置JBOSS支援粘性mod_jk
修改$JBOSS_HOME/ server/default/deploy/jboss-web.deployer/META-INF/jboss-service.xml
<attribute name="UseJK">false</attribute> 改為ture
(6) 配置JBOSS支援粘性session
修改$JBOSS_HOME/ server/default/deploy/jboss-web.deployer/server.xml
<Engine name="jboss.web" defaultHost="localhost"> 改為
<Engine name="jboss.web" defaultHost="localhost" jvmRoute="server1"
注意:jvmRoute的值必須和mod_jk中的節點名字正確對應,否則無法正確路由
測試檔案
在%JBoss_Home%/server/default/deploy/jboss-web.deployer/ROOT.war/目錄下添加一個新檔案夾/test,並在裡面添加如下3個jsp檔案:
index.jsp
<%@ page contentType="text/html;charset=ISO-8859-1" %>
<html>
<head>
<title>Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body onload="document.form.name.focus()">
<br><br><br>
<center>
The host is : <%=java.net.InetAddress.getLocalHost().toString()%><br>
Your session id is : <%=session.getId()%><br>
Your session detail is : <%=session.toString()%><br>
Your session context is : <%=session.getSessionContext()%><br><br>
Please input your name:<br>
<form action="test_action.jsp" method="POST" name="form">
<input type="input" name="name"/>
<input type="submit" value="submit">
</form>
</center>
</body>
</html>
test_action.jsp
<%@ page contentType="text/html;charset=ISO-8859-1" %>
<html>
<head>
<title>Test Action</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<%
String name = request.getParameter("name");
session.setAttribute("name",name);
String host = java.net.InetAddress.getLocalHost().toString();
%>
<body>
<br>
<br>
<center>
The host is : <%=host%><br><br>
Your session id is : <%=session.getId()%><br>
Your session detail is : <%=session.toString()%><br>
Your session context is : <%=session.getSessionContext()%><br><br>
Your name is : <%=name%><br>
This name is set into the session.<br>
Please click <a href="session.jsp">here</a> to check the session valid or not.
</center>
</body>
</html>
session.jsp
<%@ page contentType="text/html;charset=ISO-8859-1" %>
<html>
<head>
<title>Test Action</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<%
String name = null;
if(session.getAttribute("name")!=null)
name = (String)session.getAttribute("name");
String host = java.net.InetAddress.getLocalHost().toString();
%>
<body>
<br>
<br>
<center>
The host is : <%=host%><br>
Your session id is : <%=session.getId()%><br>
Your session detail is : <%=session.toString()%><br>
Your session context is : <%=session.getSessionContext()%><br><br>
<%
if(name!=null){
out.print("Your name is "+name+"<br>");
out.print("The session is valid.");
}
else{
out.print("The session is invalid!!!");
}
%>
<a href="index.jsp">Return!</a>
</center>
</body>
<%
if(session.getAttribute("name")!=null)
session.invalidate();
%>
</html>
訪問方式: http://192.168.80.14/test/index.jsp