30.7. 故障切换模式

JBoss EAP 消息传递定义两种类型的客户端故障转移:

  • 自动客户端故障切换
  • 应用程序级别的客户端故障切换

JBoss EAP 消息传递还提供 100% 透明自动重新连接同一服务器(例如,当出现瞬态网络问题时)。这与故障转移类似,除了重新连接到同一服务器并在客户端重新连接和会话附加中讨论。

在故障转移过程中,如果客户端在任何非持久或临时队列上拥有消费者,则在备份节点上故障转移期间将自动重新创建这些队列,因为备份节点将不知道非持久队列。

30.7.1. 自动客户端故障

JBoss EAP 消息传递客户端可以配置为接收所有实时和备份服务器的知识,这样,在客户端(实时服务器连接)连接发生连接故障时,客户端将检测故障并重新连接到备份服务器。然后备份服务器会在故障转移前自动重新创建每个连接上存在的任何会话和消费者,从而让用户不必手动编码手动重新连接逻辑。

JBoss EAP 消息传递客户端在 client-failure-check-period 期限内未从服务器接收到数据包时,会检测到连接失败,如 检测检测连接中所述

如果客户端没有在分配的时间内接收数据,它将假定连接失败并尝试故障转移。如果套接字由操作系统关闭,则服务器进程可能会被终止,而不是服务器硬件本身崩溃,客户端将直接故障转移。

JBoss EAP 消息传递客户端可以通过多种不同方式进行配置,以发现实时备份服务器对的列表。例如,它们可以被配置为显式端点,但最常见的方法是客户端在第一次连接到集群时接收关于集群拓扑的信息。如需更多信息,请参阅服务器发现

默认 HA 配置包括使用 推荐的 http-connector 进行集群通信的集群连接。这与使用默认的 RemoteConnectionFactory 进行服务器连接时远程客户端使用的 http-connector 相同。虽然不建议这样做,但您可以使用不同的连接器。如果使用自己的连接器,请确保它作为配置的一部分被远程客户端和集群节点使用的 cluster -factory 使用 如需有关连接器和集群连接的更多信息,请参阅配置消息传递传输和集群连接

警告

用于 JMS 客户端 的 connection-factory 中定义的 连接器 必须与群集使用的 cluster-connection 中定义的连接器相同。否则,客户端将无法更新其底层实时/备份对拓扑,因此不知道备份服务器的位置。

使用 CLI 命令检查 connection-factory 和 cluster- connection 的配置。例如,若要读取名为 RemoteConnectionFactoryconnection-factory 的当前配置,可使用以下命令:

/subsystem=messaging-activemq/server=default/connection-factory=RemoteConnectionFactory:read-resource

类似地,以下命令读取名为 my-cluster 的 cluster-connection 的配置。

/subsystem=messaging-activemq/server=default/cluster-connection=my-cluster:read-resource

要启用自动客户端故障转移,客户端必须配置为允许非零重新连接尝试。如需更多信息,请参阅客户端重新连接和会话附加。默认情况下,只有在与实时服务器至少建立了一条连接后才会进行故障转移。换句话说,当客户端无法与实时服务器进行初始连接时,不会发生故障转移。如果初始尝试失败,客户端会只根据 reconnect -attempts 属性尝试连接到 live 服务器,并在配置完尝试次数后失败。

/subsystem=messaging-activemq/server=default/connection-factory=RemoteConnectionFactory:write-attribute(name=reconnect-attempts,value=<NEW_VALUE>)

