[color=red]Tomcat服务器集群与负载均衡实现[/color] [url][/url]


[size=x-large][color=red]关于Session[/color][/size]
[size=large][color=blue]使用Apache搭建Sticky模式的Tomcat集群[/color][/size] [url]http://ajita.iteye.com/blog/1848665[/url]
Apache配置tomcat负载和session粘连(stickysession) [url]http://wwwcomy.iteye.com/blog/1909211[/url]


[color=red][b]ProxyPass[/b][/color]: 硬编码配置方式
[color=red][b]ProxyPassMatch[/b][/color]: 正则表达式配置方式


[color=red][b]奇葩的Permission denied问题[/b][/color]:
在centos7或者fedora22里面,如果Selinux打开,并且负载均衡的时候,修改了8009端口,[color=darkblue]tail -f -n200 /etc/httpd/logs/lbtest-access.log[/color],那么会碰到
[color=gray][Mon Sep 14 20:54:35.873664 2015] [proxy:error] [pid 2618] (13)Permission denied: AH00957: AJP: attempt to connect to 192.168.0.198:8681 (192.168.0.198) failed
[Mon Sep 14 20:54:35.873773 2015] [proxy:error] [pid 2618] AH00959: ap_proxy_connect_backend disabling worker for (192.168.0.198) for 60s
[Mon Sep 14 20:54:35.873790 2015] [proxy_ajp:error] [pid 2618] [client 127.0.0.1:40870] AH00896: failed to make connection to backend: 192.168.0.198[/color]
找了一个晚上,终于发现[url]http://sysadminsjourney.com/content/2010/02/01/apache-modproxy-error-13permission-denied-error-rhel/[/url],执行[color=red]setsebool -P httpd_can_network_connect 1[/color],就能改变了Selinux的配置了,就解决问题了.


[size=x-large][color=red]Apache 2.2[/color][/size]
[size=x-large][color=red]以下是Apache 2.2集群方法[/color][/size]
Apache和Tomcat 配置负载均衡(mod-proxy方式) [url][/url]
Apache + Tomcat集群配置详解 (1) [url]http://zyycaesar.iteye.com/blog/294089[/url]
Apache + Tomcat集群配置详解 (2) [url]http://zyycaesar.iteye.com/blog/295227[/url]
Tomcat 集群 [url]http://publib.boulder.ibm.com/wasce/V2.1/zh_CN/configuring-tomcat-native-clusters.html[/url]
配置过程:
apache 2.x版本, 参考:
#######################################################
上传集群测试工程:test到webapp下面
vim /home/tomcat/server/tomcat7/webapps/test/index.jsp 修改标记title等信息. 方便测试

1. 配置Apache[位置于IP1]:
[color=darkblue]vim /etc/httpd/conf/httpd.conf[/color]
#---------------------start------------------------
[color=gray]LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
LoadModule proxy_http_module modules/mod_proxy_http.so[/color]
#----------------------end---------------------

如果你想看到小猫页面
[color=darkblue]DirectoryIndex index.html index.jsp index.html.var[/color]

在文件末尾加反向代理配置文件
[color=darkblue]Include conf/httpd-vhosts.conf[/color]

在文件末尾加文档最下面加上

ProxyRequests Off 
<proxy balancer://cluster> 
    BalancerMember ajp://ip1:8009 loadfactor=1 route=jvm1
    BalancerMember ajp://ip2:8009 loadfactor=1 route=jvm2
    ProxySet stickysession=ROUTEID
</proxy>



创建虚拟主机文件:


[color=darkblue]vim /etc/httpd/conf/httpd-vhosts.conf[/color]


<VirtualHost *:80> 
    Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
    ServerAdmin panyongzheng@163.com
    ServerName httpd.rh.com# apache的域名或者IP
    ServerAlias localhost 
    ProxyPass / balancer://cluster/ stickysession=JSESSIONID|jsessionid nofailover=On
    ProxyPassReverse / balancer://cluster/ 
    ErrorLog "logs/lbtest-error.log"
    CustomLog "logs/lbtest-access.log" common
</VirtualHost>



重启apache:


[color=darkblue]service httpd restart[/color]


tail -f -n200 /etc/httpd/logs/error_log


