集群架构

组件层级:

Server
|
Service
|
Engine
|  \
|  --- Cluster --*
|
Host
|
------
/      \
Cluster    Context(1-N)
|             \
|             -- Manager
|                   \
|                   -- DeltaManager
|                   -- BackupManager
|
---------------------------
|                       \
Channel                    \
----------------------------- \
|                          \
Interceptor_1 ..               \
|                            \
Interceptor_N                    \
-----------------------------      \
|          |         |             \
Receiver    Sender   Membership       \
-- Valve
|      \
|       -- ReplicationValve
|       -- JvmRouteBinderValve
|
-- LifecycleListener
|
-- ClusterListener
|      \
|       -- ClusterSessionListener
|
-- Deployer
\
-- FarmWarDeployer

工作原理

为了便于理解集群的工作机制,下面将通过一些实际情境来加深一下你的理解,我们只打算采用 2 个 Tomcat 实例:Tomcat A 和 Tomcat B。具体发生的事件流程为:

  1. Tomcat A 启动。
  2. Tomcat A 启动完毕后,Tomcat B 才启动。
  3. Tomcat A 接收一个请求,创建了一个会话 S1
  4. Tomcat A 崩溃。
  5. Tomcat B 接收到对会话 S1 的请求。
  6. Tomcat A 启动。
  7. Tomcat A 接收到一个请求,调用会话 S1 上的 invalidate 方法。
  8. Tomcat B 接收到一个对新会话 S2 的请求。
  9. Tomcat A 会话 S2 由于不活跃而超时。

介绍完了事件序列,下面详细剖析一下在会话复制代码中到底发生了什么。

1.Tomcat A 启动

Tomcat 使用标准启动顺序来启动。Host 对象创建好之后,会关联一个 Cluster 对象。在解析上下文时,如果 web.xml 中包含 distributable 元素,Tomcat 就会让 Cluster 类(在该例中是 SimpleTcpCluster)创建复制的上下文的管理器。启用了集群并在 web.xml 中设置了 distributable 元素后,Tomcat 会为该上下文创建一个 DeltaManager(而不是 StandardManager)。Cluster 类会启动一个成员服务(组播)和一个复制服务(TCP 单播)。下文将会介绍更多的架构细节。

2.Tomcat B 启动

Tomcat B 启动时,采取的顺序与 Tomcat A 基本一样。集群启动,建立成员(Tomcat A 与 Tomcat B)。Tomcat B 会请求集群中已有服务器(本例中是 Tomcat A)的会话状态。如果 Tomcat A 响应该请求,那么在 Tomcat B 开始侦听 HTTP 请求之前,Tomcat A 会将会话状态传到 Tomcat B那里;如果 Tomcat A 没有响应该请求,Tomcat 会等待 60 秒,超过这个时间之后,发出一个日志项。该会话状态会发送到每一个在 web.xml 中设置了 distributable 元素的应用。注意:为了有效地使用会话复制,所有的 Tomcat 实例都必须拥有相同的配置。

3.Tomcat A 接收一个请求,创建了一个会话 S1

Tomcat A 对发送给它的请求的处理方式,与没有会话复制时的处理方式完全相同。请求完成时会触发相应行为,ReplicationValve 会在响应返回用户之前拦截请求。如发现会话已经更改,则使用 TCP 将会话复制到 Tomcat B 上。一旦序列化的数据被转交给操作系统的 TCP 逻辑,请求就会重新通过 valve 管道返回给用户。对于每一个请求,都将复制所有的会话,这样做就有利于复制那些在会话中修改属性的代码,使其即使不必调用 setAttribute 或removeAttribute,也能被复制。另外,使用 useDirtyFlag 配置参数也可以优化会话的复制次数。

4.Tomcat A 崩溃

当 Tomcat A 崩溃时,Tomcat B 会接到通知,得知 Tomcat A 已被移出集群,随即 Tomcat B 就在其成员列表中也将 Tomcat A 移除,Tomcat B 从而不再收到关于 Tomcat A 的任何通知。负载均衡器会把从 Tomcat A 发送给 Tomcat B 的请求重新定向,所有的会话都将保持现有的状态。

