一简介
每个Tomcat worker是一个服务于web server等待执行servlet的Tomcat实例例如我们经常使用像Apache之类的web server转发sevlet请求给位于其后面的一个Tomcat进程(也就是前面所说的worker)本文详细介绍了如何配置各种类型worker和 loadbalance并说明了各种类型worker的特性和loadbalance配置的原理
二为什么使用Tomcat workers
上文描述了一个非常简单的结构事实上能够配置多个Tomcat workers来处理web server转发的servlet请求而这样配置的理由不外乎以下几种假想环境
* 我们在开发环境中发布不同的Tomcat workers为各自不同的应用服务当然在开发环境中的开发者共享同一个web server但是每个Tomcat worke服务于拥有它的开发者
* 我们在不同的Tomcat进程上定义各自的虚拟主机这样不同的公司可以使用各自的web site从而使他们的web site得到了合理的分割
* 我们提供负载平衡的web site也就意味着同时使用多个Tomcat workers而每个Tomcat worker具有独立的主机并且在workers之间要分配通过web server转发来的请求
当然这些假想情况也许并不能涵盖使用多个workers的所有状况
三workersproperties配置说明
定义Tomcat workers的方法是在apache的conf目录下编写一个名为workersproperties的属性文件本文将详细解释如何进行配置的
定义Workers列表
定义workers的方法就是在apache的conf目录下编写一个workersproperties文件使其作为apache的插件来发挥作用
定义workers列表的格式
workerlist =<使用分割的worker 名字列表>
例如
workerlist= worker worker
当apache启动时workersproperties作为插件将初始化出现在workerlist列表中的workers
定义Workers的类型
每个被命名的worker都应有一些关于其自身的附加信息这些信息包括了worker的类型和其它相关信息这里讨论的是JK中定义的workers类型
定义worker类型的格式
worker worker名字 type =<worker类型>
worker名字的命名最好遵循java的命名规范
worker类型取值于下面的表格
定义一个名为local的worker其使用ajpv协议与Tomcat 进程通讯
workerlocaltype=ajp
定义一个名为remote的worker其使用ajpv协议与Tomcat 进程通讯
workerremotetype=ajp
定义一个名为fast的worker其使用JNI的方式与Tomcat 进程通讯
workerfasttype=jni
定义一个名为loadbalancer的worker其作为对多个Tomcat 进程的负载平衡使用
workerloadbalancertype=lb
各个类型具有不同的行为我们在下文中会详细解释
设置Worker属性
在定义worker之后还需要提供各个worker的属性这些属性的定义使用下面的方式
worker<worker名字><属性>=<属性值>
ajp类型的Worker属性
ajp类型的worker工作时使用基于TCP/IP socket的ajpv协议转发请求给进程外Tomcat worker
ajp worker属性如下
host
侦听ajp请求的Tomcat worker主机
port
Tomcat worker主机的侦听端口
lbfactor
当此Tomcat worker被用于一个负载平衡worker使用时此属性将被使用它定义了此worker的负载平衡权值
例如下面的worker定义了一个位于主机上的Tomcat它使用端口侦听apache发来的请求并具有的负载权值
workerworkerhost=
workerworkerport=
workerworkerlbfactor=
注意在ajpv协议中针对每个请求都要一个连接建立使用关闭其默认侦听端口为
ajp类型的Worker属性
ajp类型的worker工作时使用基于TCP/IP socket的ajpv协议转发请求给进程外Tomcat worker
ajpv协议与ajpv协议的主要不同
* ajpv具有更丰富的二进制协议它使用将频繁使用的字符串编码为小整数的方式对请求数据进行压缩
* ajpv重用打开的socket并保留这些打开的socket以处理将来的请求这在apache与Tomcat之间具有防火墙的网络环境下是必要的
* ajpv具有对SSL信息的处理能力以致容器能够实现SSL的相关方法(如isSecure())
注意ajp当前只能用于支持进程外协议的Tomcat x x and
下表描述了ajpworker接受的属性
host
侦听ajp请求的Tomcat worker主机
port
Tomcat worker主机的侦听端口
lbfactor
当此Tomcat worker被用于一个负载平衡worker使用时此属性将被使用它定义了此worker的负载平衡权值
cachesize
当在多线程的web server(例如apacheIIS Netscape)中使用JK时此属性是有效的如果将cachesize的值设置为较高的值这些支持多线程的web server将获得很好的处理能力如果此属性不被设置则连接cache特性将失效
cache_timeout
本属性用于声明JK在cache中保留一个打开的socket的时间它对减少web serer的线程数有所帮助
使用cache_timeout的原因
周所周知一个身背重负的web server(例如apache)建立childs/threads来处理负载而当负载减少时它将销毁无用的childs/threads每个 child在转发请求给Tomcat时要打开一个ajp连接而在Tomcat那一端也将建立一个ajp线程与之通讯但是问题出现在一个 ajp连接建立完成后child没有及时的释放那个ajp连接由于web server将保持它的childs/threads运行已处理高负载即使childs/threads处理快速的静态内容在Tomcat端也将积累很多的无用ajp线程
socket_keepalive
当防火墙位于web server与Tomcat之间时防火墙将尝试断开未激活的网络连接此属性将告诉操作系统在未激活的连接中发送KEEP_ALIVE信息(发送间隔时间依赖于操作系统的设置一般为秒)这样将防止防火墙切断未激活的网络连接
但此设置并不是万能钥匙它对于某些防火墙也无能为力
socket_timeout
此属性说明连接在未激活的状况下持续多久web server将主动切断之这是一个使Tomcat端的陈旧线程不致过多的好方法但是也带来了在下一次请求到来时需要重新打开socket的开销此属性与cache_timeout有类似的功效但是它工作在noncache模式
connect_timeout
web server在连接建立后将一个PING请求发送到ajp协议的连接上 此属性说明了web server等待PONG回应的时间(以ms为单位)此属性在jk 版本被增加进来以求避免Tomcat的死机Tomcat + + and +实现了对使用ajp的 ping/pong的支持此属性默认为失效的
prepost_timeout
web server在转发一个请求后将一个PING请求发送到ajp协议的连接上此属性说明了web server等待PONG回应的时间(以ms为单位)此属性在jk 版本被增加进来以求避免Tomcat的死机Tomcat + + and +实现了对使用ajp的 ping/pong的支持此属性默认为失效的
reply_timeout
此属性告诉web server在接到远端的Tomcat已死并实时的切换到集群中的另外一个Tomcat的回应之前等待一段时间默认情况下web server将永远等待属性值为web server要等待回应的时间(以ms为单位)所以如果具有运行时间较长的servlet时设置其值要小心此属性在jk 版本被增加进来以求避免Tomcat的死机和在支持ajp的servlet引擎上发生的问题此属性默认为失效的
recovery_options
此属性说明了web server在检测到Tomcat失败后如何进行恢复工作默认情况下web server将转发请求给处于负载平衡模式中的另一个Tomcat属性值为说明全部恢复属性值为说明如果在Tomcat接到请求后出现失败状况则不进行恢复属性值为说明如果在Tomcat发送http头给客户端后出现失败状况则不进行恢复属性值为说明如果在Tomcat接到请求后出现失败状况或者在Tomcat发送http头给客户端后出现失败状况则不进行恢复此属性在jk 版本被增加进来以求避免Tomcat的死机和在支持ajp的servlet引擎上发生的问题此属性默认为全部恢复
例如一个名为worker的worker的配置
workerworkerhost=
workerworkerport=
workerworkerlbfactor=
workerworkercachesize=
workerworkercache_timeout=
workerworkersocket_keepalive=
worker worker want ajp connection to be dropped after mn (timeout)
workerworkersocket_timeout=
说明上例中的worker要求操作系统在连接上发送KEEPALIVE信号
注意在ajpv协议中默认端口为
设置lb Worker属性
负载平衡类型的worker并不与Tomcat worker通讯它负责管理这些Tomcat worker
其管理范围如下
* 初始化在web server的worker列表中定义的worker
* 使用worker的负载平衡权值执行基于权值的负载平衡将数量多的请求发送到负载平衡权值高(在web server看来就是更加健壮的)的worker
* 维护在同一个Tomcat worker上的同一个session的请求使其发送到同一个Tomcat worker上以达到Tomcat worker上的session一致性持续性
* 标识已经失败的Tomcat workers悬空发向它们的请求在被lb worker管理的其它workers上寻找可以失败恢复的worker
被同一个lb worker管理多个worker之间的负载平衡的(基于它们的lbfactor和当前用户session)也可以尽量避免由于单一的Tomcat进程死掉而造成这个网站被杀的不良反应
下表说明了lb worker接受的属性
* balanced_workers一个由分割的worker列表用来声明lb worker需要被管理的workers这些workers不应出现在workerlist属性中
* sticky_session表述是否将对SESSION ID的请求路由回到相同的Tomcat worker如果属性值不为它将被设置为JK_TRUEsession将是粘性的即SESSION ID的请求路由回到相同的Tomcat worker当Tomcat正使用能够跨越多个Tomcat实例持久化session数据的Session Manager时它将被设置为JK_FALSE属性默认值为JK_TRUE
例如worker balance管理着两个workersworkerworker
worker
balance
balanced_workers=worker
worker
高级lb Worker属性
JK x版本通过增加两个新的属性local_worker_only 和 local_worker 为lb worker增添了新的负载平衡和容错支持
下面让我们举一个实际的环境作为example
一个集群具有两个节点(worker+worker)一个web server与tomcat workers一前一后一个负载平衡器(lb Worker)位于节点的前面web server的后面
配置如下
workerlist=router
# Define a local_worker worker using ajp
workerworkerport=
workerworkerhost=
workerworkertype=ajp
workerworkerlbfactor=
workerworkerlocal_worker=
# Define another local_worker worker using ajp
workerworkerport=
workerworkerhost=
workerworkertype=ajp
workerworkerlbfactor=
workerworkerlocal_worker=
# Define the LB worker
workerroutertype=lb
workerrouterbalanced_workers=workerworker
workerrouterlocal_worker_only=
在worker和worker上的local_worker标志告诉lb_worker哪个连接属于本地worker
如果 local_worker值为非则它将被设置为JK_TRUE用来标记local worker而JK_FALSE的情况则相反如果至少一个worker被标记为local worker则lb_worker将工作于local worker模式这种模式下所有的local workers将被移到在lb_worker中的内部worker列表的头部
这意味着一个带有session id的请求到达lb_worker时相应的worker(根据权值排序权值最大的那个worker)将被确定作为此请求的接受/处理者如果这个 worker死掉/当机请求将被发送到处于非错误状态的第一个local worker如果一个没有session id的请求到达lb_worker时此请求将被路由到第一个local worker如果所有的local worker均处于错误状态则这时local_worker_only标志显得尤其重要如果local_worker_only的属性值为非则它被设置为 JK_TRUE否则被设置为 JK_FALSE当它被设置为 JK_TRUE时这个没有session id的请求将得到一个错误作为回应否则lb_worker将尝试将请求路由到其它的被管理的worker上如果其中的一个worker处于错误状态并且恢复会话的工作并没有任何改变local worker将查找这个没有session id的请求(因为在local worker中保存有这个请求的session)而其它的worker只能查找带有session id的请求
注意local_worker默认值是local_worker_only默认值也是
为什么需要这么复杂的过程吗?
因为我们对于一个关闭的节点需要一个具有灵性的维护
在节点前面的平衡器周期性的对每个节点的特定端口进行查询如果我们从集群中移走一个节点我们就会隐性的关闭掉这个特定的端口由于负载平衡器不能连接它这个节点将被标记为down但是我们没有移动在那个关闭的节点上的session到其它的节点上在这个环境下如果平衡器发送一个没有 session id的请求到一个端口被关掉的节点那么一个错误将发生如果平衡器测试到一个节点被标记为down的状态而没有其它的节点允许发送没有session id的请求这样这些陈旧的session请求就只有路由到那个被关闭的节点才能被接受在一段时间后这些陈旧的session将超时由于所有的陈旧的session过期那个不可达(被关闭)的节点将失去这个请求同时也会导致我们的servlet系统发送一个没有session id的重定向回应给浏览器
但是可能被关闭的节点将会up重新加入到集群中来在它上面仍将保留着陈旧的session所以在最后一个 session超时后更新节点能够为陈旧的session的恢复带来希望而不是杀掉sessions或者把它们移到其它节点上而且有时如果那些陈旧的session中有许多big的对象那么移动它们也将花费许多时间
jni类型的Worker属性
jni worker会在web server进程中打开一个JVM并在其中执行Tomcat这叫做进程内worker来往于JVM的消息将通过调用JNI方法被传递这使jni worker比那些需要使用ajp消息通讯的进程外worker执行的更快
注意由于JVM是多线程的jni worker应该只被用于在支持对线程的web server(AOLServer IIS Netscape and Apache )上同时还应该确认在web server上使用的线程方案是否与被使用的JK web server插件相匹配
由于jni worker 打开了一个JVM它将接受一些属性(例如classpath等)并将其传递给JVM
workerworker名class_path进程内的JVM要使用的classpath它将包括所有的Tomcat的jar文件和class配置文件等
为了获得JSP编译器的支持我们需要将Javac添加到classpath中当然对于Java需要添加toolsjar到classpath而对于JDKxx则要添加classeszip到classpath
workerworker名class_path用于以多行的形式声明多个classpathJK环境将用或者把这些classpath连接起来
例如给名为wrkjni的worker设置classpath
workerwrkjniclass_path=/var/tomcat/lib/tomcatjar
workerwrkjniclass_path=/opt/IBMJava/lib/toolsjar
workerworker名bridge用于标识将通过JNI方式被使用的Tomcat的类型此属性目前有两个属性值tomcat or tomcatTomcat x虽然已经过时但是被提供用于发布在一些类似iSeries系统上此属性的默认值为tomcat
例如给wrkjni设置bridge类型为tomcat
workerwrkjnibridge=tomcat
workerworker名cmd_line 此属性提供了在Tomcat启动代码执行的命令行使用时将命令行的命令参数分解为多个cmd_line属性JK环境通过在各个cmd_line属性值之间添加空格将这些cmd_line连接在一起
例如设置wrkjni的cmd_line属性
workerwrkjnicmd_line=config
workerwrkjnicmd_line=/etc/tomcat/conf/altserverxml
workerwrkjnicmd_line=home
workerwrkjnicmd_line=/var/tomcat
上面例子中的第一行声明了config参数名而第二行声明了与之对应的参数值第三行与第四行同理
workerworker名jvm_lib用于声明JVM的实现库的完整路径Jni worker使用这个路径动态装载JVM
例如设置wrkjni的JVM shared lib (IBM SDK on Linux)
workerwrkjnijvm_lib=/opt/IBMJava/jre/bin/classic/libjvmso
例如设置wrkjni的JVM shared lib (Sun SDK on Windows)
workerwrkjnijvm_lib=c:\JDK\\jre\bin\classic
workerworker名stdout设置JVM写它的Systemout的完整路径位置
例如将wrkjni的JVM系统输出路径设置为/var/log/
workerwrkjnistdout=/var/log/
workerworker名stderr设置JVM写它的Systemerr的完整路径位置
例如将wrkjni的JVM系统错误输出路径设置为/var/log/
workerwrkjnistderr=/var/log/
workerworker名ms设置JVM的初始堆大小
例如设置wrkjni的JVM的初始堆为M
workerwrkjnims=
workerworker名mx设置JVM的最大的堆大小
例如设置wrkjni的JVM堆最大为M
workerwrkjnimx=
workerworker名sysprops设置JVM的系统属性
例如设置wrkjni的JVM使用法语
workerwrkjnisysprops=Duserregion=FR
workerworker名ld_path设置附加的动态链接库路径(类似于LD_LIBRARY_PATH)
例如添加一些动态链接库路径到wrkjni的java环境中
workerwrkjnild_path=/opt/IBMJava/jre/bin/
workerwrkjnild_path=/opt/IBMJava/jre/bin/classic
注意在Linux下上面的ld_path并不能更新LD_LIBRARY_PATH所以需要在执行web server之前手动更新LD_LIBRARY_PATH
属性文件宏
我们可以在属性文件中定义宏这些宏让我们定义属性并在以后使用它们来构建其它的属性文件当我们修改Java HomeTomcat Home系统路径分隔符时这是很有用的
例如定义了属性workerstomcat_homeworkersjava_home
workerstomcat_home=d:\tomcat
workersjava_home=d:\sdk\jdk
在定义workerinprocessclass_path时就可以使用前面定义的workerstomcat_home
workerinprocessclass_path=$(workerstomcat_home)$(ps)classes
一个简单而完整的workerproperties
文件中定义了比较完整的结构可以做为参考模版
* 一个位于localhost的使用端口的ajp worker
* 一个位于localhost的使用端口的ajp worker
* 一个jni worker
* 一个lb worker负责ajp workerajp workers的负载平衡
文件内容如下
# Define some properties
workersapache_log=/var/log/httpd/
workerstomcat_home=/var/tomcat
workersjava_home=/opt/IBMJava/
ps=/
# Define workers real workers using ajp ajp jni the last one being a loadbalancing worker
workerlist=worker worker worker worker
# Set properties for worker (ajp)
workerworkertype=ajp
workerworkerhost=locahost
workerworkerport=
workerworkerlbfactor=
# Set properties for worker (ajp)
workerworkertype=ajp
workerworkerhost=locahost
workerworkerport=
workerworkerlbfactor=
workerworkercachesize=
workerworkercache_timeout=
workerworkersocket_keepalive=
workerworkersocket_timeout=
# Set properties for worker (jni)
workerworkertype=jni
# Set worker bridge type here Tomcat
workerworkerbridge=tomcat
# Set worker classpath
workerworkerclass_path=$(workerstomcat_home)$(ps)classes
workerworkerclass_path=$(workerstomcat_home)$(ps)lib$(ps)tomcatjar
# Set worker tomcat command line
workerworkercmd_line=home
workerworkercmd_line=$(workerstomcat_home)
# Set worker Tomcat/JVM settings
workerworkerjvm_lib=$(workersjava_home)$(ps)jre$(ps)bin$(ps)classic$(ps)libjvmso
workerworkerstdout=$(workersapache_log)$(ps)inprocessstdout
workerworkerstderr=$(workersapache_log)$(ps)inprocessstderr
workerworkersysprops=tomcathome=$(workerstomcat_home)
# Set properties for worker (lb) which use worker and worker
workerworkerbalanced_workers=workerworker