tail -f -n200 /etc/httpd/logs/lbtest-error.log


tail -f -n200 /etc/httpd/logs/lbtest-access.log



修改tomcat配置: 202.105.182.130 --> jvm1, 202.105.182.144 --> jvm2,


[color=darkblue]vim /home/tomcat/server/tomcat7/conf/server.xml[/color] 第一个用jvm1, 第二个jvm2


--------------------------


<!-- You should set jvmRoute to support load-balancing via AJP ie :-->
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">    
    <!-- <Engine name="Catalina" defaultHost="localhost">-->
    ......
    ......
    <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>


http://ip2:8080/test/ http://ip1:8080/test/


http://ip1/test/


重启Tomcat:


[color=darkblue]sh /home/tomcat/server/tomcat7/bin/shutdown.sh && rm -rf /home/tomcat/server/tomcat7/logs/catalina.out && sh /home/tomcat/server/tomcat7/bin/startup.sh && tail -f -n200 /home/tomcat/server/tomcat7/logs/catalina.out[/color]

启动Tomcat碰到的错误:


Caused by: java.net.UnknownHostException: auto: 未知的名称或服务


java.net.UnknownHostException: idcw009: idcw009: 未知的名称或服务


vi /etc/hosts


-----------


127.0.0.1 追加 auto idcw009


127.0.0.1 追加 auto idcw088



当两个机子做集群的时候, 后台数据拿到null异常信息: 单服务器多个tomcat的问题????????????=============>stickysession方式不起作用


解决方式:[url]http://motech-project.readthedocs.org/en/latest/deployment/sticky_session_apache.html[/url]




[size=x-large][color=red]Apache 2.4[/color][/size]


[size=x-large][color=red]Apache 2.4 + Tomcat7集群配置[/color][/size]


Apache2.4 proxy model文档[url]http://httpd.apache.org/docs/current/mod/mod_proxy.html[/url]


[color=red]Apache 2.4 + Tomcat7集群配置[/color][url][/url]


win7 : Apache-2.4.4 + tomcat-7.0.41 集群的配置 [url][/url]



先总结的[size=medium][color=red][b]坑坑洼洼[/b][/color][/size]


1.找不到包, 尤其版本相对应的问题. 见[url][/url]


2.权限问题, Redis没有使用root权限启动, 会出现无法访问Redis问题.


3.apache的session问题,用Redis的session的时候, 不需要stickysession=jsessionid, 否则可能出现死循环(只出现很卡, 让后电脑几乎动不了).




[color=blue]做了一些修改[/color]


[size=medium][color=red][b]1. 准备工作[/b][/color][/size]


首先下载Tomcat7 和[color=red][b]Apache2.4[/b][/color]


然后安装Apache,安装完成后在IE中输入localhost访问,如果出现It Works则表示Apache安装好了,然后解压缩到Tomcat1和Tomcat2两个目录中。


分别启动Tomcat1和Tomcat2看是否可以正常启动。


[size=medium][color=red][b]准备测试工程[/b][/color][/size]


同时准备一个应用, 分别存放到两个tomcat的webapps下面, 源码如下


[b][color=blue]集群服务器里面的应用[/color][/b],[color=red]其实两个工程的源码应该是一模一样, 我只是为了测试到底有没有集群, 所以特意在index.jsp的第一行标注是哪个服务器的代码[/color]


[color=red][b]Tomcat1[/b][/color]的test工程源码


在webapps下面添加test目录,添加index.jsp


<%@ page contentType="text/html; charset=UTF-8" %> 
<%@ page import="java.util.*" %>
<html><head><title>Cluster App Test</title></head>
<body>
第<label style='color:red;'><b>1</b></label>个集群服务器的Tomcat
<hr/>
Server Info:
<%
out.println(request.getLocalAddr() + " : " + request.getLocalPort()+"<br>");%>
<%
out.println("<br> ID " + session.getId()+"<br>");
String dataName = request.getParameter("dataName");

