实战Apache+Tomcat集群和负载均衡

目录

1.    什么是J2EE集群... 3

1.1.     序言... 3

1.2.     基本术语... 3

伸缩性(Scalability):... 4

高可用性(High availability):... 4

负载均衡(Load balancing):... 4

容错(Fault tolerance):... 5

失效转移(Failover):... 5

等幂方法(Idempotent methods):... 5

1.3.     什么是J2EE集群... 5

2.    J2EE集群给我们带来了什么... 9

3.    实战准备... 9

4.    首战失败... 9

5.    详细配置... 9

6.    负载均衡... 9

7.    失败转移... 9

1.   什么是J2EE集群

1.1. 序言

越来越多的关键应用运行在J2EE(Java 2, Enterprise Edition)中,这些诸如银行系统和账单处理系统需要高的可用性(High Availability, HA),同时像Google和Yahoo这种大系统需要大的伸缩性。高可用性和伸缩性在今天高速增长的互连接的世界的重要性已经证实了。eBay于1999年6月停机22小时的事故,中断了约230万的拍卖,使eBay的股票下降了9.2个百分点。

J2EE集群是用来提供高可用性和伸缩性服务,同时支持容错处理的一种流行的技术。但是,由于J2EE规范缺乏对集群的支持,J2EE供应商实现集群的方法也各异。这给J2EE架构师和开发人员带来了很多困难。以下是几个常见的问题:

l        为什么带集群功能的商业J2EE服务器产品如此昂贵?(10倍于不带集群功能的产品)

l        为什么基于单服务器环境构建的应用不能在集群中运行?

l        为什么应用在集群环境中运行得很慢,但在非集群环境中却快得多?

l        为什么集群的应用移植到其他服务器中失败?

l        理解这些限制和要素的最佳方法是学习他们的实现方式。

1.2. 基本术语

在我们讨论不同的集群实现之前,先谈谈几个概念。这有助于理解不同的J2EE集群产品不同的设计结果和概念:

1.2.1.    伸缩性(Scalability):

在一些大的系统中,预测最终用户的数量和行为是非常困难的,伸缩性是指系统适应不断增长的用户数的能力。提高这种并发会话能力的一种最直观的方式就增加资源(CPU,内存,硬盘等),集群是解决这个问题的另一种方式,它允许一组服务器组在一起,像单个服务器一样分担处理一个繁重的任务。

1.2.2.    高可用性(High availability):

单一服务器的解决方案并不是一个健壮方式,因为容易出现单点失效。像银行、账单处理这样一些关键的应用程序是不能容忍哪怕是几分钟的死机。它们需要这样一些服务在任何时间都可以访问并在可预期的合理的时间周期内有响应。集群方案通过在集群中增加的冗余的服务器,使得在其中一台服务器失效后仍能提供服务,从而获得高的可用性。

1.2.3.    负载均衡(Load balancing):

负载均衡是集群的一项关键技术,通过把请求分发给不同的服务器,从而获得高可用性和较好的性能。一个负载均衡器可以是从一个简单的Servlet或Plug-Ins(例如一个Linux box利用ipchains来实现),到昂贵的内置SSL加速器的硬件。除此之外,负载均衡器还需执行一些其他的重要任务,如“会话胶粘”让一个用户会话始终存在一个服务器上,“健康检查”用于防止将请求分发到已失效的服务器上。有些负载均衡器也会参与我们下面将要谈到“失效转移”过程。

1.2.4.    容错(Fault tolerance):

高可用性意味着对数据正确性的要求不那么高。在J2EE集群中,当一个服务器实例失效后,服务仍然是有效的,这是因为新的请求将被冗余服务器处理。但是,当一个请求在一个正在失效的服务器中处理时,可能得到不正确的结果。不管有多少个错误,容错的服务应当能确保有严格的正确的行为。

1.2.5.    失效转移(Failover):

失效转移是集群中用来获取容错能力的另一项关键的技术。当一个结点失效后,通过选择集群中的另一个结点,处理将会继续而不会终止。转移到另一个结点可以被显式的编码,或是通过底层平台自动地透明地路由到另一个服务器。

1.2.6.    等幂方法(Idempotent methods):

等幂方法是指这样一些方法:重复用相同的参数调用都能得到相同的结果。这些方法不会影响系统状态,可以重复调用而不用担心改变系统。例如:getUsername()就是等幂的,而deleteFile就不是。当我们讨论HTTP Session失效转移和EJB失效转移时,它是一个重要的概念。

1.3. 什么是J2EE集群

