集群环境中,分布式状态管理是集群必须提供的一个关键服务。比如一个有状态的会话Bean应用程序,会话状态必须在所有节点的Bean实例中保持同步,因此不管哪个节点服务请求,客户端应该得到同样的会话状态。实体Bean应用程序中,Bean实例必须在集群中进行缓存以减轻数据库负载。目前JBoss AS中的状态复制和分布式缓存由两种方式提供: HASessionState MBean 和 JBoss Cache 框架。
HTTP会话复制用来在你的WEB客户端和集群的节点之间复制状态,那么当某一个节点崩溃时,集群中的其他节点可以恢复。要实现这个目的,必须执行两个步骤:
如果使用all配置集启动JBoss,缺省会启用会话状态复制功能,只需要布署你的应用程序,会话状态就已经可以在集群中进行复制。
但是负载平衡需要额外的软件支持,作为一个常用的场景,我们将描述如何设置Apache和mod_jk。这个功能也可以通过专门的硬件或者其他软件来实现。
注意
一个负载平衡器跟踪HTTP请求,根据和请求关联的会话,决定派发请求到合适的节点。这被称作使用sticky-sessions的负载平衡器:一
旦在某个节点创建了会话,随后的请求将由同一个节点进行处理。使用一个支持sticky-sessions的负载平衡器可以不用复制会话,没有会话状态复
制的花销,每个请求将是被同一个节点处理。但是一旦这个节点崩溃,保存在这个节点的所有客户会话都将丢失,客户必须登录进另一个节点,重新启动一个新的会
话。在某些情况,不复制HTTP会话是可以接受的,因为比较关键的状态都保存在数据库中,但是也有些情况下,丢失会话状态是无法接受的,这种情况下,会话
状态复制的花销是必须承受的。
Apache
是一个可以通过插入模块扩展功能的WEB服务器,mod_jk(最新的mod_jk2)模块的主要功能就是允许从Apache分发请求到一个
Servlet容器,还可以支持在多个Servlet容器中负载平衡HTTP调用(遵循sticky-sessions)。
更改APACHE_HOME/conf/httpd.conf 文件,在文件尾部添加一行:
java 代码
- # Include mod_jk's specific configuration file
- Include conf/mod-jk.conf
创建文件APACHE_HOME/conf/mod-jk.conf:
java 代码
- # 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 /application/* 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
-
- JkMount status
- Order deny,allow
- Deny from all
- Allow from 127.0.0.1
-
其中有两个设置是非常重要的:
除了使用 JkMount 指令,也可以使用JkMountFile 指令指定一个配置文件,包含多个URL映射配置。你只需要在APACHE_HOME/conf 目录下创建一个uriworkermap.properties 文件。文件格式为/url=worker_name,示例文件如下:
java 代码
- # Simple worker configuration file
-
- # Mount the Servlet context to the ajp13 worker
- /jmx-console=loadbalancer
- /jmx-console/*=loadbalancer
- /web-console=loadbalancer
- /web-console/*=loadbalancer
如上示例将配置 mod_jk 派发请求 /jmx-console 和/web-console 给Tomcat。
接着需要创建conf/workers.properties文件,这个文件用来指定名个不同的Servlet容器的位置以及调用如何在这些容器之间进行平衡。针对一个两个节点的设置,示例文件如下:
java 代码
- # Define list of workers that will be used
- # for mapping requests
- worker.list=loadbalancer,status
-
- # Define Node1
- # modify the host as your host IP or DNS name.
- worker.node1.port=8009
- worker.node1.host=node1.mydomain.com
- worker.node1.type=ajp13
- worker.node1.lbfactor=1
- worker.node1.cachesize=10
-
- # Define Node2
- # modify the host as your host IP or DNS name.
- worker.node2.port=8009
- worker.node2.host= node2.mydomain.com
- worker.node2.type=ajp13
- worker.node2.lbfactor=1
- worker.node2.cachesize=10
-
- # Load-balancing behaviour
- worker.loadbalancer.type=lb
- worker.loadbalancer.balance_workers=node1,node2
- worker.loadbalancer.sticky_session=1
- #worker.list=loadbalancer
-
- # Status worker for managing load balancer
- worker.status.type=status
上述文件配置mod_jk执行Round-Robin的基于sticky-sessions的负载平衡策略,两个节点都监听8009端口。
works.properties文件中,每个节点使用worker.XXX 命名规范进行定义,这里 XXX 可以是任何名字,用来命名Servlet容器。对于每个工作者,必须指定在目标Servlet容器中配置的AJP13连接器的IP地址和端口号。
lbfactor 属性用来表示负载平衡权重,决定节点之间的优先级,值越大,该节点将接受越多的请求。这个选项可以用来分配不同的节点不同的负载。
cachesize 属性定义关联的Servlet容器的线程池大小,确定这个值没有超过Servlet容器在AJP13连接器中的配置。可以参考jakarta.apache.org/tomcat/connectors-doc/config/workers.html
conf/workers.properties 最后定义负载平衡工作者,唯一需要改变的是worker.loadbalancer.balanced_workers 设置,必须列出刚才定义的所有工作者。
sticky_session 属性指定集群针对HTTP会话的处理方式,如果指定worker.loadbalancer.sticky_session=0,每个请求将在两个节点中进行负载平衡。但是当一个用户在某个节点创建一个会话时,比较好的方式是随后的请求都导向这个节点,这被称作"sticky session",由于客户总是使用会话创建的节点服务所有随后的请求。否则用户的会话数据必须在两个节点进行同步,要启用"sticky session",必须设置 worker.loadbalancer.sticky_session 值为1。
注意
A non-loadbalanced setup with a single node required the worker.list=node1 entry before mod_jk would function correctly.
最后必须配置集群中所有节点的JBoss Tomcat实例。
在每个节点中,我们必须根据workers.properties文件中的worker名称来命名节点。比如,在JBoss 实例node1中,需要编辑JBOSS_HOME/server/all/deploy/jbossweb-tomcat50.sar/server.xml 文件,定位 元素添加一个jvmRoute属性:
xml 代码
- <Engine name="jboss.web" defaultHost="localhost" jvmRoute="node1">
- ... ...
- Engine>
然后,对于集群中的每一个节点,我们必须通知它添加一个jvmRoute值到会话cookies中,以便mod_jk可以路由随后的请求。编辑 JBOSS_HOME/server/all/deploy/jbossweb-tomcat50.sar/META-INF/jboss-service.xml 文件,定义UseJK的元素,设置值为true:
xml 代码
- <attribute name="UseJK">trueattribute>
到此为止,我们已经成功设置Apache_mod_jk使用sticky-session方式的负载平衡。
目前为止,已经成功配置了sticky-session方式的负载平衡,但是这种方式并不是一个完善的解决方案,因为一个节点崩溃,所有的会话数据都会丢失。更可选的解决方式是在集群的节点之间复制会话状态,这样客户访问任一个节点都会获得同样的会话状态。
jboss.cache:service=TomcatClusteringCache MBean 使用 JBoss Cache提供HTTP会话状态复制服务,这个MBean定义在deploy/tc5-cluster.sar/META-INF/jboss-service.xml 文件中。
注意
JBoss AS 4.0.4 CR2以前的版本,HTTP会话缓存配置文件在deploy/tc5-cluster-service.xml文件中。
下面是一个典型的deploy/tc5-cluster.sar/META-INF/jboss-service.xml 文件
xml 代码
- <mbean code="org.jboss.cache.aop.TreeCacheAop"
- name="jboss.cache:service=TomcatClusteringCache">
-
- <depends>jboss:service=Namingdepends>
- <depends>jboss:service=TransactionManagerdepends>
- <depends>jboss.aop:service=AspectDeployerdepends>
-
- <attribute name="TransactionManagerLookupClass">
- org.jboss.cache.BatchModeTransactionManagerLookup
- attribute>
-
- <attribute name="IsolationLevel">REPEATABLE_READattribute>
-
- <attribute name="CacheMode">REPL_ASYNCattribute>
-
- <attribute name="ClusterName">
- Tomcat-${jboss.partition.name:Cluster}
- attribute>
-
- <attribute name="UseMarshalling">falseattribute>
-
- <attribute name="InactiveOnStartup">falseattribute>
-
- <attribute name="ClusterConfig">
- ... ...
- attribute>
-
- <attribute name="LockAcquisitionTimeout">15000attribute>
- mbean>
详细的配置请参见JBossCache缓存配置部分,下面讨论几个比较关键的配置:
-
TransactionManagerLookupClass 设置事务管理器工厂,缺省值为org.jboss.cache.BatchModeTransactionManagerLookup,这个类通知缓存不要参与JTA事务,自己管理事务。
-
IsolationLevel 设置更新分布式缓存的隔离级别,可选值包括:SERIALIZABLE, REPEATABLE_READ, READ_COMMITTED, READ_UNCOMMITTED, 和 NONE。 这里的隔离级别和数据库的隔离级别有同样的含义,对于大多数WEB应用程序来讲通常设置为REPEATABLE_READ。
-
CacheMode 控制缓存如何被复制。可选值包括:REPL_SYNC 和REPL_ASYNC,确定改变是应该同步还是异步复制。使用同步复制,确保在请求完成之前传播改变,同步复制相对来说会慢一些。
-
ClusterName 指定集群名称。缺省的集群名称是由当前的JBoss分区名称加上"Tomcat-"前缀。所有的节点应该使用相同的集群名称。
-
UseMarshalling 和InactiveOnStartup 属性必须有相同的值,如果使用FIELD级别的会话复制,这两个值必须为true。
-
ClusterConfig 配置底层的JGoups 堆栈。最重要的配置元素是广播地址和端口号mcast_addr 和mcast_port,详细配置请参见JGroups配置。
-
LockAcquisitionTimeout 设置获取锁的最大超时值,缺省值为15000。
-
UseReplQueue 使用异步复制时是否启动复制队列,这允许多个更新一起执行从而提升性能。复制队列属性由 ReplQueueInterval 和ReplQueueMaxElements 属性配置。
-
ReplQueueInterval 配置JBoss缓存处理复制队列的时间间隔。
-
ReplQueueMaxElements: 配置复制队列可以保存的最多的元素数目。
要在你的WEB应用程序中启用集群,必须在web.xml文件中声明distributable。示例如下:
可以在jboss-web.xml文件中进一步配置会话复制,示例如下:
xml 代码
- <jboss-web>
- <replication-config>
- <replication-trigger>SET_AND_NON_PRIMITIVE_GETreplication-trigger>
- <replication-granularity>SESSIONreplication-granularity>
- <replication-field-batch-mode>truereplication-field-batch-mode>
- replication-config>
- jboss-web>
replication-trigger 元素确定什么情况触发一次会话复制,有四个选项:
-
SET: 使用此选项,一个会话只有在设置属性时才被认为需要复制。如果你的应用总是需要改变会话的属性,这个选项将是性能最优的。如果一个对象只是被检索和修改,但是不需要改写会话,这个对象的改变并不会造成会话复制。
-
SET_AND_GET: 使用此选项,任何属性的获取和设置都被认为需要复制。如果一个对象被检索和修改,但是不需要改定会话,这个对象的改变将会被复制。这个选项有显著的性能问题。
-
SET_AND_NON_PRIMITIVE_GET: 此选项类似于 SET_AND_GET,唯一的例外是只有非原始类型的检索操作被认为需要复制。比如,HTTP会话请求可以从属性中检索一个非原始类型的对象实例并更改,如果我们没有指定此选项,更改将不会被复制。这是缺省设置。
-
ACCESS: 使用此选项,只要会话被访问都被认为需要复制。由于每个HTTP请求都会造成会话被访问,所以每个请求都会导致会话复制。使用此选项,会话时间戳将在集群中同步。注意使用此选项会有显著的性能问题。
replication-granularity 元素控制复制粒度,支持的选项包括:
-
SESSION: 基于会话的复制,只要会话发生改变,整个会话对象都会被序列化。
-
ATTRIBUTE: 基于属性的复制,复制只在属性被认为是脏的时候发生,比如lastAccessTime.。对于持有大量数据的会话,这个选项可以提升性能。
-
FIELD: 基于字段的复制,复制只在会话属性包含的对象的字段发生变化时发生。
replication-field-batch-mode 表示是否需要在每个HTTP请求进行批量更新,缺省值为true。
如果你的会话通常比较小,SESSION选项比较适合,如果会话比较大,而且某些部分不是经常被访问,ATTRIBUTE 选项比较适合,如果会话属性包含大数据量的对象而且只有字段经常更改,FIELD 选项比较适合。
FIELD-level replication only replicates modified data fields inside
objects stored in the session. It could potentially drastically reduce
the data traffic between clustered nodes, and hence improve the
performance of the whole cluster. To use FIELD-level replication, you
have to first prepare your Java class to indicate which fields are to
be replicated. This is done via JDK 1.4 style annotations embedded in
JavaDocs:
To annotate your POJO, we provide two annotations: @@org.jboss.web.tomcat.tc5.session.AopMarker and @@org.jboss.web.tomcat.tc5.session.InstanceAopMarker. When you annotate your class with AopMarker, you indicate that instances of this class will be used in FIELD-level replication. For exmaple,
/*
* My usual comments here first.
* @@org.jboss.web.tomcat.tc5.session.AopMarker
*/
public class Address
{
...
}
If you annotate it with InstanceAopMarker instead, then all of its sub-class will be automatically annotated as well. For example,
/*
*
* @@org.jboss.web.tomcat.tc5.session.InstanceOfAopMarker
*/
public class Person
{
...
}
then when you have a sub-class like
public class Student extends Person
{
...
}
there will be no need to annotate Student. It will be annotated automatically because it is a sub-class of Person.
However, since we only support JDK 1.4 style annotation (provided by JBoss Aop) now, you will need to perform a pre-processing step. You need to use the JBoss AOP pre-compiler annotationc and post-compiler aopc
to process the above source code before and after they are compiled by
the Java compiler. Here is an example on how to invoke those commands
from command line.
$ annotationc [classpath] [source files or directories]
$ javac -cp [classpath] [source files or directories]
$ aopc [classpath] [class files or directories]
Please see the JBoss AOP documentation for the usage of the pre- and post-compiler. The JBoss
AOP project also provides easy to use ANT tasks to help integrate those
steps into your application build process. In the next AS release, JDK
5.0 annotation support will be provided for greater transparency. But
for now, it is important that you perform the pre- and post-compilation
steps for your source code.
Note
Or, you can see a complete example on how to build, deploy, and
validate a FIELD-level replicated web application from this page: http://wiki.jboss.org/wiki/Wiki.jsp?page=Http_session_field_level_example. The example bundles the pre- and post-compile tools so you do not need to download JBoss AOP separately.
When you deploy the web application into JBoss AS, make sure that the following configurations are correct:
-
In the server's deploy/tc5-cluster.sar/META-INF/jboss-service.xml file, the inactiveOnStartup and useMarshalling attributes must both be true.
-
In the application's jboss-web.xml file, the replication-granularity attribute must be FIELD.
Finally, let's see an example on how to use FIELD-level replication on those data classes. Notice that there is no need to call session.setAttribute() after you make changes to the data object, and all changes to the fields are automatically replicated across the cluster.
<pre class="programlistin