if (dataName != null && dataName.length() > 0) {
String dataValue = request.getParameter("dataValue");
session.setAttribute(dataName, dataValue);
}
out.print("<b>Session 列表</b>");
Enumeration e = session.getAttributeNames();
while (e.hasMoreElements()) {
String name = (String)e.nextElement();
String value = session.getAttribute(name).toString();
out.println( name + " = " + value+"<br>");
System.out.println( name + " = " + value);
}
%>
<form action="index.jsp" method="POST">
名称:<input type=text size=20 name="dataName">
<br>
值:<input type=text size=20 name="dataValue">
<br>
<input type=submit>
</form>
</body>
</html>


创建WEB-INF目录,创建web.xml


<?xml version="1.0" encoding="UTF-8"?> 
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<distributable />
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.jsp</welcome-file>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>


[color=red][b]Tomcat2[/b][/color]的test工程源码


在webapps下面添加test目录,添加index.jsp


<%@ page contentType="text/html; charset=UTF-8" %> 
<%@ page import="java.util.*" %>
<html><head><title>Cluster App Test</title></head>
<body>
第<label style='color:red;'><b>2</b></label>个集群服务器的Tomcat
<hr/>
Server Info:
<%
out.println(request.getLocalAddr() + " : " + request.getLocalPort()+"<br>");%>
<%
out.println("<br> ID " + session.getId()+"<br>");
String dataName = request.getParameter("dataName");

if (dataName != null && dataName.length() > 0) {
String dataValue = request.getParameter("dataValue");
session.setAttribute(dataName, dataValue);
}
out.print("<b>Session 列表</b>");
Enumeration e = session.getAttributeNames();
while (e.hasMoreElements()) {
String name = (String)e.nextElement();
String value = session.getAttribute(name).toString();
out.println( name + " = " + value+"<br>");
System.out.println( name + " = " + value);
}
%>
<form action="index.jsp" method="POST">
名称:<input type=text size=20 name="dataName">
<br>
值:<input type=text size=20 name="dataValue">
<br>
<input type=submit>
</form>
</body>
</html>


创建WEB-INF目录,创建web.xml


<?xml version="1.0" encoding="UTF-8"?> 
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<distributable />
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.jsp</welcome-file>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>



[b][size=medium][color=red]2. 配置Apache2.4[/color][/size][/b]


①打开conf/httpd.conf文件,加载以下模块。 在LoadModule的地方, 增加一下代码


#---------------------start------------------------
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
LoadModule speling_module modules/mod_speling.so
#LoadModule ssl_module modules/mod_ssl.so
#----------------------end---------------------


② 如果你想看到小猫页面,


<IfModule dir_module>
DirectoryIndex index.html index.jsp
</IfModule>


在上面的index.html下面添加index.jsp就可以了



③在文件末尾加反向代理


[color=red]纵向集群[/color]


ProxyRequests Off 
<proxy balancer://cluster>
BalancerMember ajp://127.0.0.1:8009 loadfactor=1 route=jvm1
BalancerMember ajp://127.0.0.1:9009 loadfactor=1 route=jvm2
</proxy>


[color=red]横向集群[/color]


ProxyRequests Off 
<proxy balancer://cluster>
BalancerMember ajp://192.168.0.11:8009 loadfactor=1 route=jvm1
BalancerMember ajp://192.168.0.12:8009 loadfactor=1 route=jvm2
</proxy>




④去掉Include conf/extra/httpd-vhosts.conf的注释标记#。


改成:[color=blue]增加[b]Include conf/httpd-vhosts.conf[/b][/color], 因为配置文件不存在, 所以增加, 并增加到httpd.conf同一个级别的文件夹.


⑤修改conf/extra/httpd-vhosts.conf文件。[color=red]没有这个文件呀, 难道直接一个空文件,然后追加?[/color],看④步.


注释掉所有的dummy-host,添加以下内容


<VirtualHost *:80> 
ServerAdmin zengwei.shao@samsung.com
ServerName localhost
ServerAlias localhost
ProxyPass / balancer://cluster/ stickysession=JSESSIONID|jsessionid nofailover=On #使用Redis的session的时候, 不需要stickysession=jsessionid, 否则可能出现死循环
ProxyPassReverse / balancer://cluster/
ErrorLog "logs/lbtest-error.log"
CustomLog "logs/lbtest-access.log" common
</VirtualHost>
这个配置文件会做负载,但sessionid不一致