此规则的一个例外是,只有一对实时 - 备份服务器,并且没有其他实时服务器,并且远程 MDB 在完全关闭时连接到实时服务器。如果 MDB 配置了 @ActivationConfigProperty(propertyName = "rebalanceConnections", propertyValue = "true",它将 尝试重新平衡它与另一个实时服务器的连接,并且不会故障转移到备份。

在初始连接中覆盖失败

由于客户端在进行第一次连接之前不会了解完整的拓扑,因此有一个时间窗口,它不知道备份。如果此时发生故障,客户端只能尝试重新连接到原始的实时服务器。若要配置客户端尝试次数,您可以设置 ClientSessionFactoryImplActiveMQConnectionFactory 上属性 initialConnectAttempts

或者,在服务器配置中,您可以设置客户端使用的连接工厂的 initial-connect-attempts 属性。默认值为 0,即,仅尝试一次。尝试次数后,将引发异常。

/subsystem=messaging-activemq/server=default/connection-factory=RemoteConnectionFactory:write-attribute(name=initial-connect-attempts,value=<NEW_VALUE>)
关于服务器复制

JBoss EAP 消息传递不会在实时服务器和备份服务器之间复制完整的服务器状态。当在备份上自动重新创建新会话时,将不会知晓该会话中已发送或确认的消息。故障切换时的任何内部发送或确认也可能丢失。

通过复制完整的服务器状态,JBoss EAP 消息传递理论上可提供 100% 透明无缝故障转移,从而避免任何丢失的消息或确认。但是,这样做需要高昂的成本:复制完整的服务器状态,包括队列和会话。这需要复制整个服务器状态机。也就是说,实时服务器上的每个操作都必须以完全相同的全局顺序复制到副本服务器上,以确保副本状态一致。这极其难以以高性能和可扩展的方式执行,特别是考虑到多个线程同时更改了实时服务器状态。

可以使用虚拟同步等技术提供完整的状态机复制,但这不能良好扩展并有效地将所有操作序列化到单个线程,从而显著降低并发性。存在用于多线程主动复制的其他技术,如复制锁定状态或复制线程调度,但这在 Java 级别上很难实现。

因此,出于 100% 透明故障转移的目的,无需降低性能和并发性。即使没有 100% 透明故障转移,也很容易通过将重复检测和重试交易结合使用,保证一次并仅一次交付一次(即便在故障的情况下也是如此)。但是,这对客户端代码不是 100% 透明的。

30.7.1.1. 在故障转移期间处理阻塞调用

如果客户端代码正在阻止服务器调用,例如,它正在等待响应以便在故障转移期间继续执行,新的会话将不知道正在进行的调用。否则被阻止的调用可能会永远挂起,等待永远不会出现响应。

为防止这种情况,JBoss EAP 消息传递将通过使生成 javax.jms.JMSException(如果使用 JMS)或 带有错误代码 ActiveMQException 的 ActiveMQ Exception (如果使用核心 API)抛出 javax.jms.JMS Exception,从而取消阻止任何在故障转移时 正在进行的阻止调用。客户端代码可以捕捉到这个异常,并在需要时重试任何操作。

如果未阻塞的方法是对 commit()或 prepare()的调用,则事务将被自动回滚,如果使用核心 API,则 JBoss EAP 消息传递将引发 javax.jms.TransactionRolledBackException,如果使用 JMS,则 带有 错误代码 ActiveMQException.TRANSACTION_ROLLED_BACK 的 ActiveMQException。

30.7.1.2. 使用事务处理失败

如果会话为事务性并且消息已在当前事务中发送或确认,则服务器无法确定故障转移期间是否丢失了消息或确认。

因此,事务将被标记为仅回滚,提交后的任何尝试都将引发 javax.jms.TransactionRolledBackException,如果使用 JMS.,或者带有错误代码 ActiveMQExceptionActiveMQException.TRANSACTION_ROLLED_BACK (如果使用核心 API)。

警告

此规则的注意事项是在通过 JMS 或核心 API 使用 XA 时。如果使用了两个阶段提交,并且已调用 prepare(),则回滚可能会导致 HeuristicMixedException。因此,提交将抛出 XAException.XA_RETRY 异常。这告知交易管理器,它在稍后某个时间点重试提交,其副作用是将丢失任何非持久消息。为避免发生这种情况,在使用 XA 时务必使用持久的消息。通过确认这一点并不是个问题,因为它们在调用 prepare () 之前被刷新到服务器。

用户可以捕捉异常,并根据需要执行任何客户端的本地回滚代码。不需要手动回滚会话,因为它已经回滚。然后,用户只需在同一会话中再次重试事务操作。

如果在执行提交调用时发生故障转移,服务器(如前文所述)将取消阻断调用以防止挂起,因为没有响应将返回。在这种情况下,客户端很难在出现故障前确定事务提交是否真正在实时服务器上处理。

注意

如果 XA 通过 JMS 或核心 API 使用,则抛出 XAException.XA_RETRY。这是为了通知交易经理,应在某一点进行重试。稍后的某个时候,交易管理器将重试提交。如果原始提交尚未发生,它将仍然存在并提交。如果不存在,则假定它已提交,尽管事务管理器可能会记录警告。

为补救这一点,客户端可以在事务中启用重复检测,并在调用未阻塞后重试事务。如需有关如何在服务器上配置检测的信息,请参阅重复消息检测。如果事务在故障转移之前确实在实时服务器上进行了成功提交,则重复检测将确保服务器上重新定向的任何持久消息都会被忽略,以防止在事务重试时它们多次发送。

30.7.1.3. 通知连接失败

JMS 提供了一种标准机制,用于发送连接失败的异步通知: java.jms.ExceptionListener。有关本课程的更多信息,请参阅 JMS javadoc。核心 API 也以类 org.apache.activemq.artemis.core.client.SessionFailureListener 的形式提供类似的功能。

如果连接失败,则 JBoss EAP 始终会调用任何 Exception Listener 或 SessionFailureListener 实例,无论连接是成功失败、重新连接还是重新连接。但是,您可以通过检查 传入SessionfailureListener 上的 connectionFailed() 的值或 javax.jms.JMSException 的 javax.jms.JMSException 的值来找出重新连接或重新附加是否发生了:

JMSException 错误代码

错误代码描述

故障切换

故障转移已经发生,我们已成功重新连接或重新连接。

断开连接

没有发生故障转移,我们断开连接。