5.Tomcat B 接收到对会话 S1 的请求

毫无悬念,Tomcat B 会照处理其他请求的方式那样来处理该请求。

6.Tomcat A 启动

在 Tomcat A 开始接收新的请求之前,将会根据上面(1)(2)两条所所说明的启动序列来启动。Tomcat A 会加入集群,联系 Tomcat B 并获取所有的会话状态。一旦接收到会话状态,就会完成加载,并打开 HTTP/mod_jk 端口。所以,除非 Tomcat A 从 Tomcat B 那里接收到了会话变更,否则没有发给 Tomcat A 的请求。

7.Tomcat A 接收到一个请求,调用会话 S1 上的 invalidate 方法

会拦截对 invalidate 的调用, 并且 session 会被加入失效会话队列。 在请求完成时,不会发送会话改变消息,而是发送一个 “到期” 消息给 Tomcat B,Tomcat B 也会让此会话失效。

8.Tomcat B 接收到一个对新会话 S2 的请求

同步骤 3。

9.Tomcat A 会话 S2 由于不活跃而超时

invalidate 调用会被拦截,当一个会话被用户标记失效时,该会话就会加入到无效会话队列。此时,失效的会话不会被复制,直到另一个请求通过系统并检查无效会话队列。

Membership 集群成员是通过非常简单的组播 ping 命令来实现的。每个 Tomcat 实例都会定期发送一个组播 ping,ping 消息中包含 Tomcat 实例自身的 IP 和配置的 TCP 监听端口。如果实例在一个给定的时间内没有收到这样的 ping 信息,就会认为那个成员已经崩溃了。非常简洁高效!当然,您需要在系统上启用广播。

TCP 复制 一旦收到一个多播 ping 包,在下一个复制请求时成员被添加到集群,发送实例将使用的主机和端口信息,以及建立TCP套接字。使用该套接字发送序列化的数据。之选择TCP套接字,是因为它内建有流量控制和保证发送的功能。所以发送的数据肯定会到达那里。

分布式的锁定与使用架构的页面s Tomcat 在跨集群同步不保持会话实例。这种逻辑的实现将是多开销和导致各种各样的问题。如果你的客户用同一个会话同时发送多个请求,那么最后的请求将会覆盖集群中的其他会话。

利用 JMX 监控集群

使用集群时,如何监控是一个重要课题。有些集群对象是 JMX MBean。

添加下列属性到启动脚本上。

set CATALINA_OPTS=\
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=%my.jmx.port% \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=false

下面是 Cluster 的 MBean 列表:

名称 描述 MBean 对象名-引擎 MBean 对象名-主机
Cluster 完整的 cluster 元素 type=Cluster type=Cluster,host=${HOST}
DeltaManager 该管理器控制会话,并处理会话复制 type=Manager,context=${APP.CONTEXT.PATH}, host=${HOST} type=Manager,context=${APP.CONTEXT.PATH}, host=${HOST}
FarmWarDeployer 将一个应用部署到该集群的所有节点上。 目前不支持 type=Cluster, host=${HOST}, component=deployer
Member 代表集群中的一个节点 type=Cluster, component=member, name=${NODE_NAME} type=Cluster, host=${HOST}, component=memdber, name=${NODE_NAME}
ReplicationValve 该 valve 控制到备份节点的会话复制 type=Valve,name=ReplicationValve type=Valve,name=ReplicationValve,host=${HOST}
JvmRouteBinderValve 将 Session ID 变为 tomcat 当前的 jvmroute 的集群回滚值 type=Valve,name=JvmRouteBinderValve, context=${APP.CONTEXT.PATH} type=Valve,name=JvmRouteBinderValve,host=${HOST}, context=${APP.CONTEXT.PATH}
联系我们

邮箱 626512443@qq.com
电话 18611320371(微信)
QQ群 235681453

Copyright © 2015-2022

备案号:京ICP备15003423号-3