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 的安装目录为%jboss%,apache安装目录为%apache%。

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都能够继续工作。缺点是node之间通信频繁,响应速度有影响,多并发、高频操作的情况下性能下降比较厉害。

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

JBoss 4.0.2集群指南_负载均衡

任何节点的关闭与启动都会在cluster中广播,比如加如一个新节点后,其他节点会得到以下提示:

JBoss 4.0.2集群指南_apache_02

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。如下图:


JBoss 4.0.2集群指南_xml_03

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的其它节点,并且被自动部署,你会看到如下部署信息:


JBoss 4.0.2集群指南_apache_04

  上面显示通过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万左右的消息数量,无一丢失,应该说还算比较可靠。响应时间也还过的去,在简单的网络环境下,能够应付比较高的并发。

为人:谦逊、激情、博学、审问、慎思、明辨、 笃行

学问:纸上得来终觉浅,绝知此事要躬行

为事:工欲善其事,必先利其器。

态度:道阻且长,行则将至;行而不辍,未来可期

转载请标注出处!