ZhongZiChang’s Dao

April 9, 2007

用Hadoop搭建分布式存储和分布式运算集群

Filed under: 开发, 搜索, 配置 — 钟 子昌 @ 7:59 pm

1. 列出使用的机器

普通PC,要求:
cpu: 750M-1G
mem: >128M
disk: >10G
不需要太昂贵的机器。

机器名:
finewine01
finewine02
finewine03

将finewine01设为主节点,其它的机器为从节点。

2. 下载和生成

从这里checkout,我选择trunk
http://svn.apache.org/repos/asf/lucene/hadoop/
使用ant进行生成

3. 部署前的准备工作
在主节点的start-all.sh脚本执行后,主节点和从节点的所有服务运行。即这个脚本将启动主节点的服务,并ssh到从所有的节点,继而启动从节点的服务。

start-all.sh这个脚本假定hadoop被安装在所有机器的同一个位置,每一台机器都用同一个路径存放hadoop的数据。

我们需要在每台机器上创建同样的目录结构。
/hadoop
/hadoop-install/hadoop-0.10.0 hadoop的0.10.0版的安装位置
/filesystem hadoop文件系统的根
/home 用户hadoop的主目录

以root登录到所有的机器,创建hadoop用户和目录结构。
ssh -l root finewine01
mkdir /hadoop
mkdir /hadoop/hadoop-install
mkdir /hadoop/filesystem
mkdir /hadoop/home
groupadd hadoop
useradd -d /hadoop/home -g hadoop hadoop
chown -R hadoop:hadoop /hadoop
passwd hadoop hadooppassword

start-all.sh脚本要启动所有机器的服务,需要对所有的机器进行ssh无密码的登录的能力。因此我们需要在每台机器上创建一个ssh key。在这个例子中,主节点也需要启动自己的服务,因此主节点同样需要做无密码的ssh登录设置。

用vi编辑/hadoop/hadoop-install/hadoop-0.10.0/conf/hadoop-env.sh,将下面的环境变量设置:

export HADOOP_HOME=/hadoop/hadoop-install/hadoop-0.10.0
export JAVA_HOME=/usr/java/jdk1.5.0_06
export HADOOP_LOG_DIR=${HADOOP_HOME}/logs
export HADOOP_SLAVES=${HADOOP_HOME}/conf/slaves

这个文件还有很多变量,这些变量的设定影响hadoop的运行。比如你以后执行脚本时发生ssh错误,就需要调整其中的HADOOP_SSH_OPTS变量。
同时需要注意的是,在初始的拷贝操作后,需要在设置hadoop-env.sh文件中的HADOOP_MASTER变量,这样程序才能通过rsync同步主节点的改变到所有的从节点上。

在主节点上创建ssh keys,然后复制到各个从节点上。这些操作必须由先前创建的hadoop用户完成。不要su成hadoop用户来做。开启一个新shell,以hadoop用户的身份登录来完成这些操作。
cd /hadoop/home
ssh-keygen -t rsa (Use empty responses for each prompt)
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /hadoop/home/.ssh/id_rsa.
Your public key has been saved in /hadoop/home/.ssh/id_rsa.pub.
The key fingerprint is:
a6:5c:c3:eb:18:94:0b:06:a1:a6:29:58:fa:80:0a:bc nutch@localhost

在主节点上,拷贝刚才创建的公共key到一个名为authorized_keys的文件:
cd /hadoop/home/.ssh
cp id_rsa.pub authorized_keys

只需在主节点上运行ssh-kegen程序。其他节点的目录结构创建后,将刚才在主节点创建的keys通过scp拷贝到从节点的同样的目录上。
scp /hadoop/home/.ssh/authorized_keys hadoop@finewine02:/hadoop/home/.ssh/authorized_keys
第一次,你需要输入hadoop用户的密码。第一次登录到其他的机器上,ssh提示你是否选择将机器加入到已知的机器列表中,选择yes。这个keys文件拷贝后,以hadoop的身份从主节点到从节点的登录就不需要密码了。
可以从主节点上以hadoop的身份测试:
ssh finewine02
接下来一个命令提示符将直接出现,不需要密码的。

一旦成功在所有机器上创建ssh keys后,就可以开始在从节点上部署hadoop。

4. 部署hadoop到一个机器上

