因为公司需要做apache+jboss的负载均衡和集群方面的要求,但阿堂原来没有这方面的经验,便开始了两天的拓荒之旅,其间的痛苦就不用说了,因 为网上的文章,基本上大同小异,估计很多都是互相转载的,作了一些变动,都基本上有些错务,加上网上介绍的jboss版本都基本与我的的版本不一致,造成 网上提到的jboss目录,我这里有可能没有,这样就更增加了一些麻烦,而向网上一些哥们寻找帮助的过程中,有的要么没做过,有的就只是做了 apaceh+tomcat的负载均衡,这样就增加阿堂解决问题的难度!最后总算解决了!先不说最终的实施效果是否如期望的那样,但这个方案能正常实施, 可以说成功了90%了,至于apache等的调优等,就另当别论了!阿堂就不想在此说了!有感于自己配置的麻烦,就写了这篇总结文章和朋友们来一起分享 了,希望能对其它网友有所帮助或借签,阿堂就很开心了!
布署环境
(1)两台Linux服务器 192.168.80.13192.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上
(4)Mod_jk版本1.2.3 (版本是 mod_jk-1.2.31-httpd-2.2.x.so,可从官方网站下载)
特别说明
(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_HOMEserverdefaultdeplay
(后面这句话,我感觉好象没有用什么用的)如果是集群环境则复制到$JBOSS_HOMEserverdefaultfarm
3. 部署JBOSS集群服务
用 $JBOSS_HOMEserverall的配置来部署集群则不需这一步,只有下面1、2两步是default配置要多做的工作。
(1). 将$JBOSS_HOMEserveralldeploy 下的cluster-service.xml和jboss-web-cluster.sar复制到$JBOSS_HOME serverdefaultdeploy
(2). 将$JBOSS_HOMEserveralllib 下的jgroups.jar、jbossha.jar、jboss-cache.jar复制到$JBOSS_HOME serverdefaultlib
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%serverdefaultdeployjboss-web.deployerROOT.warweb.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%confhttpd.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%serverdefaultdeployjboss-web.deployerROOT.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 >
<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="http://blog.sina.com.cn/s/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="http://blog.sina.com.cn/s/index.jsp">Return!</a>
</center>
</body>
<%
if(session.getAttribute("name")!=null)
session.invalidate();
%>
</html>
访问方式: http://192.168.80.14/test/index.jsp