Spring DefaultMessageListenerContainer无法故障切换至备用URL问题
Spring JMS对接Tibco EMS实现故障自动切换方案
问题场景
我们有一个Spring应用,使用spring-jms-5.2.24.RELEASE.jar提供的JMS工具对接Tibco EMS主题消费者,配置了双地址URL用于故障切换,配置如下:
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate"> <property name="environment"> <props> <prop key="java.naming.provider.url">tcp://usitdjms-vd3:7222,tcp://usitdjms-vd4:7222</prop> <prop key="java.naming.factory.initial">com.tibco.tibjms.naming.TibjmsInitialContextFactory</prop> <prop key="java.naming.security.principal">${USERNAME}</prop> <prop key="java.naming.security.credentials">${PASSWORD}</prop> </props> </property> </bean>
但当vd3服务器宕机时,代码并未自动切换至vd4,而是持续打印错误,仅重启应用才能恢复,错误日志如下:
08/15/2023-14:20:19:453 [jmsContainer-1] - ERROR - DefaultMessageListenerContainer.java:962 - Could not refresh JMS Connection for destination 'Topic[in_order_perf]' - retrying using FixedBackOff{interval=5000, currentAttempts=54, maxAttempts=unlimited}. Cause: Failed to connect to the server at tcp://usitdjms-vd4.ny.cantor.com:7222; nested exception is java.net.ConnectException: Connection refused (Connection refused)
解决方案
1. 补充Tibco EMS故障转移属性
Tibco EMS的多地址故障切换需要通过专属配置属性开启重试与重连机制,在JndiTemplate的environment中添加以下属性:
com.tibco.tibjms.connect.attemptCount:设为-1表示无限次尝试连接com.tibco.tibjms.connect.retryInterval:重试间隔(单位毫秒,示例设为3000)com.tibco.tibjms.reconnect.attemptCount:重连尝试次数(-1表示无限)com.tibco.tibjms.reconnect.retryInterval:重连间隔(示例设为5000)com.tibco.tibjms.connect.attemptDelay:首次重试延迟(示例设为1000)
修改后的配置示例:
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate"> <property name="environment"> <props> <prop key="java.naming.provider.url">tcp://usitdjms-vd3:7222,tcp://usitdjms-vd4:7222</prop> <prop key="java.naming.factory.initial">com.tibco.tibjms.naming.TibjmsInitialContextFactory</prop> <prop key="java.naming.security.principal">${USERNAME}</prop> <prop key="java.naming.security.credentials">${PASSWORD}</prop> <!-- 开启Tibco EMS故障转移机制 --> <prop key="com.tibco.tibjms.connect.attemptCount">-1</prop> <prop key="com.tibco.tibjms.connect.retryInterval">3000</prop> <prop key="com.tibco.tibjms.reconnect.attemptCount">-1</prop> <prop key="com.tibco.tibjms.reconnect.retryInterval">5000</prop> <prop key="com.tibco.tibjms.connect.attemptDelay">1000</prop> </props> </property> </bean>
2. 调整Spring JMS容器的连接配置
确保DefaultMessageListenerContainer的配置支持自动恢复并避免连接缓存:
- 设置
recoveryInterval:连接恢复间隔(示例设为5000毫秒) - 把
cacheLevelName设为CACHE_NONE或CACHE_CONSUMER:避免缓存旧连接导致无法切换 - 可选配置退避策略:用
ExponentialBackOff实现指数递增的重试间隔
示例配置:
<bean id="jmsListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="jmsConnectionFactory"/> <property name="destination" ref="topicDestination"/> <property name="messageListener" ref="topicMessageListener"/> <property name="recoveryInterval" value="5000"/> <property name="cacheLevelName" value="CACHE_NONE"/> <!-- 可选:指数退避重试配置 --> <property name="backOff"> <bean class="org.springframework.util.backoff.ExponentialBackOff"> <property name="initialInterval" value="3000"/> <property name="multiplier" value="1.5"/> <property name="maxInterval" value="30000"/> </bean> </property> </bean>
3. 排查客户端版本兼容性
确认使用的Tibco EMS客户端jar版本与服务器版本匹配,客户端jar必须包含com.tibco.tibjms.naming.TibjmsInitialContextFactory类,且支持多地址故障转移功能。旧版本客户端可能无法正确解析多地址URL并触发轮询。
原理说明
Tibco EMS的JNDI初始上下文工厂原生支持多地址轮询,但需要通过连接属性开启重试、重连机制。Spring JMS的DefaultMessageListenerContainer在连接失败时,会根据配置的恢复策略重新从JNDI获取连接,此时Tibco客户端会依次尝试配置的所有URL,直到成功建立连接。
内容的提问来源于stack exchange,提问作者Michael Starkie
相关产品推荐
相关产品推荐