一个天真的问题,不是吗?但我仍要用几句话和图来回答它。通常,J2EE集群技术包括"负载均衡"和"失效转移"。

如图1所示,负载均衡意味着有许多客户端向目标对象同时发出请求。负载均衡器在调用者和被调用者之间,分发请求到与原始对象相同的冗余对象中。伸缩性和高可用性就是这样得到的。

如图2所示,失效转移与负载均衡不同。有时客户端会连续发请求到目标对象,如果请求中间目标对象失效了,失效转移系统将检测到这次失败,并将请求重定向到另一个可用的对象。通过这种方式可以获得容错能力。

如果你想知道更多的有关J2EE集群的知识,你就会问到一个基本的问题,“什么对象可以集群?”和“在我的J2EE代码中哪里会发生负载均衡和失效转移呢?”。这些都是用来理解J2EE集群的非常好的问题。实际上,并不是所有的对象都能被集群的,并且负载均衡和失效转移并不是在J2EE代码所有地方都能发生。看看下面的例子代码:

在Class A的bussiness()方法中,instance1可以负载均衡吗?或是当其失效,可以失效转移到其他B的实例上吗?我想是不行的!对负载均衡和失效转移来说,必须要有个拦截器在调用者和被调用者之间分发或重定向请求到不同的对象上。Class A和Class B的实例是运行在一个JVM中紧密耦合的,在方法调用间加入分发逻辑非常困难。

什么类型对象可以被集群?——只有那些可以被部署到分布式拓朴结构中的组件。

在我的J2EE代码中,什么地方会有负载均衡和失效转移?——只在你调用分布式组件的方法时。

在如图4所示的分布式环境中,调用者和被调用者被分离在有明显边界的不同的运行容器中,这个边界可以是JVM,进程和机器。

当目标对象被客户端调用时,目标对象的功能是在容器中运行的(这就是为什么我们说它是分布式的原因)。客户端和目标对象通过标准的网络协议通信。这些特性就为一些机制提供了机会可以介入到方法调用之间实现负载均衡和失效转移。

如图4,浏览器通过HTTP协议调用JSP对象,JSP运行在WEB服务器中,浏览器只需要返回结果而不关心它是怎么运行的。在上述场景中,一些东西就可以在浏览器与WEB服务器之间实现负载均衡和失效转移的功能。在J2EE平台,分布式技术包括:JSP(Servlet),JDBC,EJB,JNDI,JMS,WEB Service等。负载均衡和失效转移就发生在这些分布式方法被调用时。在后续部分我们将详细讨论这些技术。

2.   实战

2.1. 软件环境

2.1.1.    Apache