首先,我们部署hadoop到一个节点上(主节点)。确保运行正常后,加入其他从节点。下面所有的操作都是由登录后的hadoop用户进行的。
cp -R /path/to/build/* /hadoop/hadoop-install/hadoop-x.x.x

然后确保这些shell脚本文件都是unix格式,并且是可执行的(这些文件分别在/bin和/conf目录中)。

一个hadoop-site.xml的例子:

fs.default.name
finewine01:9000

The name of the default file system. Either the literal string
“local” or a host:port for NDFS.

mapred.job.tracker
finewine01:9001

The host and port that the MapReduce job tracker runs at. If
“local”, then jobs are run in-process as a single map and
reduce task.

mapred.map.tasks
2

define mapred.map tasks to be number of slave hosts

mapred.reduce.tasks
2

define mapred.reduce tasks to be number of slave hosts

dfs.name.dir
/hadoop/filesystem/name
dfs.data.dir
/hadoop/filesystem/data
mapred.system.dir
/hadoop/filesystem/mapreduce/system
mapred.local.dir
/hadoop/filesystem/mapreduce/local
dfs.replication
1
fs.default.name // 缺省的文件系统 “local”或者”host:port”

hadoop包含两个组件,分别是分布式文件系统和mapreduce功能。分布式文件系统允许你在多台普通机器上存储和复制文件。mapreduce可以让你很容易就执行并行程序任务。

分布式文件系统包含名字节点和数据节点。当一个客户想操作一个在文件系统上的文件时,它首先和名字节点联系,名字节点将告诉它在那个数据节点上可以取得文件。名字节点负责调度和保存那些数据块保存和复制到在那些机器上。数据节点是数据仓库,保存真实的文件数据块。当你在同一台机器上运行名字节点和数据节点的服务时,它同样会通过sockets来通讯,如同在不同机器上一样。

mapreduce是一种分布式运算,就如分布式文件系统一样,只不过分布的一个运算操作,而不是文件。负责mapreduce调度服务器叫做mapreduce job tracker。每一个执行运算操作的节点都有一个守护叫task tracker,task tracker运行并且和job tracker通信。

主节点和从节点的通信以连续的heartbeat(5-10秒)的方式进行。如果从节点的heartbeat停止,主节点将假定该从节点失效并不再使用该节点。

mapredu.job.traker // mapreduce的主节点,”local”或者”host:port”

mapred.map.tasks和mapred.reduce.tasks用于设定并行任务的数量。

dfs.name.dir // 名字节点用于存储数据节点的跟踪和调度信息

dfs.data.dir // 数据节点用于存储实际的数据块

mapred.system.dir // mapreduce tasker 存储自己的数据,仅仅在tasker所在的机器上,不在mapreduce主机上

mpred.local.dir // mapreduce 在节点上存储自己的本地数据。mapreduce使用巨大的本地空间来执行它的tasks。tasks退出时,mapreduce产生的中间文件并没有被删除。在每个节点上,这个属性是一样的。

dfs.replication // 冗余,单个文件将被拷贝到多少机器上。这个值不能高于的所有的数据节点的数量。否则守护启动时会看见很多错误信息。

在你启动hadoop服务之前,确保格式化名字节点

bin/hadoop namenode -format

现在可以启动hadoop服务了

bin/start-all.sh

停止hadoop服务可以使用下面的命令

bin/stop-all.sh

如果设置正确,会看到正常的输出信息

5. 部署Hadoop到多台机器上

一旦你在一台机器上成功运行hadoop,就可以将配置文件拷贝到其他的机器上。
如:
scp -r /hadoop/hadoop-install/hadoop-x.x.x hadoop@finewine02:/hadoop/hadoop-install/

对每台从节点机器都执行这个操作。然后编辑slaves文件,增加每一个slave到这个文件中,每行一个。编辑hadoop-site.xml的值,修改map和reduce的task的数量。修改replication属性。

6. 分布式搜索

产品系统一般每个索引库存100万条记录。50个服务器每秒处理20多个请求。
多处理器、多磁盘的系统,每个服务使用一个独立的磁盘和索引,这样机器的费用可以降低50%,电力解约到75%。一个多磁盘的机器并不能像单磁盘机器一样每秒处理那么多查询,但是它能处理更大的索引数量,因此平均起来效率更高一些。

7. 同步代码到从节点上

hadoop提供同步代码到从节点的能力。这个功能是可选的,因为它会减慢服务启动的速度,并且有时候你并不想同步改变到从节点上。

虽然从节点可以和主节点同步,但在第一次,你仍然需要基本的安装到从节点上,以至同步的脚本生效。这些工作上面我们在上面已经做了,所以不需要做改变。

同步的启动是由主节点ssh到从节点上,并执行bin/hadoop-daemon.sh脚本。这个脚本调用rsync来同步主节点。这表示你需要能以无密码的方式由从节点登录到主节点。前面,我们设置了从主节点到从节点的无密码登录,现在则设置反向登录。

如果是由于rsync选项引起了问题,查看bin/hadoop-daemon.sh脚本,大约在82行有rsync的选项。

那么,首先第一件事情就是在conf/hadoop-env.sh文件中,设置hadoop主节点变量。如:
export HADOOP_MASTER=finewine01:/hadoop/hadoop-install/hadoop-x.x.x

然后拷贝到所有从节点上,scp /hadoop/hadoop-installl/hadoop-x.x.x/conf/hadoop-env.sh hadoop@finewine02:/hadoop/hadoop-install/hadoop-x.x.x/hadoop-env.sh

最终,你需要登录到所有的从节点上,为每台机器创建一个ssh key。然后拷贝回到主节点,并追加到文件/hadoop/home/.ssh/authorized_keys文件中。在每个从节点上进行如下操作:

ssh -l nutch finewine02
cd /hadoop/home/.ssh

ssh-keygen -t rsa (Use empty responses for each prompt)
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /hadoop/home/.ssh/id_rsa.
Your public key has been saved in /hadoop/home/.ssh/id_rsa.pub.
The key fingerprint is:
a6:5c:c3:eb:18:94:0b:06:a1:a6:29:58:fa:80:0a:bc nutch@localhost

scp id_rsa.pub hadoop@finewine01:/hadoop/home/finewine02.pub

在每台从节点机器完成上面的操作后,追加所有的文件到主节点的authorized_keys文件。

cd /hadooop/home
cat finewine*.pub >> .ssh/authorized_keys

当这些操作完成后,每次运行bin/start-all.sh脚本,文件都会从主节点同步的每一个从节点。

8. 查看状况

端口 : 50070
dfs的状态

端口:50060
tracker状态

50030
map/reduce管理

其他端口:
dfs.secondary.info.port 50090
dfs.datanode.port 50010
dfs.info.port 50070
mapred.job.tracker.info.port 50030
mapred.task.tracker.report.port 50050
tasktracker.http.port 50060

October 17, 2006

Apache2.0 PHP GD JPEG PNG FreeType zlib

Filed under: 配置 — 钟 子昌 @ 5:20 pm

+——————————————————————-+
+ Apache2.0 + PHP4 + GD + JPEG + PNG + FreeType + zlib +
+——————————————————————-+
+ $date 20060108 +
+ $author zczhong +
+——————————————————————-+
1. zlib
tar zxvf zlib-1.2.2.tar.gz
cd zlib-1.2.2
./configure
make
make install
ln -s zlib-1.2.2 zlib //for libpng

2. libpng
tar zxvf libpng-1.2.7.tar.gz
cd libpng-1.2.7
mv scripts/makefile.linux makefile
make
make install

3. freetype
tar zxvf freetype-2.1.9.tar.gz
cd freetype-2.1.9
./configure
make
make install

4. jpeg
tar zxvf jpegsrc.v6b.tar.gz
cd jpeg-6b
./configure –enable-shared
make
make test
make install

5. gd
tar zxvf gd-2.0.33.tar.gz
cd gd-2.0.33
./configure –with-png –with-freetype –with-jpeg
make install //sudo

6. apache
source release :
./configure \
–prefix=/usr/local/apache2 \
–enable-so \
–enable-mods-shared=most \
–enable-ssl=shared
binary release :
./configure \
–prefix=/usr/local/apache2 \
–enable-mods-shared=most \
–enable-ssl=shared
make
make install

7. php5(4不支持mysqli)
./configure \
–with-zlib \
–with-gd \
–with-jpeg-dir \
–with-ttf \
–with-freetype-dir \
–with-iconv \
–with-iconv-dir \
–with-apxs2=/usr/local/apache2/bin/apxs \
–with-xmlrpc \
–enable-bcmath \
–enable-gd-native-ttf \
–enable-memory-limit \
–enable-zend-multibyte \
–enable-mbstring \
–enable-dba \
–enable-exif \
–disable-ipv6 \
–disable-path-info-check \
–disable-debug

options:
–prefix=/usr/local/php \
–with-mysqli=/usr/local/mysql/bin/mysql_config \
–with-mysql=/usr/local/mysql \

make
make install
vi apache2/conf/httpd.conf
AddType application/x-httpd-php .php .phtml
AddType application/x-httpd-php-source .phps

October 7, 2006

Tomcat 的集群和负载均衡 - 第二部分

Filed under: 配置 — 钟 子昌 @ 11:14 am

英文原版,中文版由钟子昌翻译整理

在第一部分中,我简述了具有可升级和高可靠性的大型J2EE系统在设计时需要考虑的各种因素。

讨论Tomcat对集群、负载均衡、容错和 session 复制等能力的支持。

在这个部分,我们将看到完整一个集群的架构和部署集群过程的安装和配置细节(通过运行多个Tomcat服务器实例)。

+ 集群的设置

下面列出的是这个 Tomcat 集群例子要实现的目标:

* 可升级能力

* 容错

* 动态配置,易于管理

* 自动发现新成员

* 失败重启和负载均衡,session数据内存复制

* 可插拔/配置的负载均衡策略

* 当一个成员加入或离开时,能通知组成员

* 通过多播的方式,无掉包的信息传输

* 集群对 web 应用和服务器来说都是无缝的。

在这个集群环境中,安装有四个 Tomcat 服务器实例。一个作负载均衡服务器,三个作集群。

集群以垂直缩放的方法设置(多个 Tomcat 服务器实例运行在一台机器上)。

下面是集群的主要组成部分的设置:

* 负载均衡 : 一个Tomcat实例,分发交易到集群的个节点上。代号TC-LB

* 集群 : 集群包含3Tomcat服务器实例,代号分别是 TC01, TC02 TC03

* session 持久化 : 选择内存复制的方式。当session对象改变时,session数据将被复制到所有3个集群成员。

* 失败重启 : Tomcat安装时自带的负载均衡器应用不能处理失败重启。

我写了一个工具类 ServerUtil ,在转发请求给服务器之前检查服务器状态。

有种两种方法检查集群节点的状态。在第一种方法中,使用 McastService 来检测是否有一个指定的服务器实例运行。

而第二种方法则通过以Web页的URL为参数创建一个URL对象,验证集群节点的有效性。

要使用这个类,需要确保 catalina-cluster.jar(位于 %TOMCAT_HOME%/server/ 库目录)

commons-logging-api.jar(位于%TOMCAT_HOME%/bin 目录) 文件在 classpath 中指定。

下面集群的主要组件的架构图。

architecturediagram.gif

1Tomcat集群架构图

+ 安装和配置 Tomcat实例

1,本例中设置 Tomcat 集群环境所用到的硬件和软件

Processor HP Pavilion Pentium III with 800 MHz

Memory 512 MB RAM

Hard Disk 40 GB

Operating System Windows 2000 server with Service Pack 4

JDK Version 1.4.0_02 (Note: JDK version 1.4 or a later version is required to enable Tomcat Clustering)

Tomcat Version 5.0.19

Tools Used Ant 1.6.1, Log4J, JMeter, JBuilder

+ 集群框架的主要元素

++ Java

* BaseLoadBalancingRule

抽象类,封装通用的规则逻辑。在这个例子中的自定义负载均衡规则就是扩展自这个基类。

* RandomRedirectRule

使用“随机”的规则,定义重定向web请求到一个有效的服务器上的逻辑。使用当前系统时间作为种子,生成一个随机的号码。

* RoundRobinRule

这个类定义一个负载均衡的逻辑,基于“轮循”规则。当一个请求进入,它将其重定向到集群成员列表中的下一个成员。

使用一个静态变量来跟踪下一个有效的集群成员,每处理一个请求,就将这个值加1

* ServerUtil

一个工具类,用来检测指定的集群节点是否有效。

这个类用 McastService org.apache.catalina.cluster.mcast 包)来检测某集群成员是否离开了这个组。

下面的类图表示这些Java类之间的关系。

classdiagram.gif

2 集群应用类图

++ 配置文件

* server.xml

用于对 Tomcat 服务器实例进行集群配置。这个版本的Tomcat安装后,server.xml文件中包含被注释掉的集群配置细节。

* web.xml

在这个文件中可指明该web应用的session数据需要被复制。

* rules.xml

这个文件用来定义的负载均衡规则。

++ 脚本

* test.jsp

一个简单的测试 JSP 脚本,用于检查服务器的状态。显示运行的Tomcat实例的名字和系统时间。

* testLB.jsp

在本应用中,这个是起始页面。它使用 HTML 重定向将web请求转发到负载均衡过滤器上。

* sessiondata.jsp

这个脚本用来验证当一个集群节点挂起时,session数据并没有丢失。显示session的内容,使用 HTML 字段操作 HTTP session 对象。

* build.xml

Ant build 脚本,让启动和停止Tomcat实例的任务实现自动化(由Ant 1.6.1 用来执行这个脚本)。一旦某个Tomcat实例启动成功,你可以通过指定IP地址和端口号,调用test.jsp来验证该Tomcat实例是否在运行。这个JSP页将显示当前系统时间和Tomcat实例的名称。你需要改变 build.properties 文件中的 home 目录的指定,在你自己的环境中运行这个脚本。

build 脚本中用于启动或停止 Tomcat 实例的几个 targets:

* 调用 target “start.tomcat5x” 启动一个特定的 Tomcat 实例(例如: tomcat50)

* 调用 stop.tomcat5x 停止一个特定的Tomcat实例

* 调用 stop.alltomcats 中止所有运行的 Tomcat 实例

+ 范例代码

本例子的代码 tomcatclustering.zip。安装完 Tomcat 服务器实例后(4),解压这个zip文件中的文件到tomcat目录。

例子代码使用RoundRobinRule作为负载均衡规则。如果您想使用随机的重定向规则,修改rules.xml文件(tomcat50/webapps/balancer/WEB-INF/conf目录中)

注释掉 关于 RoundRobinRule 的元素,取消关于 RandomRedirectRule 元素的注释。 同样,如果您想用两个实例,而不是三个,注释掉第三个,并改变maxServerInstances属性的值为2(替换原来的3)。

注意:缺省情况下,tomcat安装后会包含好几个其他的应用,我删除了所有其他的web应用(jsp-examples,等等),仅仅保留 balancer 和 本例的web应用。

HTTP 请求流程

本例集群环境中的 web请求流程如下:

1. 运行起始页面(http://localhost:8080/balancer/testLB.jsp)

2. JSP将请求重定向到负载均衡过滤器(URL:http://localhost:8080/balancer/LoadBalancer)

3. 负载均衡器(TC-LB)拦截web请求,并根据配置文件中指定的负载均衡规则重定向到下一个有效的集群成员(TC01, TC02 或者 TC03)

4. 被选中的集群成员的sessiondata.jsp (位于 “clusterapp” web应用)被调用;

5. 如果 session 被修改, ClusterAppSessionListener session 监听器方法将被调用,用于记录 session 修改事件;

6. sessiondata.jsp web浏览器上显示session的详细内容(例如session id,最后访问事件,等等)

7. 随机停止一个或两个集群节点(调用 Ant 脚本的 “stop.tomcat5x” target );

8. 重复上面7个步骤,查看是否对某个有效的集群成员的请求失败。同时,检查session信息是否在集群成员内部进行无数据丢失的拷贝。

sequencediagram.gif

3 表示一个web请求的流程

集群应用的序列图

+ 集群的配置

在这个集群中,运行一个“clusterapp”web应用。为优化session复制,所有的实例拥有一样的目录结构和内容。

由于Tomcat服务器实例使用IP多播来传输session,我们必须确定集群机器上的IP多播功能是可用的。为验证,你可以运行《如果编写多播服务和客户程序。Tomcat:The Definitive Guide》这本书中的例子Java程序MulticastNode,或者,参考http://java.sun.com/docs/books/tutorial/networking/datagrams/broadcasting.html

当一个集群节点启动,集群中的其他成员将在服务器控制台上显示一条记录信息,说明一个成员已经被添加到集群中。类似的,当一个集群节点下线,其他的节点将在控制台上显示一个集群成员离开的记录。

clustercommunication.gif

4 当集群中添加或者删除一个成员时所产生的记录信息

按照下面的步骤可打开Tomcat服务器的集群和session复制功能:

1.所有的session属性必须实现java.io.Serailizable接口

2.取消对server.xml文件中Cluster元素的注释。userDirtyFlagreplicationMode两个属性用于优化频率和session复制机制。

3.取消对server.xmlValue元素的注释。ReplicationValue用于拦截HTTP请求并在集群成员内复制session数据。Value元素有一个“filter”的属性,可以用来过滤不会对session进行修改的请求(HTML页面和图像文件)

4.由于全部Tomcat实例都是运行在同一台机器上,每个Tomcat实例的tcpListenPort属性需要设置成唯一。名字格式为 mcastXXX(mcastAddr, mcastPort, mcastFrequency, mcastDropTime) 的属性都是用于集群关系的多播ping,而名字格式为tcpXXX(tcpThreadCount, tcpListenAddress, tcpListenPort tcpSelectorTimeout) 是用于 session 复制(下面的集群配置参数表显示Tomcat服务器实例的不同配置)

5.web.xml meta文件(位于clusterapp\WEB-INF目录)应该拥有元素。为一个指定的web应用复制session状态,distributable元素必须被定义。这表示如果你有不止一个web应用需要session复制,那么你需要增加distributable到所有web应用的web.xml文件中。《Tomcat:The Definitive Guide》这本书的“Tomcat集群”这章对这个问题有很好的解释。

2 集群的配置参数

配置参数 实例 1 实例 2 实例 3 实例 4
Instance Type 负载均衡器 集群节点1 集群节点2 集群节点3
Code name TC-LB TC01 TC02 TC03
Home Directory c:/web/tomcat50 c:/web/tomcat51 c:/web/tomcat52 c:/web/tomcat53
Server Port 8005 9005 10005 11005
Connector 8080 9080 10080 11080
Coyote/JK2 AJP Connector 8009 9009 10009 11009
Cluster mcastAddr 228.0.0.4 228.0.0.4 228.0.0.4 228.0.0.4
Cluster mcastPort 45564 45564 45564 45564
tcpListenAddress 127.0.0.1 127.0.0.1 127.0.0.1 127.0.0.1
Cluster tcpListenPort 4000 4001 4002 4003

注意:由于所有的集群成员都是运行在同一台机器上,他们使用同一个IP地址(127.0.0.1)

如果你没有使用Ant脚本启动和停止Tomcat实例,不要在你的机器上设置CATALINA_HOME环境变量。如果这个变量被设置,所有的实例都尝试使用同一个目录(CATALINA_HOME 变量指定的)来启动Tomcat实例。结果只有第一个实例能成功启动,其他的实例会崩溃,出现邦定异常信息,通知端口已经被使用:“java.net.BindException: Address already in use: JVM_Bind:8080”

+ 负载均衡的设置

我写了两个简单,自定义的负载均衡规则(RoundRobinRule RandomRedirect),用于重定向进入的web请求。这些规则都是基于负载均衡算法(例如轮循和随机重定向)。你可以编写基于其他因素(如加权和最后访问时间等)类似的自定义负载均衡规则。Tomcat 负载均衡器提供一个样例(基于参数的负载均衡规则),它根据HTTP请求的参数决定重定向web请求到不同的URL上。

保持 server.xml (TC-LB实例) 中关于集群和value元素的注释状态,因为该实例并非集群成员。

+ 测试的设置

++ session持久化测试

session持久化测试中, 主要目标是在一个web请求过程中验证当一个集群成员崩溃后,session数据并没有丢失。JSP sessiondata.jsp 用来显示session内容。这个脚本同时提供HTML text字段,用于添加/修改/删除 session属性。在添加属性给HTTP session后,我随机的停止集群节点,并检测有效的集群成员上的session

++ 负载测试

负载测试的目的是研究自定义的负载均衡算法,当一个或多个节点停止服务的情况下,web请求如何被有效的分发到指定的集群节点。JMeter负载测试工具就是用来模拟多并发web用户的情况。

测试负载均衡的步骤如下:

1. 启动负载均衡器和集群实例。

2. 运行起始JSP 脚本(testLB.jsp)。

3. 通过手动停止一个或者多个容器来模拟服务器崩溃。

4. 检查负载分发模式。

5. 重复100次步骤14

所有的记录信息被重定向到一个文本文件,叫tomcat_cluster.log(位于 tomcat50/webapps/balancer目录)。在序列图中(图2)的所有web对象的响应时间是使用Log4J信息记录。表3是耗时(毫秒)表。

下表表示负载测试的耗时(使用RoundRobinRule算法)和负载分发百分比(使用RandomRedirectRule算法)。

Table 3. 负载测试的耗时

# Scenario testLB.jsp
(ms)
RoundRobinRule
(ms)
sessiondata.jsp
(ms)
Total
(ms)
1 三个服务器都在运行 54 76 12 142
2 两个服务器实例在运行(TC02 was stopped) 55 531 14 600
3 一个服务器在运行
(TC01 and TC02 were stopped)
56 1900 11 1967

注意:所有的耗时是100个并发用户的平均值。

4. 当使用随机负载均衡规则是的负载分发。

# Scenario TC01 (%) TC02 (%) TC03 (%)
1 所有服务实例在运行 30 46 24
2 两个服务实例在运行 (TC02 was stopped) 56 0 44

注意:负载分发的百分比也是基于100个并发用户的负载。

+ 总结

session持久化测试中,增加session属性后,其中的一个集群节点挂起,通过验证,证实在服务器停机时间,session属性并没有丢失。session属性的具体内容记录在文本文件中。

在负载测试中,当一个或者两个服务器实例停止,仅有一个 Tomcat 实例运行,回应的时间比起所有三个实例都有效时长。当原先停止的实例重新启动,负载均衡器自动重新发现这些服务器有效,将接下来的请求重定到这些服务器实例上,马上能提高回应的时间。

这里用来发现集群成员是否有效的机制(ServerUtil)并非是最快的方法。

这个集群设置的一个缺陷是它仅仅提供一个负载均衡器。当用作负载均衡器的 Tomcat 实例挂起时会发生什么事情呢?就没有途径转发请求到集群,这个结果叫做单点失败(SPoF).其中一个解决方法就是有另外一个Tomcat实例运行着,作为一个备用负载均衡器。如果主负载均衡器崩溃时,备用均衡器将接替它的工作。典型的 高可靠性集群(HA) 包含两个负载均衡器防止 SPoF 的情况发生。

在上面的例子中,所有Tomcat实例(包括负载均衡器)都是配置在同一台机器上运行。更好的设置就是在一台独立的机器上运行负载均衡器。同样,限制每台机器拥有两个集群节点,充分利用水平缩放的方法来保证集群的效率。

J2EE Web 应用服务器来讲,HTTP session 复制是一种昂贵的操作。J2EE集群环境下,session管理的实现应该在项目的分析和设计阶段中就需要考虑。编码时必须想着集群环境。如果没有在设计阶段就考虑集群的实现,为了让应用能在集群环境下工作,代码可能需要全部重写。这会造成非常大的影响。

如果web应用支持各种对象缓存机制,那么在应用开发的初始阶段,集群环境中的缓存对象就应该被考虑。这是非常重要的,因为对于提供精确和即时的事务数据给Web用户,在所有集群的节点中保持缓存数据的同步是非常危险的。

一旦J2EE集群成功设置和运行,它的管理和维护将变得非常重要。保持集群的运行和将应用的变化推到所有的集群节点上。需要有一个方法提供这些服务,实现一个监视器服务,周期性的检测服务器的有效性,如果集群中有节点无效,它将会发出通知。这个服务有规律间隔的检测失效的节点,并从活动集群节点列表中删除失效的节点。它应该拥有一种能力,当改变和更新出现时,它能同步和更新集群中所有的服务器。由于对web应用的所有的请求必须通过负载均衡系统,这个系统能检测到活动session的数量,活动session的数量,回应次数,高峰负载的次数,高峰其间活动session的数量,低谷其间活动负载的数量,等等。这些审计信息可以为提高性能,优化整个系统作为参考。

在这里,可以通过手动调整配置文件(server.xml rules.xml)满足设置集群和负载均衡器的所有配置需求。如果Jakarta项目组提供基于Web的集群管理工具,那我们就可以通过使用管理工具修改配置来管理集群和负载均衡。

+ 资源

* Tomcat 5 主页

* Tomcat 站点的集群主页

* Tomcat 站点的负载均衡主页

* Tomcat: The Definitive Guide by Jason Brittain and Ian F. Darwin

* Java Performance Tuning, 2nd Edition by Jack Shiraji

* Creating Highly Available and Scalable Applications Using J2EE, The Middleware Company, EJB Essentials Training Class Material

September 2, 2006

Tomcat 的集群和负载均衡 - 第一部分

Filed under: 配置 — 钟 子昌 @ 11:03 pm

英文原版,由钟子昌翻译整理

最新版的 Tomcat servlet 容器提供集群和负载均衡的能力,对于部署可升级、健壮的Web应用来说,这是必不可少的。这篇文章的第一部分描述集群与负载均衡的特性和要点。第二部分举出一个如何配置 Tomcat 集群的实例,同时介绍在集群环境中使用内存复制的方式来实现 session 的持久化 。

Tomcat 5 自带一个基于规则的负载均衡应用。根据两种负载均衡策略(扩展自规则API,分别是 round-robin 和 random 算法)来重定向进入的请求。讲述运行在集群环境中的样例Web应用的性能评测。通过负载测试工具 JMeter 模拟多个Web用户的方式来研究负载均衡机制。

+ 大型系统的设计

企业级的 Web 门户应用必须提供可升级能力(scalability)和高可用性(HA),在同一个网站下为数以百万计的用户提供服务。可升级是系统可以通过增加服务器来支持更多的数量的用户。高可靠性是系统提供基本的冗余能力。在集群中的某个成员失效时,其他成员能透明的接替处理对 Web 服务的请求。在集群的环境中部署一个 Web 门户应用能提供门户网站需要的可升级和高可用性的能力。基本上,集群的主要目的是防止某些站点出现当机的问题,应付系统的单点失效。

大型系统的设计目的是在企业应用环境中提供稳定的服务,确保最少的当机时间和最大的可升级能力。运行的不是单一的服务器,有多个协作服务器也同时在运行。为了达到可升级的能力,集群能任意增加机器数量,而为了最少的当机时间,集群中每一个组件都是可冗余的。大型系统的主要因素就是集群,包含负载均衡、容错、session 状态持久化等特性。在集群中,通常负载均衡器(硬件或软件)布置在应用服务器的前面。这些负载均衡器通过使用重定向 Web 交易到相应的集群成员来在集群的节点内分发负载,并且在同时检查集群内是否有服务器失效。

+ 集群

集群的定义好比一组应用服务器透明的运行J2EE应用,就象在一台机器上执行一样。有两种方式的集群:垂直缩放和水平缩放。垂直缩放可以通过提高单台机器上的运行服务的数量来达到,水平缩放就要提高集群内机器的数量。水平缩放比垂直缩放更可靠。使用垂直缩放,机器的处理能力、CPU的使用情况和JVM堆内存配置都是决定应该在这台机器上运行多少的服务器实例的主要因素(众所周知的 server-to-cpu 比率)。

J2EE集群内的服务器一般使用三种配置选项的一种:
独立(independent),每个服务都有属于自己的一份应用程序文件的拷贝;
共享文件系统(shared file system),集群内所有的服务器拥有一个共同的存储设备,所有服务器的应用文件都是从该设备取得;
第三方的配置方法(managed),有一台管理服务器控制对应用内容的访问,通过”pushing“相应的应用内容到管理服务器作为响应。管理服务器能保证集群中所有的成员的应用有效。当部署应用时,所有的服务器都会更新,而反部署时,所有的服务器都会将应用删除。

集群可以在J2EE应用的各个层次使用,包括数据层。一些数据库提供商提供集群数据库,支持多个数据库服务器中的数据复制、客户透明访问( servlet 容器或者应用服务器无须知道从那个数据库服务器中取的数据)。JDBC集群的例子是 Oracle9i’s Real Application Clusters(RAC) 和集群 JDBC(C-JDBC)。RAC 支持数据库连接的失败重启、透明变更JDBC连接、请求到一台恢复后的数据库节点。C-JDBC是一个开放源的数据库集群,允许 Web 应用通过 JDBC 透明的访问数据库集群。实现数据库节点内的负载均衡和失败重启。

++ Tomcat 的集群

在 Tomcat 先前的版本中(4.1)可以通过第三方的jar文件来实现集群。在一个集群内安装和配置多个 Tomcat 实例不是一件容易的事情。将集群的能力增加到开放源的 servlet 容器(Tomcat)和应用服务(JBoss)中,JavaGroups 是一个不错的选择。在最新版本的 Tomcat,集群已经成为主要安装包的一部分。将第三方的集群实现对 Tomcat 服务器的影响减少到最低。

在典型的集群环境中,为了让成员之间相互协作和复制状态,它们之间需要互相通信。
组的通信可以使用 point-to-point RMI(TCP-IP)或者IP多播两种方式。
大部分的J2EE应用服务器(如JBoss、Oracle、WebLogic和Borland) 都是使用IP多播让集群成员进行通信,在集群内发送 state/update/heartbeat 数据给其他成员。
Tomcat 集群成员的通信是如下进行的:所有的集群成员用多播 ping 消息来对话。每一个 Tomcat 实例将发送一个消息,广播其 IP 地址和 TCP 监听端口(为session replication)。如果在给定的时间帧内,某个实例没有接收到这些信息,那么该实例就会被认为是当机。

另一个比较流行的概念是 farming,提供集群范围内的 Web 应用的热部署。在服务器 farm内,一个 Web 应用通过拷贝 war 文件到集群内的一个节点上进行部署,farming将会把这个 Web 应用部署到整个集群中。类似的,从一个集群节点上删除 war 文件,farming 将会在集群内所有的节点上反部署这个 Web 应用。Tomcat的集群文档中讲到在接下来的版本中将会支持 farming 能力。

+ 负载均衡

一种机制,能将服务器的负载被分发到集群的不同节点上。基于负载均衡策略,应用不再在单一的服务器上执行,而是在动态选择的服务器上。当客户请求服务,一个或多个协作服务器处理这个请求。负载均衡为集群提供单一的入口,就如直接和独立的 Web 或者应用服务器交互一样,这对客户来说是透明的。

两种比较流行的负载均衡方法分别是 DNS 轮循 和 硬件负载均衡。 DNS 轮循提供单个逻辑名称,返回集群内某台机器的ip地址。这种方式是廉价、简单并且容易配置,但它并没有提供服务器之间的联系和高可靠性的能力。相对来说,硬件负载均衡通过虚拟 ip 地址来解决 DNS 轮循存在的问题。负载均衡器有一个单独的ip地址,映射到集群内的每一个节点上。负载均衡器接收到请求,然后重写头部来指向集群内的其他机器。如果我们从集群内移出一些机器,那么这个改变马上生效。硬件负载均衡的好处是服务器的联系性和高效。缺点是昂贵并且设置复杂。(作者在这里没有提到这种硬件的负载均衡方式可以用软件实现,这种方式称为 Vitrual Server )。

对于负载的分发有多种算法,下面是一些较为常用的算法:
* round-robin 轮循
* random 随机
* weight-based 权重
* minimum load 最小负载
* last access time 最后访问时间
* programmatic parameter-based 负载均衡器根据方法中的参数来选择服务器

负载均衡算法涉及统计上的差异,速度和简单性。举个例子,weight-based算法比其他的算法需要更长的计算时间。想得到对负载均衡的更详细的解释,参考ONJava的文章 “Load Balancing Web Applications

++ Tomcat的负载均衡

先前版本的tomcat并没有提供负载均衡的能力。集成 apache web server 和 tomcat servlet container 就是一个不错的处理 web 请求的负载均衡集群。在Apache+Tomcat 中,被称为 Tomcat Worker 的 Tomcat 实例被配置来实现负载均衡。

Tomcat 5 提供三种方法来实现负载均衡:
分别是用JK本地连接器,用 Apache2 的 mod_proxy 和 mod_rewrite,或者用 balancer web app。
在这篇文章中,我们重点使用第三种,使用 balancer web application 来重定向web请求到集群内的各个节点。这个负载均衡的应用是基于规则的。使用servlet filter机制重定向进入的web请求到下一个有效的集群成员上。servlet filter在servlet 2.3 规范中有详细的介绍。过滤器(servlet filter)可以在 web 应用中负责多种不同的任务。例如JAAS认证,加密,记录日志和审核,数据压缩,XSLT 过滤器转换 XML 内容等等。就如 Tomcat 均衡器网站讲述的那样,这个均衡器应用并非设计用来替代其他的强大的负载均衡机制。它用简单并且易于扩展的方法来重定向交易(traffic)到其他的服务器上。检查均衡器应用所提供的样例Java类,了解均衡器如何用不同的规则标准来完成各种不同的任务。

负载均衡配置文件(rules.xml)包含不同的规则和重定向的URLs。balancer filter 检查RuleChain来决定将请求重定向到那里,按照rules.xml中指定的顺序来检查规则。当一条规则匹配时,过滤器停止评估,并且重定向请求到规则指定的URL上。

+ 容错

容错是系统的一种能力,能够做到系统中的一个服务器失效时,另一个有效的服务器能够接管,这对最终用户来讲是透明的。理想的情况是集群服务监测到集群内其中的一个服务器失效而不能处理请求时,停止发送请求到该服务器。然后周期性的检查集群中的该成员是否再次生效,如果生效,将再次将其添加到活动服务器节点池中。

++ Tomcat 的容错

Tomcat 5 并没有提供一个内建的失败重启机制来检查集群成员的崩溃。希望,未来的版本能提供这个功能,用来发现集群内有效的机器,确定那些成员能处理进来的请求。

集群解决方案一般提供两种层次的失败重启能力:

* 请求层次的失败重启
如果集群中的一台服务器挂起,所有接下来的请求将会被重定向到集群中的其他服务器。这包含一种 heartbeat 机制来保持跟踪服务状态和避免发送请求到没有回应的服务器上。在我们的集群设置中,一个 Tomcat 实例扮演着负载均衡器的角色,处理请求层次上的失败重启,并转发 web 请求到集群中的其他节点。
* session 层次的失败重启
一个 web 客户可以拥有一个由HTTP服务器维持的 session。如果集群中的其中一台服务器挂起,集群中的另一台服务器能接手前一台服务器的session,保持连续性。这需要在集群内复制 session 数据。拥有 session 复制能力的 Tomcat 集群能处理 session 层次的失败重启。

+ session 状态的持久化

失败重启和负载均衡都需要集群内不同的服务器之间能进行 session 状态的复制。当原来的服务器失败时,session 状态复制允许客户无缝的从集群中的另外一台服务器上取得 session 信息。这个状态可以包括系统状态和/或应用状态(应用状态包含存储在 HTTP session中的对象和数据)。session复制的主要目的是当集群成员崩溃、为应用升级或者系统维护停止工作时能够不丢失任何 session 的内容。

谈到session的持续化,有一个简单的集群方案,集群成员不知道其他成员的session状态。在这个方案中,用户session完全在一台服务器上,由负载均衡器来选择。这叫做粘性session(或者叫session affinity)。因此seesion数据保存在接收web请求的集群成员上。

从另外一方面来将,集群可以以这样的一种方式实现,每一个集群成员完全明白其他成员的 session 状态,通过 session 状态的周期性传播到其他备用集群成员。这种session 被称为复制 session。

有三种方法实现session的持久化:
* 内存对内存的复制;
* 文件系统 session 持久化, session 信息从一个中央文件系统读写;
* 数据库 session 持久化, session 数据存储在一个JDBC数据存储器。

在内存 session 持久化中,当 HTTP session 中的独立的对象改变,这个对象将会被序列化到其他的备用机器上,而在数据库session持续化中,当 session 中的任何对象改变时,session 中的所有对象将被一起序列化。

数据库/文件系统session持续化的缺点是限制了当在 HttpSession 存储大型或大量对象时的可伸缩性。每一次用户增加一个对象到 HttpSession 中,session中所有的对象都会被序列化并被写到数据库或者共享文件系统中。

++ tomcat 中的 session 复制

当前 Tomcat 版本的 session 复制是一种 all-to-all 的复制,即在任何时,session 中的属性被传播到集群的所有成员。当集群小的情况下,这个算法是高效的,为应付大型集群的情况,Tomcat 的下一个版本将提供主-从复制,session 将仅仅被保存在一个或者两个备份服务器上。

在tomcat中,有三种类型的session复制机制:
* 内存中复制,使用Tomcat 5自带的SimpleTcpCluster(在org.apache.catalina.cluster.tcp包中,文件为server/lib/catalina-cluster.jar);
* session持久化,保存session在一个共享数据库上(org.apache.catalina.session.JDBCStore);
* 在共享的文件系统上保存session的状态 (org.apache.catalina.session.FileStore, part of catalina-optional.jar)。

+ 实现一个J2EE集群需要考虑的因素

设计J2EE集群需要考虑很多因素。下面这些问题都是在一个大型的J2EE系统需要考虑的(这个列表取自EJB基本训练文档-“用J2EE创建高可用性和可扩展的应用)。

++集群

* 那种类型的集群适用:垂直还是水平扩展?
* 在那个层次实现集群:web服务器或者,servlet,JSP容器还是HTTP session对象;或者EJB,应用服务JMS和JNDI对象还是数据库集群?

++ 负载均衡

* 选中一个服务器的时间(也就是affinity,姻亲关系):每次请求,每个事务或者每次会话?
* 如何选择服务器(也就是负载均衡策略):randomly, round-robin, weight-baesd, least loaded server,或者由应用决定?
* 负载均衡在哪个位置上实现,客户端还是服务器?

++ 容错

* 服务器如何进行失败重启检测?
* 什么时候适合失效重启和尝试使用其他的服务器?
* 失败节点上的系统和应用的状态?

++ session 状态持久化

* 状态如何传播?
* 传播的频率?
* 对象状态如何持久化?
* 状态持久化机制的效率如何?
* 复制的状态是否粘性?
* 网络环境对session状态的复制有限制吗?

+ 建议的集群设置

下面列出在推荐的集群环境中,我要达到的目标:
* 可升级能力高
* 容错
* 动态配置,易于管理
* 自动发现新成员
* 失败重启和负载均衡,session数据内存复制
* 可插拔/配置的负载均衡策略
* 当一个成员加入或离开时,能通知组成员
* 通过多播的方式,无掉包的信息传输
* 集群对 web 应用和服务器来说都是无缝的。对客户端和服务端都是透明的。客户透明是指客户端无须知道集群服务或者集群的设置。集群的识别和访问和单机一样,而不是各自独立的服务。服务器透明是指服务器上的应用程序代码不需要知道是在一个集群之内。应用程序代码不能和其他的集群成员通信。

+ 总结

在这篇文章的第二部分,我们看看如何部署一个集群(运行多个Tomcat服务实例)来达到上面提交的目标。我们将讨论在Tomcat 5 中实现 session 复制的集群架构和配置细节。

待续 …

关于:
Srini Penchikala 是Flagstar Bank的信息系统问题的专家。

Powered by WordPress