Apache 整合 Tomcat 、集群
1.1 使用 mod_proxy 整合
1.2 使用 mod_jk 整合
1.3 集群
1.3.1 Tomcat 集群配置
1.3.2 mod_proxy 进行负载均衡
1.3.3 mod_jk 进行负载均衡
Apache 整合 Tomcat 主要有两种方式,通过 mod_proxy 整合和通过 mod_jk 整合。
使用 mod_proxy 整合 Tomcat 需要我们开启 Apache 的代理功能,代理功能的开启可以通过在 Apache 的 conf/httpd.conf 文件中将如下内容前的“ # ”号去除,这样 Apache 就能在运行的时候加载 mod_proxy 模块了,从而开启了 Apache 的代理功能。
#LoadModule proxy_module modules/mod_proxy.so
开启了 mod_proxy 之后我们还需要选择一个协议来作为 Apache 代理 Tomcat 的协议,可以是 AJP 协议、 Http 协议等。如果需要使用 Http 协议,那么请将 Apache 的 conf/httpd.conf 文件中如下内容前的“ # ”号去除(需要使用其它的协议时使用类似的方式)。 Tomcat 的官方文档说在进行 Tomcat 集群时使用 AJP 协议比使用 Http 协议的性能要更好 ( It should be noted that the performance of HTTP proxying is usually lower than the performance of AJP, so AJP clustering is often preferable. )。
#LoadModule proxy_http_module modules/mod_proxy_http.so
Apache 的代理有两种方式, Reverse 方式和 Forward 方式。 Forward 方式需要在客户端进行配置以利用代理服务器获取目标内容, Forward 方式的一种常见场景就是我们的内网机器都无法连接外网,但是其中有一台可以连接,然后我们在可以连接外网的机器上搭建一个代理,让其它内网机器都通过该代理来访问外部网络。 Reverse 方式就无需在客户端进行配置了,客户端请求的目标地址是直接对应 Reverse 代理的,然后由 Reverse 代理在内部决定请求哪个真实的地址。本文将主要讲解 Reverse 代理方式。
首先去掉 httpd.conf 文件中如下内容前的“ # ”号,以将 Virtual Host 的配置包含在 Apache 服务器的配置文件中,然后我们就可以在 httpd-vhosts.conf 文件中进行 Virtual Host 的配置了。
#Include conf/extra/httpd-vhosts.conf
然后,我们在 conf/extra/httpd-vhosts.conf 文件中添加如下内容,其表示我们定义了一个虚拟主机,该虚拟主机将接收任何请求。
<VirtualHost *:80>
ProxyPass "/" "http://localhost:8080/" max=300
ProxyPassReverse "/" "http://localhost:8080/"
</VirtualHost>
上述的指令 ProxyPass 是用来映射代理的路径的,其语法是:
ProxyPass path !|url [key=value[key=value…]]
其中 path 表示 Apache 请求的相对路径,而“ ! ”则表示不对该路径进行代理, url 则表示需要代理的路径,后面的 key=value 表示需要指定的参数。在我们的示例中就是使用根路径“ / ”代理本地 8080 端口的根路径“ / ”,然后参数 max 表示同时最多允许 300 个对后台代理服务的并发请求。关于 ProxyPass 的更多信息请参考 http://httpd.apache.org/docs/2.4/zh-cn/mod/mod_proxy.html#proxypass 。
指令 ProxyPassReverse 是用来对后台代理应用返回过来的 Response Header 中的 URL 进行转换的,使其能够以 Apache 的形式正确的展示。比如请求后台服务后需要重定向到 http://localhost:8080/examples 则通过 ProxyPassReverse 指令进行转换后将会把重定向地址改为 http://localhost/examples 。
这个时候我们在 8080 端口启动 Tomcat ,然后在 80 端口启动 Apache ,之后我们所有对 80 端口的请求都将由 Apache 代理请求到 8080 端口的 Tomcat 应用。
如下这样的配置就表示不对“ /examples ”路径进行代理,此时访问“ /examples ”时将去 Apache 自己的路径下寻找对应的资源。
<VirtualHost *:80>
ProxyPass "/examples" "!"
ProxyPass "/" "http://localhost:8080/" max=300
ProxyPassReverse "/" "http://localhost:8080/"
</VirtualHost>
此时如果你在浏览器里面访问 http://localhost/examples 时会得到一个 403 页面,原因是 Apache 默认会禁止对根目录以外的路径的访问。此时,我们需要找到 httpd.conf 文件中的如下内容:
<Directory />
AllowOverride none
Require all denied
</Directory>
并将其修改为如下内容:
<Directory />
AllowOverride none
Order Deny,ALlow
Allow from All
</Directory>
更多内容请参考 http://httpd.apache.org/docs/2.4/mod/mod_proxy.html 。
首先我们需要下载 mod_jk.so ,其由 Tomcat 提供,可以从网址 http://tomcat.apache.org/connectors-doc/ 寻找对应 Apache 版本的 mod_jk.so 进行下载。下载后将 mod_jk.so 放到 Apache 服务器的 modules 目录下。
然后在 httpd.conf 文件的末尾加上如下内容以加载 mod_jk 。
# 加载 mod_jk 模块
LoadModule jk_module modules/mod_jk.so
# 指定 workers.properties 文件的路径
JkWorkersFile conf/workers.properties
# 指定 Jk 的日志输出路径
JkLogFile logs/mod_jk.log
# 指定 Jk 日志文件的输出级别, [debug/info/error]
JkLogLevel info
# 指定日志输出时间的格式
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
之后可以在 extra/httpd-vhosts.conf 文件中加上如下内容(注意需要在 httpd.conf 文件中 include extra/httpd-vhosts.conf 文件)。
<VirtualHost *:80>
# 指定交给 worker1 进行处理的路径
JkMount /* worker1
# 指定不交给 worker1 进行处理的路径
JkUnMount /examples/* worker1</VirtualHost>
上述指令所代表的含义已经在注释中给出,关于针对 Jk Module 的更多配置信息官方文档 http://tomcat.apache.org/connectors-doc/reference/apache.html 的配置说明。
接下来需要在我们指定的 workers.properties 文件中定义对应的 worker 。这里我们指定如下。
# 指定需要定义的 worker 列表,多个 worker 之间使用逗号分隔
worker.list=worker1
# 指定 worker1 的 type
worker.worker1.type=ajp13
# 指定 worker1 连接的 Tomcat 的 IP
worker.worker1.host=localhost
# 指定 worker1 连接 tomcat 的端口,因为我们 worker1 使用的 type 为 ajp13 ,这里需要是 ajp13 协议的端口
worker.worker1.port=8009
# 指定 worker1 对应的连接池的大小
worker.worker1.connection_pool_size=300
使用 ajp13 协议时,对应的连接端口对应于我们的 Tomcat 的 server.xml 文件中配置的 ajp13 协议的端口,如:
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
更多关于 workers.properties 文件可以配置的信息请参考 Tomcat 的官方文档。 http://tomcat.apache.org/connectors-doc/reference/workers.html 。
通过如上配置后除了 /examples 下的内容以外,我们就完全把 Tomcat 交给了 Apache 代理。
Apache 整合 Tomcat 进行集群是将多个 Tomcat 进行集群,集群的各个节点之间可以进行 Session 的同步等,然后通过上述介绍的两种方式使用 Apache 代理以实现对各个节点之间的负载均衡。其实除了 Apache 服务器对 Tomcat 集群进行负载均衡外,常用的进行负载均衡代理的还有 Nginx ,本文的主题是 Apache 整合 Tomcat ,所以不会对 Nginx 代理 Tomcat 集群进行讲解,有兴趣的朋友可以自己查阅相关的文档进行了解。
Tomcat 的集群配置最简单的方式是将 Tomcat 的 conf 目录下的 server.xml 文件中的 <cluster> 标签的注释打开。
<!--
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
-->
注意如果是在同一台电脑上同时启动多个 Tomcat 进行集群时需要保证各个 Tomcat 使用的 Server 端口、 Http 端口和 Ajp 端口都是各不相同的。
只配置一个 <Cluster className=”org.apache.catalina.ha.tcp.SimpleTcpCluster”/> 时 Tomcat 默认会为我们配置如下内容:
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
channelSendOptions="8">
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4"
port="45564"
frequency="500"
dropTime="3000"/>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4000"
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=""/>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/"
watchDir="/tmp/war-listen/"
watchEnabled="false"/>
<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener">
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener">
</Cluster>
关于 Tomcat 集群的详细配置信息请参考 Tomcat 的官方文档 http://tomcat.apache.org/tomcat-7.0-doc/cluster-howto.html 。
使用 Tomcat 进行集群时,如果我们 Session 能够在多个节点之间进行自动复制,我们需要保证存在在 Session 中的 attribute 都是可序列化的,即都是实现了 java.io.Serializable 接口的,此外我们需要部署在每个 Tomcat 节点上的应用的 web.xml 文件中定义了 <distribuable/> 。
为使用 mod_proxy 能够支持负载均衡,我们需要加载 mod_proxy_balancer 模块,该模块提供的功能就是负载均衡,但是使用的负载均衡算法并不由它提供,而是由对应的算法模块提供。 Apache 自带的算法模块有如下四种。
LoadModule lbmethod_bybusyness_module modules/mod_lbmethod_bybusyness.so
LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so
LoadModule lbmethod_heartbeat_module modules/mod_lbmethod_heartbeat.so
mod_proxy_balancer 默认使用的负载均衡算法是 byrequests ,所以默认情况下我们还需要加载 mod_lbmethod_byrequests 模块。此外,还需要加载提供共享内存的模块 mod_slotmem_shm.so 。所以,总的来说我们需要加载如下三个模块。
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
接下来,我们可以在 VirtualHost 下配置如下内容:
<VirtualHost *:80>
<Proxy "balancer://elim">
# 定义负载均衡的成员, loadfactor 用于指定负载权重的
BalancerMember "http://localhost:8080" loadfactor=1
BalancerMember "http://localhost:9090" loadfactor=2
#ProxySet 指令用于设定 balancer 的参数
ProxySet lbmethod=byrequests
</Proxy>
# 代理什么路径就在 balancer://elim 后加什么路径
ProxyPass "/" "balancer://elim/"
ProxyPassReverse "/" "balancer://elim/"
</VirtualHost>
在上述配置中我们通过 <Proxy> 标签来定义了一个负载均衡代理,名叫“ balancer://elim ”,在使用 <Proxy> 标签进行代理的定义时,如果我们的前缀是以“ balancer:// ”开始的,则定义的是一个负载均衡代理。在其中可以通过 BalancerMember 来指定对应的成员, loadfactor 用来指定对应的负载权重。通过 ProxySet 指令我们指定了负载均衡代理 balancer://elim 的负载均衡算法为 byrequests ,这也是负载均衡代理的默认算法。然后通过 ProxyPass 指定对应路径的代理时,我们就可以指定为我们定义的负载均衡代理 balancer://elim 。需要注意的是如果我们需要代理的是根路径,需要在 balancer://elim 后也加上根路径“ / ”。通过如上配置以后我们的负载均衡代理就建立起来了。
关于 mod_proxy_balancer 负载均衡的更多介绍请参考文档 http://httpd.apache.org/docs/2.4/mod/mod_proxy_balancer.html 。更多负载均衡配置信息请参考 http://httpd.apache.org/docs/2.4/mod/mod_proxy.html 。
Apache 为我们提供了一个 mod_status 模块,通过该模块我们可以监测我们的负载均衡代理的运行情况,或在运行时修改我们的负载均衡的成员的配置信息。首先我们需要加载 mod_status 模块。
LoadModule status_module modules/mod_status.so
然后我们需要在 httpd.conf 文件中通过 Location 指定我们监测负载均衡的路径和对应的处理器。如下配置则表示我们将监测路径定义为 /balancer-status ,对应的处理器为 balancer-manager ,然后允许所有的用户请求该地址。
<Location /balancer-status>
SetHandler balancer-manager
Order deny,allow
Allow from all
</Location>
如果我们需要监测的是我们的 Apache 服务器的运行情况,则可以将上述的 SetHandler 设置为 server-status 。需要注意的是当和代理一起使用的时候,需要避免指定的访问路径和代理的代理路径相冲突,此时需要我们将监测 Apache 服务器运行情况的访问路径从代理中移除。
当使用 mod_jk 进行负载均衡的时候,我们需要修改我们的 workers.properties 文件的定义。此时,我们将 workers.properties 的内容修改为如下形式。
worker.list=cluster
# 指定名为 cluster 的 worker 的类型是 lb ,即负载均衡
worker.cluster.type=lb
# 指定负载均衡 worker 的参与者为 worker1 和 worker2
worker.cluster.balance_workers=worker1,worker2
# 当为 true 时同一个 Session 发送的请求只会代理到相同的 Tomcat 节点上
worker.cluster.sticky_session=false
# 表示是否强制绑定 session ,该参数只针对 sticky_session 为 true 时才有效。当 sticky_session 和 sticky_session_force 都为 true
# 时,如果对应 Session 的 Tomcat 不可用了,对应的请求将会返回对应的错误页面给客户端,否则将由其它节点来进行接管处理。
worker.cluster.sticky_session_force=false
# 指定 worker1 连接 Tomcat 的协议类型
worker.worker1.type=ajp13
# 指定 worker1 的对应的 Tomcat 的 IP
worker.worker1.host=localhost
# 指定 worker1 连接 Tomcat 时使用的端口号
worker.worker1.port=8009
# 指定 worker1 在集群中负载的权重为 1 ,数字越大负载越高
worker.worker1.lbfactor=1
# 指定 worker2 连接 Tomcat 的协议类型
worker.worker2.type=ajp13
# 指定 worker2 的对应的 Tomcat 的 IP
worker.worker2.host=localhost
# 指定 worker2 连接 Tomcat 时使用的端口号
worker.worker2.port=18009
# 指定 worker2 在集群中负载的权重为 2 ,由于 worker1 为 1 ,所以 worker2 将占总负载的 2/3 。
worker.worker2.lbfactor=2
该配置表示我们的 Tomcat 集群中拥有两台 Tomcat 服务器,然后这两台 Tomcat 都是部署在同一台服务器上的。接下来,我们需要告诉 Apache 哪些请求将交给我们的负载均衡 worker —— cluster 处理。
<VirtualHost *:80>
# 指定将所有的请求都交给我们的负载均衡 worker —— cluster 进行处理。
JkMount /* cluster
</VirtualHost>
使用 mod_jk 进行负载均衡的时候需要注意的是我们需要在我们的 Tomcat 的 server.xml 文件中为我们的 Engine 指定 jvmRoute ,且对应的值还必须和我们在 workers.properties 文件中指定的负载均衡 worker 的名称一致。
<Engine name="Catalina" defaultHost="localhost" jvmRoute="worker1">
经过上述配置以后我们的 mod_jk 对 Tomcat 集群的负载均衡就搭建好了。其默认的负载均衡策略是通过 Request 来进行负载均衡的,我们可以通过在 worker.properties 文件中对类型为 lb 的 worker 指定其 method 属性来显示的指定负载均衡策略。除了 Request 外,还有 Session 、 Next 、 Traffic 、 Business ,关于这几种负载均衡策略的详细介绍请参考文档 http://tomcat.apache.org/connectors-doc/reference/workers.html 。
mod_jk 中有一类特殊的 worker 类型, status 类型,它可以用来监测 worker 的运行情况和在运行时动态更改 worker 的配置信息等。定义 status 类型的 worker 非常简单,只需要指定一个 worker 的 type 为 status ,同时在 worker.list 中进行声明。
# 指定名为 cluster 的 worker 的类型是 lb ,即负载均衡
worker.cluster.type=lb
# 指定负载均衡 worker 的参与者为 worker1 和 worker2
worker.cluster.balance_workers=worker1,worker2
# 当为 true 时同一个 Session 发送的请求只会代理到相同的 Tomcat 节点上
worker.cluster.sticky_session=false
# 表示是否强制绑定 session ,该参数只针对 sticky_session 为 true 时才有效。当 sticky_session 和 sticky_session_force 都为 true
# 时,如果对应 Session 的 Tomcat 不可用了,对应的请求将会返回对应的错误页面给客户端,否则将由其它节点来进行接管处理。
worker.cluster.sticky_session_force=false
…
…
# 指定 worker 的类型为 status
worker.status.type=status
之后我们需要为类型为 status 的 worker 指定一个对应的访问路径。
<VirtualHost *:80>
# 指定将请求路径 /status 交给名叫 status 的 worker 处理。
JkMount /status status
# 指定将所有的请求都交给我们的负载均衡 worker —— cluster 进行处理。
JkMount /* cluster
</VirtualHost>
这样我们在请求路径为 /status 的时候就可以监测 worker 的运行状态等信息了。更多关于类型为 status 的 worker 的介绍请参考官方文档 http://tomcat.apache.org/connectors-doc/reference/status.html 。
http://httpd.apache.org/docs/2.4/mod/mod_proxy.html
http://tomcat.apache.org/connectors-doc/reference/apache.html
http://tomcat.apache.org/connectors-doc/reference/workers.html
http://tomcat.apache.org/tomcat-7.0-doc/cluster-howto.html
http://tomcat.apache.org/connectors-doc/generic_howto/loadbalancers.html
http://tomcat.apache.org/connectors-doc/reference/status.html
http://httpd.apache.org/docs/2.4/mod/mod_proxy_balancer.html
http://httpd.apache.org/docs/2.4/mod/mod_status.html
(注:本文是基于 Apache Tomcat7.0.50 版本和 Apache Httpd2.4 版本所写)