apache 2.0.55 (由http://httpd.apache.org/进入下载)

2.1.2.    Tomcat

Tomcat 5.5.25 (由http://tomcat.apache.org/进入下载)

2.1.3.    Mod_jk

在页面 http://tomcat.apache.org/   Download 标题下找到 Tomcat Connectors 链接进入( 点击下载mod_jk-apache-2.0.55.so),看起来像是个Unix/Linux下的动态库,实际应是个Win32 的 DLL 动态库,大概是为保持不同平台配置的一致性,才用了这个扩展名。

2.2. 负载均衡

 用Apache进行分流,把请求按照权重以及当时负荷分tomcat1,tomcat2...去处理

2.2.1.    安装apache,tomcat

我把Apache安装在F:\ZQ\apache2.0.55\Apache2

  解压两分Tomcat, 分别在 F:\ZQ\apache-tomcat-5.5.1,F:\ZQ\apache-tomcat-5.5.2

2.2.2.    修改Apache配置文件http.conf

在apache安装目录下conf目录中找到http.conf,在文件最后加上下面一句话就可以了

include conf/mod_jk.conf

2.2.3.    http.conf 同目录下新建mod_jk.conf文件,内容如下

#加载mod_jk Module

LoadModule jk_module modules/mod_jk-apache-2.0.55.so

#指定 workers.properties文件路径

JkWorkersFile conf/workers.properties

#指定那些请求交给tomcat处理,"controller"为在workers.propertise里指定的负载分配控制器

JkMount /*.jsp controller

如果还要指定*.do也进行分流就再加一行

JkMount /*.do controller

如果你想对所有的请求进行分流只需要写成

JkMount /* controller

2.2.4.    在http.conf同目录下新建 workers.properties文件,

内容如下(可能要去除 # 不在行首的注释)

worker.list = controller,tomcat1,tomcat2  #server 列表

#========tomcat1========

worker.tomcat1.port=8009

#ajp13 端口号,在tomcat下server.xml配置,默认8009

worker.tomcat1.host=localhost

#tomcat的主机地址,如不为本机,请填写ip地址

worker.tomcat1.type=ajp13

worker.tomcat1.lbfactor = 1

#server的加权比重,值越高,分得的请求越多

#========tomcat2========

worker.tomcat2.port=8109

#ajp13 端口号,在tomcat下server.xml配置,默认8009

worker.tomcat2.host=localhost

#tomcat的主机地址,如不为本机,请填写ip地址

worker.tomcat2.type=ajp13

worker.tomcat2.lbfactor = 2

#server的加权比重,值越高,分得的请求越多

#========controller,负载均衡控制器========

worker.controller.type=lb

worker.controller.balanced_workers=tomcat1,tomcat2

#指定分担请求的tomcat

worker.controller.sticky_session=1

2.2.5.    修改tomcat配置文件server.xml

如果你是水平集群,即在不同电脑上安装tomcat,tomcat的安装数量为一个,可以不必修改tomcat配置文件.我这里是在同一台电脑上安装两个tomcat,实现的是垂直集群方式,所以必须修改其中一个的设置,以避免端口冲突,按照参考文章是把原来以9开头的端口号改为以9开头端口号,但是在我机器上如果以9开头的端口号,例如9080、9082会与我的WebSphere Application Server配置冲突,所以我这里采取的策略是把原来端口号的第三位改为1,如8080改为8180。

打开tomcat2/conf/server.xml文件

1) 将关闭Tomcat的监听端口改成由8005改为8105

即把

<Server port="8005" shutdown="SHUTDOWN">

改为

<Server port="8105" shutdown="SHUTDOWN">

2) 把http服务端口号由8080改为8180

找到

<!-- Define a non-SSL HTTP/1.1 Connector on port 8080 -->

<CONNECTOR port="8080"

把这里的8080改为8180

3) 把AJP端口号由8009改为8109

找到

<!-- Define an AJP 1.3 Connector on port 8009 -->

<CONNECTOR port="8009"

把这里的8009改为8109

4) 把 HTTP 代理端口从8082改为8182(这个配置默认是被注释掉的,可跳过这一步)

找到

<CONNECTOR port="8082"

把这里的8082改为8182

5) 编写一个测试 jsp

建立一个目录TestCluster,里面新建一个test.jsp,内容为

<%

System.out.println("===========================");

%>

把TestCluster放到tomcat1,tomcat2的webapps下

6) 启动apache,tomcat1,tomcat2,进行测试

通过 http://localhost/TestCluster/test.jsp 访问,多刷新几次页面,查看Tomcat1和Tomcat2的窗口,你将可以看到打印了一行行"===========================",并且从统计上来说,大约在tomcat2打印的数量是在Tomcat1中的两倍,可以看到请求会被tomcat1,tomcat2按照不同的权重分流处理,实现了负载均衡。

作下面的集群配置,请在workers.properties把tomcat1和tomcat2的权重改为一样的,使请求较平均分配,将有便于看到实验的效果。

2.3. 配置集群

只配置负载均衡还不行,还要session复制,也就是说其中任何一个tomcat的添加的session,是要同步复制到其它tomcat, 集群内的tomcat都有相同的session

2.3.1.    修改tomcat1, tomcat2的server.xml,将集群部分配置,

即对<Cluster>节点的在注释符删掉,并将tomcat2的4001端口改为4002,以避免与tomcat冲突,当然,如果是两台电脑,是不用改端口的,去掉注释符即可

即取消对如下处

<Cluster className="org.apache.catalina.cluster.tcp.SimpleTcpCluster"

managerClassName="org.apache.catalina.cluster.session.DeltaManager"

expireSessionsOnShutdown="false"

............

<ClusterListener className="org.apache.catalina.cluster.session.ClusterSessionListener"/>

</Cluster>

前后的注释标记<!--  -->,启用该项配置,实现服务器间的Session复制。

2.3.2.    为 Tomcat1和 Tomcat2 增加 jvmRoute

这里的作用大概是为了在集群的TOMCAT里面,生成全局HTTP会话标识,这样可以保证会话的唯一性,有利于session的复制

(先跳过这一步,有精力可以试验一下)

在 Tomcat1 和 Tomcat2 的 server.xml 文件,找到

<ENGINE name="Catalina" defaultHost="localhost">

分别改为

<ENGINE name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">

<ENGINE name="Catalina" defaultHost="localhost" jvmRoute="tomcat2">

然而实际我配置的时候还不能加jvmRoute属性,配置了反而有问题。

刷新浏览器窗口总是在某一个tomcat控制台输出形如

SessionID:154678FA6D4D0ABD57658B750E7A3532.tomcat1  (在tomcat1窗口)

或者

SessionID:3800571A532AECEA7280F45361861AD4.tomcat2  (在tomcat2窗口)

由控制台打印的结果可以看出,SessionID在哪个tomcat上产生,那么后续该会话的请求将总是会这个tomcat来处理。

并且注意到SessionID的形式比通常情况多了一个后缀.tomcat1或.tomcat2,还搞不清楚是为什么。

配置时请视实际情况而取舍。

2.3.3.    修改测试项目 TestCluster

修改test.jsp,内容如下

<%@ page contentType="text/html; charset=GBK" %>

<%@ page import="java.util.*" %>

<html><head><title>Cluster App Test</title></head>

<body>

<%

System.out.println("SessionID:"  + session.getId());

%>

Server Info:

<%

out.println(request.getServerName() + " : " + request.getServerPort()+"<br>");%>

<%

out.println("<br> ID " + session.getId()+"<br>");  // 如果有新的 Session 属性设置

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><br>");

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="test.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>

4. 配置Session复制

在TestCluster目录下新建WEB-INF目录,WEB-INF下新建web.xml,内容如下

<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">

<display-name>TomcatClusterDemo</display-name>

<distributable/>

<web-app>

也就是在需要集群的应用的web.xml中加上属性,表明该应用可多应用分流处理,能进行Session的复制

把TestCluster复制到Tomcat1、Tomcat2的webapps目录下,重启apache,tomcat1,tomcat2

5. 测试Session的复制

通过 http://localhost/TestCluster/test.jsp 访问,输入名称为 name, 值为 Unmi,提交查询,多刷新几次浏览器窗口,你将会看到在两个Tomcat窗口都打印出相同的SessionID及其中的值,并且每次刷新后打印的结果都一样的。

如果不为应用的web.xml加上 ,同样测试上面那个test.jsp页面,每次刷新分流到不同的tomcat上都会产生不一样的SessionID,在同一个tomcat上也是间隔出现不同的sessionID。

更切身的体验是一定要自己动手配置一遍,并仔细观察两个tomcat的控制上的输出。因本文是参考 轻松实现Apache,Tomcat集群和负载均衡 的实践经历,

2.3.4.    测试session复制

启动环境后,启动2个浏览器,得到的是2个不同的session标识,各自提交您输入的key-value后,把一个tomcat2服务关闭掉,再访问页面,发现tomcat2的session会复制到tomcat1中

2.3.5.    后记:

用 WebSphere Application Server ND 版配置过垂直和水平集群,但是自己试验集群环境下的应用却不想搬弄这个庞然大物。眼下急于想体验的就是 Quartz 如何适应集群环境,问题的焦点就是:Quartz 定时任务随 Web 应用启动,而 Web 应用部署在集群环境中,如何保证同一时刻只有一个同名的任务实例在跑。

所以会考虑用Apache+Tomcat配置一个轻量级的WEB应用集群,一般进行HTTP分流都是使用Apache,包括WAS集群也是,很少用IIS的。虽然单纯的用Tomcat的balancer应用也能配置进行负载分流,但那个性能应该好不到哪儿去。

用Apache+Tomcat配置的Web应用集群就是部署起来麻烦些,总是要保持双份的应用拷贝,WAS集群则不需要,不知道Jboss做WEB应用集群是怎么样一种情况。

好了,下面要进行该做的事情了,最后也希望能写个工具能完成从下载到安装配置,启动,停止,重启的全自动化,以及界面的人性化。

3.   实战总结

3.1. 失败总结

在准备软件环境的环节中:最开始的时候小弟下载了Apache2.2.21,mod_jk下载的是2.2.X版本,没有按照网上给出的版本,所有配置都弄好后,访问页面,总是提示404错误

后来换回2.0.55版本,这次实战才成功了。

4.   参考文献

4.1. 结合Apache和Tomcat实现集群和负载均衡

http://blog.csdn.net/kypfos/article/details/3081330

4.2. 揭开J2EE集群的神秘面纱

http://www.kuqin.com/java/20080418/6942.html

实战Apache+Tomcat集群和负载均衡的更多相关文章

  1. 使用Apache + mod_jk + tomcat来实现tomcat集群的负载均衡出现的无法加载mod_jk.conf文件的问题

    用Apache + mod_jk + tomcat来实现tomcat集群的负载均衡的 如果出现了问题,可以用cmd  cd到Apache安装文件的bin下,运行httpd文件,错误信息就会打印出来. ...

  2. Web服务器Tomcat集群与负载均衡技术

    我们曾经介绍过三种Tomcat集群方式的优缺点分析.本文将介绍Tomcat集群与负载均衡技术具体实施过程. 在进入集群系统架构探讨之前,先定义一些专门术语: 1. 集群(Cluster):是一组独立的 ...

  3. linux下配置tomcat集群的负载均衡

    linux下配置tomcat集群的负载均衡 一.首先了解下与集群相关的几个概念集群:集群是一组协同工作的服务实体,用以提供比单一服务实体更具扩展性与可用性的服务平台.在客户端看来,一个集群就象是一个服 ...

  4. tomcat集群和负载均衡的实现(session同步)

      (一)环境说明 (1)服务器有4台,一台安装apache,三台安装tomcat (2)apache2.0.55.tomcat5.5.15.jk2.0.4.jdk1.5.6或jdk1.4.2 (3) ...

  5. tomcat集群与负载均衡

    参考文章http://kalogen.iteye.com/blog/784908,加上了自己调试过程中遇到的问题. 注1:实现此集群的方法参考了网上的很多文章,但由于很多文章都表明是原创的,故无法知道 ...

  6. Nginx实现tomcat集群进行负载均衡

    一.背景 随着业务量和用户数量的激增,单一的tomcat部署应用已经无法满足性能需求,而且对于每次发布项目期间服务不可用的问题也凸显,既然出现了这个问题,那么我们本文就借助nginx来完美的解决这个问 ...

  7. Tomcat集群 Nginx负载均衡 shell脚本实时监控Nginx

    第一步,安装Tomcat 系统环境:Centos7 第1步:下载tomcat安装包 tomcat官网:https://tomcat.apache.org/ 第2步:安装包上传至linux中 第3步:下 ...

  8. 结合Apache和Tomcat实现集群和负载均衡 JK 方式

    本文基本参考自 轻松实现Apache,Tomcat集群和负载均衡,经由实操经历记录而成,碰到些出入,以及个别地方依据个人的习惯,所以在一定程度上未能保持原文的完整性,还望原著者海涵. 因原文中有较多的 ...

  9. 结合Apache和Tomcat实现集群和负载均衡

    http://fableking.iteye.com/blog/360870 TomcatApacheJSP应用服务器Web  本文基本参考自 轻松实现Apache,Tomcat集群和负载均衡,经由实 ...

随机推荐

  1. Swift语言精要 - 扩展(Extension)

    swift的Extension用户在不访问代码的情况下扩展基本结构类型或自定义类 extension Int { var doubled : Int { } func multiplyWith(ano ...

  2. shell alias添加别名使用

    大家一定知道SHELL的基本用法,那么著名的命令:`ll`是代表`ls -l`,那么是怎么实现的哪?其实是添加了一个别名alias ll="ls -l" 我使用alias最多的地方 ...

  3. MapReduce任务参数调优(转)

    http://blog.javachen.com/2014/06/24/tuning-in-mapreduce/ 本文主要记录Hadoop 2.x版本中MapReduce参数调优,不涉及Yarn的调优 ...

  4. Unity3D调用android方法(非插件方式)

    关于Unity3Dproject与androidproject的转换与合并,请參考我的另外一篇博客.假设你对Unity3Dproject增加到androidproject的过程不熟悉.也请先看完以下这 ...

  5. error: expected unqualified-id extern "C" {

    通常为include该文件的头文件内类的声明处未加“:”

  6. java 遍历Map的四种方式

      java 遍历Map的四种方式 CreationTime--2018年7月16日16点15分 Author:Marydon 一.迭代key&value 第一种方式:迭代entrySet 1 ...

  7. urlencode编码问题(以及urlparse)

    # -*- coding: cp936 -*- #python 27 #xiaodeng #urlencode编码问题(以及urlparse) import sys, urllib def urlen ...

  8. Lotus Domino开发心得(一)

    —- Lotus Domino 是当今办公自动化系统的主流开发平台之一,目前大部分企业和机构都在使用Lotus Domino 开发自己的无纸办公系统.在开发过程中,我积累了一些小技巧,现在公布出来,希 ...

  9. windows下Oracle Tuxedo编译应用前需要配置的相关环境变量

    rem (c) BEA Systems, Inc. All Rights Reserved. rem Copyright (c) BEA Systems, Inc. rem All Rights Re ...

  10. 【php】分享一个php转换微信、QQ、微博 特殊非主流 | 杀马特 网名的 function

    1.实现的效果: 2.将上面带有图标.非主流的文字,转换成utf-8格式,能存进去和读出来. 3.code: function emoji_encode($nickname){ $strEncode ...