<VirtualHost *:80>
ErrorLog "logs/acooly.org-error_log"
LogFormat "%{Host}i %h %l %u %t \"%r\" %s %b" vcommon
CustomLog logs/access_log vcommon

ProxyRequests Off
ProxyPreserveHost on
# apache+tomcat cluster
ProxyPass / balancer://tomcat-cluster/ stickysession=JSESSIONID|jsessionid nofailover=Off
ProxyPassReverse / balancer://tomcat-cluster/
<Proxy balancer://tomcat-cluster/>
BalancerMember ajp://192.168.0.11:8009 loadfactor=1 route=jvm1
BalancerMember ajp://192.168.0.12:8009 loadfactor=1 route=jvm2
ProxySet lbmethod=bybusyness
</Proxy>
</VirtualHost>



说明


[b]ProxyRequests[/b] Off 表示启用反向代理,必须开启;


[b]ProxyPass[/b]为代理转发的Url,即将所有访问/的请求转发到群集balancer://cluster


[b]BalancerMember[/b]为群集的成员,即群集服务器1或2,负载均衡服务器会根据均衡规则来将请求转发给BalancerMember


[b]loadfactor[/b]=1 即为均衡规则的权重


[b]stickysession[/b]=jsessionid -- 用于处理session发送问题




[size=medium][b][color=red]2.配置Tomcat[/color][/b][/size]


[color=red]两个方向集群共同修改[/color]


server.xml, 第1个改为jvm1, 第二个改为jvm2


<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm${n}">


取消注释

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>


或者改为


<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>
</Cluster>



[color=red]纵向集群[/color]


tomcat1保持配置不变, 第二个tomcat2的server.xml里面涉及到的端口, 第一个数字改为9, 比如:9080,9443......


[color=red]横向集群[/color]


端口一律不变



配置完成之后,启动两个Tomcat测试一下Tomcat是否可以正常启动


如果可以正常启动,则进行下面步骤


最后启动Apache2.4,在页面中输入


到此, 直接启动的时候, 好像已经集群好了.



[size=medium][b][color=red]测试[/color][/b][/size]


[url]http://192.168.2.100/test/[/url](apache服务器地址),[color=red]不断刷新, 不断看到调用不同的服务器. [/color], 会看到seession输出类似7F401A91D1BB932EB482B9819A13233D.jvm1, 注意, 点的后面是服务器标识.


[color=red][b]纵向集群[/b][/color]:session一直改变!!,要使用session共享来解决问题.


[color=red][b]横向集群[/b][/color]:session保持不变,VM和docker都测试成功.


[color=darkblue]注意:如果纵向只要Redis的session共享方式, 横向不使用的话, 那么纵向和横向个子的session不变, 但是从两个方向切换的时候, 就会改变. 所以, 一旦使用session共享, 那么横向和纵向都需要配置.[/color]




[size=medium][color=red][b]session共享[/b][/color][/size]


[b]这是因为没有做Session处理, 它包含两种方式:[/b]


[color=blue][b]I: Session Sticky[/b][/color],把同一个用户的Session一直发送到同一个逻辑单元处理,指定负载的分发组件(如Apache),把请求中包含特定属性的请求发到同一个连接,如指定jsessionid一致的请求到同一个请求,或者在负载组件中给每个响应增加一个头部属性,指定下次的分发目的地. 配置见: [url]http://ajita.iteye.com/blog/1848665[/url],[color=red]测试成功[/color]



[b][color=blue]II: 通过数据库来实现tomcat集群[/color][/b], 例子1: [url]http://mushme.iteye.com/blog/1147960[/url], 例子2:[url]http://tangmingjie2009.iteye.com/blog/1772169[/url], 不测试



[color=blue][b]II: mod_jk方式[/b][/color] apache + tomcat + mod_jk 集群部署及Session共享和遇到的问题 [url]http://bbs.vpigirl.com/forum.php?mod=viewthread&tid=562[/url], 不测试



[b][color=blue]IV: Redis的Session共享[/color][/b], 所有的后端处理逻辑单元共享Session或者Session更新时通知其它逻辑单元(1. 各自维护Session,当请求处理结束后,通知其它组件单元更新Session,包括异步和同步模式; 2. 统一维护Session,如把Session放在memcached或者共享文件系统中,可以在容器级别或者应用级别做这个事情; 3.利用cache server),


