JBoss 4.0.2叢集指南
JBoss 4.0.2叢集指南
本文主要講解JBoss
cluster的基本知識以及簡單的配置方法,其間涉及了一些Jboss的補充知識。
一、材料準備:
1、 JBoss 4.0.2
JBoss各個版本之間差異比較大,即使同為JBoss4.x的版本,內部元件的版本也不一致,所以請儘量使用同一版本的server。目前已經證明可以配置cluster的版本多為JBoss 3.2.6和JBoss 4.0.2。
2、 Apache 2.0.54
3、 Apache
mod_jk-1-2-13-apache-2-0-54
二、安裝:
1、 jboss4.0.2與apache
2.0.54的安裝請自行搞定。假設JBoss
2、 mod_jk的安裝。
從apache.org獲得檔案mod_jk-1-2-13-apache-2-0-54.so,將該檔案拷貝到%apache%\modules。
三、JBoss cluster入門
Jboss支援如下型別的cluster:EJB、web、JNDI、JMS,我們主要了解webcluster。Webcluster實際上可以劃分為兩個話題:負載均衡 (load
balance) 和狀態同步。它們是互相獨立的,單獨配置。
負載均衡的概念比較簡單,重要的是負載均衡的粒度。可以選擇針對每個request的均衡,或者是針對每個使用者的均衡。選擇不同的粒度,需要不同的狀態同步方式。
1、基於request的負載均衡
該種方式下,負載均衡器(loadbalancer)會根據各個node的狀況,把每個httprequest進行分發。使用這樣的均衡策略,就必須在多個node之間複製使用者的session,實時保持整個cluster的使用者狀態同步,這種操作被稱為session複製(sessionreplication)。Jboss的實現原理是使用攔截器(interceptor),根據使用者的同步策略攔截request,做同步處理後再交給server產生響應。
該方法的優點是客戶不會被繫結都具體的node,只要還有一個node存活,使用者狀態都不會丟失,cluster都能夠繼續工作。缺點是
2、 基於使用者的負載均衡
該種方式下,當用戶發出第一個request後,負載均衡器動態的把該使用者分配到某個節點,並記錄該節點的jvm路由,以後該使用者的所有request都會被繫結這個jvm路由,使用者只會與該server發生互動,這種策略被稱為粘性session(sessionsticky)。該方法的優點是響應速度快,多個節點之間無須通訊。缺點也很明顯,某個node死掉以後,它負責的所有使用者都會丟失session。
四、實戰
1、負載均衡
Jboss的負載均衡目前有兩種方案,一是使用apache的mod_jk,二是使用JBoss自帶的負載均衡模組。下面分別講解這兩種配置。
mod_jk的配置
1、 請確認%apache%\modules下已經有mod_jk-1-2-13-apache-2-0-54.so檔案。
2、 修改%apache%\conf\httpd.conf在檔案末尾新增:
Include conf/mod_jk2.conf |
3、 在%apache%\conf下新建檔案mod_jk2.conf檔案內容如下:
# Load mod_jk module. Specify the filename # of the mod_jk lib you’ve downloaded and # installed in the previous section LoadModule jk_module modules/mod_jk-1-2-13-apache-2-0-54.so # Where to find workers.properties JkWorkersFile conf/workers2.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 indicate to send SSL KEY SIZE, JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories # JkRequestLogFormat set the request format JkRequestLogFormat "%w %V %T" JkMount /* loadbalancer |
其中
JkMount /* loadbalancer |
的意思是,把所有的請求都發給loadbalancer處理。可以通過修改url來控制傳送某些request。
4、 在 %apache%\conf下新建檔案workers2.properties其內容為:
worker.list=loadbalancer,server1,server2 # Define the first node... worker.server1.port=8009 worker.server1.host=172.16.0.116 worker.server1.type=ajp13 worker.server1.lbfactor=1 worker.server1.local_worker=1 worker.server1.cachesize=10
# Define the first node... worker.server2.port=8009 worker.server2.host=172.16.32.88 worker.server2.type=ajp13 worker.server2.lbfactor=1 worker.server2.local_worker=1 worker.server2.cachesize=10
# Now we define the load-balancing behaviour worker.loadbalancer.type=lb worker.loadbalancer.balanced_workers=server1,server2 worker.loadbalancer.sticky_session=1 |
其中對於node的命名規則是worker.節點名.xxxx。
所以上述檔案定義了兩個節點:server1和server2。8009埠是jboss預設的ajp埠,另外需要注意的是worker.server2.lbfactor引數,它是節點的負載加權,它的值越大,獲得負載的機會就越大。可以根據node的硬體效能進行調整。worker.loadbalancer.sticky_session引數是指定是否使用粘性session。所有需要負載均衡的節點,都必須在worker.loadbalancer.balanced_workers引數中列舉出來。請記住所有node的名稱和它對應著哪臺機器,後面的配置中會使用。嘗試啟動apache:%apache\bin\apache.exe,正常情況下沒有任何提示。如果你使用的jk是2.0的,那麼配置檔案的寫法完全不同,由於mod_jk2已經停止開發,所以apache並沒有提供任何講解,對於配置檔案的編寫也沒有任何指導。 Jboss自帶均衡器的配置
將資料夾%jboss%\docs\examples\varia\loadbalancer\loadbalancer.sar拷貝到%jboss%\server\all\d eploy下,並且修改loadbalancer.sar\loadbalancer.sar\META-INF\jboss-service.xml,在
<host> |
標籤中類出所有節點,在
<sticky-session> |
標籤中指定是否使用粘性session。配置完成。該均衡器的缺點是負載能力相對不高,配置引數太少,比如無法指定不同節點的負載加權,所以後面都以mod_jk為例,不再講解jboss自帶的負載均衡器的內容。負載均衡的配置基本完成,啟動jboss,其中過程中會列出DefaultPatition中所有的節點:
run.bat
-c all
任何節點的關閉與啟動都會在cluster中廣播,比如加如一個新節點後,其他節點會得到以下提示:
2、session sticky配置
apache應該會以粘性session的方式分發請求。部署一個應用測試一下,你會發現粘性session沒有起作用。因為我們還沒有給jboss配置jvm路由(jvmRoute),apache就無法知道究竟哪些session是屬於哪個節點的。我們繼續往下:修改server1機器上的jboss的配置檔案:%jboss%\server\all\deploy\jbossweb-tomcat55.sar\ META-INF\
jboss-service.xml在110行有:
<attribute name="UseJK">false</attribute> |
,將它改為true。值得注意的是在這行標籤上面有一段註釋,要求你在server.xml中必須有:Enginename="jboss.web"jmvRoute="Node1"defaultHost="localhost"請注意這裡有一個氣死人不償命的小bug,jboss的官方文件把jvmRoute寫成了jmvRoute,就是v和m兩個字母的顛倒讓我鬱悶了三天,翻遍了jboss.com和theserverside.com。都是直接拷貝的錯,吐血吐到脫水啊。
下面需要修改server1上的%jboss%\server\all\deploy\jbossweb-tomcat55.sar\
server.xml,在32行左右有:
<Engine name="jboss.web" defaultHost="localhost"> |
給它增加一個jvmRoute屬性:
<Engine jvmRoute="server1" name="jboss.web"defaultHost="localhost"> |
請注意,jvmRoute的值必須和mod_jk中的節點名字正確對應,否則無法正確路由。Cluster中的所有節點都應該做相應的配置。Jboss的配置完成了,下面需要在你的web應用中修改配置檔案,讓它支援叢集。在WEB-INF\web.xml中加入屬性:
<distributable/> |
Ok,基於使用者的cluster完成了,每個使用者會繫結都某個節點上進行互動。這種繫結是如何完成的呢?原
來apache把客戶分發到節點後,該節點會在使用者的sessionid後面加上此節點的路由名稱,變成這個樣子:Efdfxxd98daja87daj76da2dka**,server1有了這個標誌,就能分辨該session屬於哪個節點。
3、session replication配置
下面要做的是基於request的cluster,也就讓各個節點之間互相複製session狀態。有兩種複製模式,同步與非同步。使用同步的方式,jboss會把session複製的操作和對request的響應放到一個應用事務(applicationtransaction),session複製完成後才去處理request。
非同步複製則傳送session複製的訊息後馬上處理request,session複製則會稍有延遲。但是在多框架的web頁面中,這樣的叢集方式會有問題。由於frame在同一時間發出多個request,會造成一些混亂,這也是採用基於使用者的叢集方式的原因之一。
JBoss4.0.2中採用了Jbosscache來實現session複製,實際上就是一個分散式快取,由於sessionid中包含了jvmroute,所以能夠分辨session屬於哪個節點。Session的更新類似於hibernate中的樂觀鎖,有了更新之後就讓session的版本號增加,其他節點通過對比版本號來決定是否同步session狀態。配置session
replication首先需要編輯%jboss%server\all\deploy\jbossweb-tomcat55.sar\META-INF\
jboss-service.xml,88行左右有:
<attribute name="SnapshotMode">instant</attribute> |
這就是剛才提到的複製模式,instant為立即複製,如果設為interval那麼系統會在延遲一段時間再進行復制,時間長度在
<attribute name="SnapshotInterval">2000</attribute> |
中指定,單位是毫秒。單獨配置這一個地方還不夠,在%jboss%server\all\deploy\ tc5-cluster-service.xml中有:
<attribute name="CacheMode">REPL_ASYNC</attribute> |
這裡才真正決定複製是同步的還是非同步的,可以指定為REPL_ASYNC(非同步)或者REPL_SYNC(同步)。在這個檔案下面一點,還有一個config標籤,裡面指定了各個節點在進行session複製的時候如何通訊,有udp和tcp兩種可選,如果使用udp方式,那麼應該將udp的lookback屬性指定為true,因為windows上有一個叫做mediasense的東西會影響udpmulticast。注意如果你不瞭解multiaddress的ip規則,請不要隨便修改mcast_addr的值。如果採用tcp方式的話,應該指定bind_addr的值為本機ip,並且在TCPPING標籤的initial_hosts屬性中列出所有節點,格式是”機器名[埠號]”,比如在我們的例子中,就應該這樣配置tcp(以其中一個節點為例):
<config> <TCP bind_addr="172.16.0.116" start_port="7810" loopback="true"/> <TCPPING initial_hosts="172.16.0.116[7810],172.16.32.88[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> |
JBoss的clustering版主建議儘量使用udp。不過在Sobey內部,建議使用tcp方式,經測試可能有不明物體在影響udp通訊,導致Timeout異常。
在%jboss%\server\all\deploy\cluster-service.xml中也有關於udp和tcp的配置資訊,在4.0以前版本的JBoss中,會以這個檔案為主配置,4.0以後都以tc5-cluster-service.xml為主配置。
Jboss的配置完成了,最後需要在web應用中增加配置資訊,控制session複製的粒度。在WEB-INF\
jboss-web.xml中增加以下內容:
<replication-config> <replication-trigger>SET_AND_NON_PRIMITIVE_GET </replication-trigger> <replication-granularity>SESSION </replication-granularity> </replication-config>
|
其中replication-trigger是指定哪些操作引發session的版本更新,它的取值有:
SET_AND_GET SET_AND_NON_PRIMITIVE_GET SET |
replication-granularity是複製粒度,可以取session或attribute。如果取為attribute有可能導致複製失敗,這是目前版本的jbosscache的一個bug,等待修正。部署專案,測試,如果配置沒有問題,可以在%jboss%\0server\all\log\server.log中發現類似於這樣的資訊:
DEBUG [org.jboss.web.tomcat.tc5.session.JBossCacheManager] check to see if needs to store and replicate session with id Im9-qpuaXppMS+xXwE3M+Q**.server1 DEBUG [org.jboss.web.tomcat.tc5.session.ClusteredSession] processSessionRepl(): session is dirty. Will increment version from: 20 and replicate. |
Session replication配置的成功率比較低,情況也很複雜,請仔細操作。
五、分散式熱部署(distributable hot deploy)
在一個節點很多的cluster中,如果部署應用的時候必須把程式檔案拷貝到每個機器上的話,那實在太愚蠢了,幸好通過all啟動的jboss自動支援分散式熱部署。把支援cluster的應用(通常需要打包成war檔案),放到%jboss%\server\all\farm下,那麼處於同一cluster中的其他節點會自動下載並且部署,jbo ss把這個稱為Farm
deploy。如下圖:
Jms叢集的意義在於提升系統在處理訊息時的併發能力,建立這樣的叢集,有三個步驟:
1、 配置jms訊息持久化所使用的資料庫
2、 配置分散式的jndi環境
3、 配置分散式jms
在jboss 4.0.2中,系統採用hibernate的方式來儲存訊息,所以能夠相容hibernate支援的所有資料庫。Jboss預設採用hsql,在我們的例子中,將使用oracle
9.2。首先需要配置連線到資料庫的jndi資料來源。方法是把doc\examples\jca下的oracle-ds.xml檔案拷貝到server\all\farm下,並且修改其中的引數,保證資料庫能夠正確連線。Cluster啟動後,該檔案能夠通過jboss的farm服務,自動拷貝到其他叢集節點,並且自動部署。假設jndi資料來源的名稱為:GlobalDS
將doc\examples\jms下的oracle-jdbc3-service.xml檔案拷貝到server\all\deploy-hasingleton\jms目錄下,並且刪除該目錄下的hsqldb-jdbc2-service.xml。修改oracle-jdbc3-service.xml,在56行左右指定name的值為資料來源的名字:GlobalDS。這樣系統會使用該資料來源來儲存jms訊息。使用如下命令啟動boss: run ?c
all
啟動完成後,正常情況下會發現oracle資料庫中多出了三張表:
1、Jms_message_log 該表用於儲存所有未處理的點對點訊息,表結構是:
Messageid 訊息id
Destination 目的地
Txid 事務id
Txop 訊息操作型別(a為新增,d為刪除)
Messageblob 訊息內容
2、JMS_REFERENCE_LOG 用於儲存所有未處理的topic訊息,表結構是:
Messageid
Destination
Txid
Txop
Messageblob
Redelivered 訊息是否被重發
3、JMS_TRANSACTION_LOG 用於儲存處理訊息過程中的一些重要的事務
需要注意的是,jboss 3.2之後就不在支援以檔案形式儲存訊息,雖然這樣最會比資料庫操作快一倍以上。Jboss官方的解釋是,使用檔案會讓系統不可靠。
客戶端在傳送jms訊息的時候,首先需要向app server查詢jndi,在jboss
cluster中,jndi是作為一個分散式的singleton出現的。每個節點除了有自己的jndi環境以外,整個cluster還具有一些全域性的jndi,客戶端在進行jndi查詢的時候,只需要向這個全域性的jndi進行查詢,cluster如果在全域性jndi中找不到對應的jndi物件,就會按次序向每個節點詢問,看他們的本地jndi中是否有匹配的物件,如果有則返回給客戶,如果所有的節點都沒有,則丟擲異常。所有以all方式啟動的jboss,都會開啟1100埠,這個埠是全域性jndi的入口,所有節點都是如此。
分散式的jndi有的節點有主次的區別,第一個啟動的jboss是主伺服器,它會儲存所有的全域性jndi,其他的節點如果收到客戶查詢jndi的請求後,都會向主伺服器請求資料。如果主伺服器不幸down掉,那麼次節點會發現這個變化,然後啟動自己的jndi環境,取代主伺服器提供服務。
下面是配置jms的jndi,開啟server\all\deploy-hasingleton\jms下的jbossmq-destinations-service.xml檔案,增加一個名為test的destination,如下:
<mbean code="org.jboss.mq.server.jmx.Queue"
name="jboss.mq.destination:service=Queue,name=test">
<depends
optional-attribute-name="DestinationManager">jboss.mq:service=DestinationManager</depends>
</mbean>
為了預防主伺服器down了之後丟失該jndi,所以最好在每個節點都進行這個配置。
在jboss 4.0.2的預設配置下,是不支援訊息bean的叢集的,要達到這個目的,必須下載一個jar包才能實現,可以從這裡獲得:
http://blog.yam.com/bromon/archives/489460.html
得到這個jar檔案後,將它命名為cdot-jbossx.jar
檔案放到server\all\deploy\jms下。下面編寫訊息bean,它的功能很簡單,接收到來自test佇列的訊息後,列印訊息id。
public class TestJmsBean
implements MessageDrivenBean, MessageListener {
MessageDrivenContext messageDrivenContext;
public void ejbCreate() {
System.out.println("訊息bean建立");
}
public void ejbRemove() {
}
public void onMessage(Message msg) {
try
{
System.out.println(msg.getJMSMessageID());
}catch(Exception e)
{
e.printStackTrace();
}
}
public void setMessageDrivenContext(MessageDrivenContext
messageDrivenContext) {
this.messageDrivenContext = messageDrivenContext;
}
}
把這個訊息bean部署到server\all\farm目錄下,它會被自動拷貝到cluster的其它節點,並且被自動部署,你會看到如下部署資訊:
上面顯示通過farm的方式,部署了一個名為GlobalDS的連線池,以及一個名為TestJms的訊息bean。
下面寫個客戶端來測試一下:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd
HH:mm:ss");
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
p.put(Context.URL_PKG_PREFIXES,
"jboss.naming:org.jnp.interfaces");
p.put(Context.PROVIDER_URL, "172.16.0.116:1100"); // 全域性jndi入口
InitialContext ctx = new InitialContext(p);
QueueConnectionFactory qcf = (QueueConnectionFactory)
ctx.lookup(
"ConnectionFactory");
QueueConnection conn =
qcf.createQueueConnection();
Queue q = (Queue) ctx.lookup("queue/test");//查詢名為test的destination
QueueSession session =
conn.createQueueSession(false,
QueueSession.AUTO_ACKNOWLEDGE);
conn.start();
QueueSender sender = session.createSender(q);
for (int i = 0; i < 10000; i++) {
TextMessage tm = session.createTextMessage(sdf.format(new
Date()));
sender.send(tm, DeliveryMode.PERSISTENT, 4, 0);//傳送持久化訊息
System.out.print("第" +
i);
}
conn.stop();
session.close();
conn.close();
執行一下,可以看到每個節點都建立了若干個訊息bean,同時在處理訊息,任意關閉一個次伺服器,系統會自動fail over。檢視Jms_message_log資料表,裡面沒有任何資料,表示所有的訊息都已經被處理。
Jboss的jms cluster功能與websphere mq比較起來,是非常簡陋的,可以配置的地方也很少,畢竟是免費的東西。Jboss的論壇上透露,在jboss
6.0中將會有全新的jboss
messaging服務,不知要等到何年何月。針對這個cluster,我做過簡單的測試,800萬左右的訊息數量,無一丟失,應該說還算比較可靠。響應時間也還過的去,在簡單的網路環境下,能夠應付比較高的併發。