Apache + Tomcat集群 + 负载均衡
Part I:
取经处: http://www.ramkitech.com/2012/10/tomcat-clustering-series-simple-load.html
http://blog.csdn.net/bluishglc/article/details/6867358
这部分先弄个简单的Load Balance的例子。
对Apache Server和Tomcat二者之间的关系有一定了解后,应该可以理解下面盗取的结构图:
在实际生产环境中,不会采取单个Tomcat实例的架构,因为没有任何灾备机制。一旦发生自热灾害,硬件损坏或者内存泄漏等严重错误,都会导致所谓的服务器宕机。
为了避免这种情况的发生,必定采用灾备机制。典型的做法就是在多台server上部署同一个Application,然后各个server之间相互为backup。
但这样,随之而来的问题就是如何分布用户请求。不可能告诉用户所有的Tomcat server IP,然后一会访问这个,过一会又访问另外一个,太不人性化了!
这就引出了负载均衡(Load Balance)这个概念。所有用户请求都由Apache Server接收,之后根据内部逻辑分发请求给各个Tomcat。
简单的Load Balance:
环境:CentOS7,Tomcat8.5.4,Apache httpd-2.4.23,tomcat-connectors-1.2.41
1. 安装Apache Server(略过)
2. 钱不够,只有一台笔记本,遂安装Tomcat并配置多个实例来模拟多个tomcat server(可以参照之前的blog进行配置)
假设3个tomcat实例命名为tomcat1,tomcat2,tomcat3
3. 安装并编译mod_jk.so(略过),用于之后Apache Server同Tomcat通信。
4. 以上准备工作完成后,下面就是Load Balance的配置喽。
1. 通过mod_jk moudle建立Apache Server同Tomcat的通信
1. 在${ApacheServerPath}/conf下创建workers.properties,内容如下:
worker.list=loadbalancer,stat worker.loadbalancer.type=lb
worker.loadbalancer.balance_workers=tomcat1,tomcat2,tomcat3 worker.tomcat1.type=ajp13
worker.tomcat1.port=8009
worker.tomcat1.host=localhost worker.tomcat2.type=ajp13
worker.tomcat2.port=8019
worker.tomcat2.host=localhost worker.tomcat3.type=ajp13
worker.tomcat3.port=8029
worker.tomcat3.host=localhost worker.stat.type=status
2. 在${ApacheServerPath}/conf/httpd.conf中添加如下内容:
LoadModule jk_module modules/mod_jk.so JkWorkersFile conf/workers.properties JkLogFile logs/mod_jk.log
JkLogLevel emerg
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
JkRequestLogFormat "%w %V %T" JkMount /status stat
JkMount /* loadbalancer
5. 启动Apache Server,各个tomcat实例进行负载均衡测试。
测试之前先在各个ROOT下创建index.html文件,用来区分哪个tomcat server被调用。
1. 请求localhost,看整个服务是否可用;
2. 请求localhost/status,看Load Balance情况。
Part II:
看到一篇稍好的blog,算是补充吧。http://freeloda.blog.51cto.com/2033581/1301888/
接Part I,虽然实现了简单的负载均衡,但是很明显存在问题。很多blog都拿购物车举例,也很通俗易懂:Part I实现的负载均衡基本会将每次的请求均匀转发到tomcat server上。当我们在购物车中添加了一项,之后再刷新页面发送新的请求,这时的Apache Server将会分发该请求到新的tomcat server,再一次创建新的session,导致购物车之前的添加项全部丢失,严重影响用户体验。
为解决这个问题,可以通过Sticky Session(粘性会话),实际上就是让Apache Server能够识别正确的Tomcat Server。
从Session的简单原理入手,当用户第一次请求时,Apache Server转发到Tomcat Server,相应创建session(位于tomcat server的内存中)。一个Session可以想象成类似Map的容器,可以CRUD各种属性。对应这个session,有一个Session ID(在Tomcat中,又称为jsessionid)。该Session ID将附到HTTP Response中返回客户端,存储于浏览器中。当用户再次发送类似的请求时,会将Session ID封装到HTTP Request中,经Apache Server转发到Tomcat Server。
所以,对Session ID改进,达到Sticky Session的目的。
由于Session ID是Tomcat Server生成的,所以理所应当修改各个Tomcat的配置文件。
1. 修改每个tomcat实例的server.xml - 在Engine标签中添加jvmRoute属性(和workers.properties中的balance_workers各项相对应)
OK!
2. 测试,启动Apache Server和Tomcat。
在浏览器发出的HTTP Request中,会发现
Cookie:JSESSIONID=40025608F7B50E42DFA2785329079227.tomcat1
实现Sticky Session。
Part III:
实现Sticky Session还不足以解决某个Tomcat宕掉的影响。假设某个Tomcat宕掉,那相应的Session全部失效,严重影响用户体验!
这就又引出Session Replication(复制会话)的概念。也就是相同的Session在其他Server中也存在,当Server宕掉,另一台可以立即被使用。
实现会话复制的方案有不少:
1. 建立Session服务器,所有Session全部存储于该服务器之上。当某个Tomcat Server宕掉后,其他server可以从该Session服务器中复制一份过来。缺点是一旦Session服务器宕掉,所有Session均失效,代价更大。
2. Tomcat集群方案
1. 采用多播的方式在Tomcat Server之间通信来复制Session,一个server的session在其他所有server均存在。缺点是如果参与集群的server数量过多,必然会导致单个server的负载过重。所以不适合大集群环境。
2. 采用Backup机制,比如两台Tomcat之间通信以保证各自Session在对方有备份。
先来尝试多播方式的Tomcat集群:)
一共3个步骤:
1. enable多播路由;2. 添加Cluster标签到conf/server.xml中;3. 给app添加<distributable/>
这里面的多播是网络通信里的概念,正在不断了解过程中。。。区别于单播与广播,和字面意思相一致。对于Tomcat集群来说,各个之间必然进行通信,成为一组,多播是首选吧。
既然是会话复制,就应该涉及到session的管理问题。在Tomcat集群中,SessionManager负责session的管理,分以下4种:
- Standard Manager
- Persistent Manager
- Delta Manager
- Backup Manager
Standard Manager:
这个是tomcat默认使用的session管理。但针对stand-alone(单机)tomcat,不能用于tomcat集群环境。
Persistent Manager:
会将session信息定期存储于文件/数据库中,可以配置以何种方式存储。但由于定期存储和更新,可能导致session更新不够及时。
Delta Manager:
Tomcat集群默认使用的session管理。每当session发生变化,比如setAttribute(), removeAttribute(),都会及时在其他集群节点上更新。这也就很容易造成集群中的tomcat负载过重,所以不适合大规模的集群环境。
Backup Manager:
这个可以看作是Persistent Manager和Delta Manager的折中。两套Tomcat server互为Backup。
--------------NND,先吐槽下,多播方式的Tomcat集群愣是前前后后耗费了2天的时间才搞定!咱国人的博客水准真是亟待提高,还是参考了老外的博客最终搞定。。。
环境:根据Part II已经实现Sticky Session,但要说明下我的CentOS7是Win7上的虚拟机。
下面记录我在build多播方式的Tomcat集群过程中的每步以及遇到的问题:
1. 按照官方文档以及该blog,copy其中Cluster标签的默认配置到各个tomcat实例中的server.xml文件中(位于Enginee标签下),并为各个tomcat实例设置不同的Receiver端口。
这里需要注意,tomcat-8.5.4关于Cluster的默认配置里已经没有<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>,而且<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>已经变成<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
2. route add -net 224.0.0.0 netmask 240.0.0.0 dev your_ethernet接口,添加your_ethernet接口到路由表中。
3. 在各个tomcat实例的app中的web.xml中添加标签<distributable/>
OK,启动httpd和3个tomcat实例(都在CentOS虚拟机上)。
按照该blog的测试方法,查看每个tomcat实例的manager界面,看是否session之间共享。结果不是。。。
在仔细检查各个配置文件并确定没问题后,开始天马行空的想,漫无目的的搜索,但大部分的信息都是讲怎么一步步配置还有零散的一些问题解决方案。尝试了几个后(比如给Membership标签添加bind属性,绑定本地IP;修改Receiver的address属性值为本地IP)都没解决,心想解决问题真心不能靠运气啊!
但是从哪下手呢? - 日志
相比较于Windows,在Linux下启tomcat不会有滚动日志显示,不能实时知道tomcat的状态。
总结一下我所遇到的问题:
1. java.io.IOException: Network is unreachable
Unable to join multicast group, make sure your system has multicasting enabled
很明显,提示多播功能可能没有enable。但是我的确做了这步的,而且通过route -e, tcpdump -ni your_ethernet接口 host 228.0.0.4也证实了(好blog)。最后有点儿醍醐灌顶的认为应该是防火墙的问题,就把CentOS自带的firewall.service给remove掉了,也防止对Receiver的端口的影响。
2. SimpleTcpCluster.startInternal Unable to start cluster
ChannelException: java.net.SocketException: No such device; No faulty members identified.
这个提示让人无从下手,但根据这篇blog所说,应该是虚拟机的网络设置不当造成的。我最后选择了桥接模式,记得重启后才生效。
3. skipping state transfer. No members active in cluster group
这个是耗费我时间最多的一项。仅仅提示你No members active而没有说明可能的原因!我是各种搜索各种尝试,整的自己晕晕乎乎的,各种郁闷(根上还是网络通信/编程这块不懂造成的)。
在解决这个问题之前,我用公司laptop的Win7环境搭了差不多相同的tomcat集群+httpd。之后顺利实现loadbalance+tomcat集群。在查看log的过程中,发现第一个启来的tomcat会有skipping state transfer. No members active in cluster group的日志,当时窃喜!说不定我CentOS上的那套集群实际上已经成功了呢?!可惜仅限于美好的想法。不过Win7下的log倒是让我知道了什么样的情况表示tomcat集群成功built起来了:"Replication member added:org.apache.catalina.tribes.membership.MemberImpl[tcp:"。
误打误撞搜到这么一篇stackoverflow,大概意思是multicast已经enable,但是tomcat就是无法集群。里面提到了IPv4和IPv6,一脸萌比。。。不过下面的回答倒是让我有了些希望。Tomcat8.5.4默认绑定IPv6,当发现server不工作时,应改绑定IPv4。
在httpd和3个tomcat实例都启来后,我尝试请求localhost,得到503页面,之后查看mod_jk.log,发现loadbalance没有问题。再尝试http直接访问tomcat,没有问题。所以断定tomcat接收分发的请求出现问题。由于接收请求是通过ajp13协议进行,很可能IPv4和IPv6捣鬼了。
参考blog,将tomcat运行环境与IPv4绑定。
到此,多播方式的Tomcat集群 + 负载均衡 终于搞定!
PS: 能花半天时间做记录,我也是醉醉的了,估计仅此一次吧:)
Apache + Tomcat集群 + 负载均衡的更多相关文章
- ngnix apache tomcat集群负载均衡配置
http://w.gdu.me/wiki/Java/tomcat_cluster.html 参考: Tomcat与Apache或Nginx的集群负载均衡设置: http://huangrs.blog. ...
- Ubuntu下基于Nginx实现Tomcat集群负载均衡
版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] Nginx是一款HTTP和反向代理服务器,有关它的介绍可以到网上搜一下,很多很多,不再累述.这里,我们记录一下Nginx ...
- 使用Nginx实现Tomcat集群负载均衡
概述 要解决的问题 环境准备以及问题解决思路 配置 测试 小结 一.概述 使用Nginx主要是来解决高并发情况下的负载均衡问题. 二.要解决的问题 1.最主要是负载均衡请求分发. 2.文件上传功能,只 ...
- nginx+tomcat集群负载均衡(实现session复制)
转自:http://talangniao.iteye.com/blog/341512 架构描述 前端一台nginx服务器做负载均衡器,后端放N台tomcat组成集群处理服务,通过nginx转发到后面( ...
- 【nginx+tomcat集群】Nginx1.12.2+Tomcat7集群+负载均衡+Session共享
今天想着将项目优化一下,就想的实现集群分布,在本机测试:利用nginx+tomcat实现 通过上一篇博客(http://www.cnblogs.com/qlqwjy/p/8535235.html),N ...
- Apache+Tomcat +mod_proxy集群负载均衡及session
序言: 在玩Apache+Tomcat +mod_jk集群负载均衡及session的时候发现,还有一种方式可以实现,就是网上各位大牛们说的mod_proxy反向代理. 实在弄的我的知识细胞洋洋.实 ...
- 实战Apache+Tomcat集群和负载均衡
实战Apache+Tomcat集群和负载均衡 目录 1. 什么是J2EE集群... 3 1.1. 序言... 3 1.2. 基本术语... 3 伸缩性(Scalability): ...
- 图文解说:Nginx+tomcat配置集群负载均衡
图文解说:Nginx+tomcat配置集群负载均衡 博客分类: appserver nginxTomcatUbuntuLinux网络应用 作者:niumd Blog:http://ari.iteye ...
- 转】Nginx+tomcat配置集群负载均衡
原博文出自于:http://blog.csdn.net/bruce_6/article/details/38228299 感谢! 相信很多人都听过nginx,这个小巧的东西慢慢地在吞食 ...
随机推荐
- POJ 1795 DNA Laboratory(状压DP)
[题目链接] http://poj.org/problem?id=1795 [题目大意] 给出n个字符串,求一个最小长度的串,该串包含给出的所有字符串. 要求长度最小且字典序最小. [题解] dp[i ...
- 理解Promise简单实现的背后原理
在写javascript时我们往往离不开异步操作,过去我们往往通过回调函数多层嵌套来解决后一个异步操作依赖前一个异步操作,然后为了解决回调地域的痛点,出现了一些解决方案比如事件订阅/发布的.事件监听的 ...
- ios学习流水账1
1.UIImageview设边框.圆角 需要引QuartzCore/QuartzCore.h> //设UIImageView边框 CALayer *layer = [m_imgView laye ...
- lodop 控件实现web打印功能
WEB套打可选方案不多,理想的更少,利用免费控件Lodop+JavaScript实现精确套打,算是较为经典的选择.这种方案其实比较简单,利用一个htm文件就可以实现模板设计过程,几乎是“空手套”式的开 ...
- 关于宏:container_of和 offsetof以及list_for_each_entry
1.offsetof(TYPE, MEMBER) #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) offse ...
- Http协议三次握手过程
Http协议三次握手过程 TCP(Transmission Control Protocol) 传输控制协议 TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接: ...
- ansible自动化工具使用
1.服务端配置 安装即可,无需启动,在安装ansible之前需要配置epel源 [root@m01 ~]# wget -O /etc/yum.repos.d/epel.repo http://mirr ...
- 2017.6.27 跟开涛学spring3--spring概述
参考来自:http://www.importnew.com/17474.html 注意,项目中使用的是spring4,这里学习的是spring3.关于spring4的变化:http://ningand ...
- 【LeetCode-面试算法经典-Java实现】【109-Convert Sorted List to Binary Search Tree(排序链表转换成二叉排序树)】
[109-Convert Sorted List to Binary Search Tree(排序链表转换成二叉排序树)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 ...
- android SQLite(单词的添加与查询应用)
本人小白,刚接触android,为方便记忆,将平时练习的代码写下来,跟大家分享,也希望大神批评指正. 这个实例主要用到的SQLite数据库的操作,可以向数据库添加单词,查询,修改以及删除单词,描述如有 ...