使用 Redis 来存储 Apache Tomcat 7 的 Session[url]http://www.open-open.com/lib/view/1378430065687[/url]


[color=red]tomcat7和redis的sessoin共享问题处理,tomcat7sessoin[/color][url]http://www.bkjia.com/ASPjc/930346.html[/url]


[b]获得集群的包[/b]: [url][/url],配置见后面,附件是我自己打包得到的.


安装Redis, 网上找教程,[color=red]注意: 安装Redis后, 要使用root权限启动.[/color]


[color=darkblue]su root


redis-server /etc/redis/redis.conf[/color]


两个tomcat都需要配置context.xml


<Context>
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
host="localhost" <!-- 这里是redis服务器IP -->
port="6379"
database="0"
maxInactiveInterval="60" />
</Context>


-------------------


配置纵向的时候没问题, 但是配置横向(两个虚拟机, 非docker方式)的时候碰到问题:


1. [color=red]Could not get a resource from the pool[/color]


原来是服务器没有开放Redis的6379端口, 晕死了............




[b][size=medium][color=red]获得包的代码[/color][/size][/b]


=====================================================================


build.gradle文件, 注意版本, 得到包后, 其实[color=blue]只需要几个包:commons-pool2-2.2.jar,jedis-2.5.2.jar,tomcat-juli-7.0.61.jar,tomcat-redis-session-manager-master-2.0.0.jar[/color].


apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'signing'

group = 'com.orangefunction'
version = '2.0.0'

repositories {
mavenCentral()
}

compileJava {
sourceCompatibility = 1.7
targetCompatibility = 1.7
}

dependencies {
compile group: 'org.apache.tomcat', name: 'tomcat-catalina', version: '7.0.61'
compile group: 'redis.clients', name: 'jedis', version: '2.5.2'
compile group: 'org.apache.commons', name: 'commons-pool2', version: '2.2'
//compile group: 'commons-codec', name: 'commons-codec', version: '1.9'

testCompile group: 'junit', name: 'junit', version: '4.+'
testCompile 'org.hamcrest:hamcrest-core:1.3'
testCompile 'org.hamcrest:hamcrest-library:1.3'
testCompile 'org.mockito:mockito-all:1.9.5'
testCompile group: 'org.apache.tomcat', name: 'tomcat-coyote', version: '7.0.61'
}

task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from 'build/docs/javadoc'
}

task sourcesJar(type: Jar) {
from sourceSets.main.allSource
classifier = 'sources'
}

artifacts {
archives jar

archives javadocJar
archives sourcesJar
}

//signing {
// sign configurations.archives
//}

task copyJars(type: Copy) {
from configurations.runtime
into 'dist'
}

uploadArchives {
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }

//repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
// authentication(userName: sonatypeUsername, password: sonatypePassword)
//}
//repository(url: "https://oss.sonatype.org/content/repositories/snapshots") {
// authentication(userName: sonatypeUsername, password: sonatypePassword)
//}

pom.project {
name 'tomcat-redis-session-manager'
packaging 'jar'
description 'Tomcat Redis Session Manager is a Tomcat extension to store sessions in Redis'
url 'https://github.com/jcoleman/tomcat-redis-session-manager'

issueManagement {
url 'https://github.com:jcoleman/tomcat-redis-session-manager/issues'
system 'GitHub Issues'
}

scm {
url 'https://github.com:jcoleman/tomcat-redis-session-manager'
connection 'scm:git:git://github.com/jcoleman/tomcat-redis-session-manager.git'
developerConnection 'scm:git:git@github.com:jcoleman/tomcat-redis-session-manager.git'
}

licenses {
license {
name 'MIT'
url 'http://opensource.org/licenses/MIT'
distribution 'repo'
}
}

developers {
developer {
id 'jcoleman'
name 'James Coleman'
email 'jtc331@gmail.com'
url 'https://github.com/jcoleman'
}
}
}
}
}
}


构建命令: [color=darkblue]gradle build -x test copyJars[/color]


得到所有包, 放到tomcat/lib下面, 注意版本.