我们在生产环境使用了ActiveMQ作为消息中间件,消息中间件连接到数据库对消息进行持久化。

最近发生了一个奇怪的事情,消费者端的生产日志总是报如下错误:

The JMS connection has failed: java.io.EOFException

Successfully refreshed JMS Connection

这个日志在生产环境大量重复,第一个错误是EOFException,当一个连接的远端主动关闭连接时,本端会接收到这个异常。

第二行的消息咋一看,是“Successfully refreshed JMS Connection”,理论上此时连接已经恢复,但是消费者依然无法获取信息。

将这个问题放到Baidu,Bing和Google上搜索,也看到了其他用户遇到过类似情况,但是仔细研究一下发现并不是我们遇到的情况,他们提供的解决方案也无法解决我们的问题。

就在问题要陷入僵局的时候,我们发现:

1. 在错误消息刷出来之前,ActiveMQ报了错,疑似它使用的持久化数据库中途挂掉,且被重启了

2. 同一个MQ有几个组件作为消费端,但是只有这个组件刷错误日志,其他组件正常恢复了连接

初步分析是因为数据库挂掉导致连接失效,因为发现MQ日志中,数据库连接报错之后很短的时间间隔内客户端就开始刷日志。

很奇怪的是其他几个组件没有问题,所以我们对比了这几个组件之间的配置文件差异,发现只有这个组件使用了SingleConnectionFactory,其他组件使用的都是CachingConnectionFactory,所以怀疑问题出在了这个配置上。

对比了下这两个类的源代码,这是CachingConnectionFactory.java的注释部分和第一行代码:

/**
* {@link SingleConnectionFactory} subclass that adds {@link javax.jms.Session}
* caching as well {@link javax.jms.MessageProducer} caching. This ConnectionFactory
* also switches the {@link #setReconnectOnException "reconnectOnException" property}
* to "true" by default, allowing for automatic recovery of the underlying Connection.
*
......
*
* @author Juergen Hoeller
* @since 2.5.3
*/ public class CachingConnectionFactory extends SingleConnectionFactory {
......

大家可以注意到,这个类就是扩展了SingleConnectionFactory类,并且将reconnectOnException设置为true

至此,问题已经有一点眉目了,如果连接出现异常,通过reconnectOnException决定是否reconnect(重连接),这个属性在SingleConnectionFactory默认设置为false的(可以参见它的代码,默认设置为false),但是在CachingConnectionFactory中设置为true,这就是为何连接失效了,客户端却没能重新连上的原因。

进一步检查日志中消息“Successfully refreshed JMS Connection”的来源,可以进一步印证我们的看法:

请查看DefaultMessageListenerContainer.java:

package org.springframework.jms.listener;
... public class DefaultMessageListenerContainer extends AbstractPollingMessageListenerContainer {
...
protected void refreshConnectionUntilSuccessful() {
while (true) {
if (this.isRunning()) {
try {
if (this.sharedConnectionEnabled()) {
this.refreshSharedConnection();
} else {
Connection con = this.createConnection();
JmsUtils.closeConnection(con);
} this.logger.info("Successfully refreshed JMS Connection");
} catch (Exception var3) {
...
}
...
}

可以看到,上面代码中的消息,正是我们在日志中反复看到的信息,而通过检查refreshSharedConnection()和createConnection(),我们发现:

refreshSharedConnection()调用链:

AbstractJmsListeningContainer.refreshSharedConnection()
AbstractJmsListeningContainer.createSharedConnection()
JmsAccessor.createConnection()
JmsAccessor.getConnectionFactory().createConnection()

createConnection()调用链:

JmsAccessor.createConnection()
JmsAccessor.getConnectionFactory().createConnection()

可以看出,这两个分支最后都是到连接工厂中调用createConnection(),查看下代码:

package org.springframework.jms.connection;

public class SingleConnectionFactory
...
public Connection createConnection() throws JMSException {
Object var1 = this.connectionMonitor;
synchronized (this.connectionMonitor) {
if (this.connection == null) {
this.initConnection();
} return this.connection;
}
}
...
}

使用SingleConnectionFactory时,如果连接对象connection不为空,即使此时连接失效,依然不会进入initConnection,所以虽然connection返回了非null值,但是这个连接其实是坏的

至此,原因查明,将SingleConnectionFactory改为CachingConnectionFactory后,此问题消失,当客户端发现连接失效后,能够主动连接到MQ服务器

思考:为何设置了“reconnectOnException”属性后,就能够自动重连了呢?

找到代码中使用了这个变量的位置:

package org.springframework.jms.connection;

public class SingleConnectionFactory
...
protected void prepareConnection(Connection con) throws JMSException {
if (this.getClientId() != null) {
con.setClientID(this.getClientId());
} if (this.getExceptionListener() != null || this.isReconnectOnException()) {
ExceptionListener listenerToUse = this.getExceptionListener();
if (this.isReconnectOnException()) {
listenerToUse = new InternalChainedExceptionListener(this, (ExceptionListener) listenerToUse);
} con.setExceptionListener((ExceptionListener) listenerToUse);
} }
...
}

代码中添加了一个异常监听器,此监听器触发的代码为:

package org.springframework.jms.connection;

public class SingleConnectionFactory
{
... public void onException(JMSException ex) {
this.logger.warn("Encountered a JMSException - resetting the underlying JMS Connection", ex);
this.resetConnection();
} public void resetConnection() {
Object var1 = this.connectionMonitor;
synchronized (this.connectionMonitor) {
if (this.target != null) {
this.closeConnection(this.target);
} this.target = null;
this.connection = null;
}
}
...
}

可见,当发生异常时,异常监听器调用了resetConnection()函数,此函数会将connection设置为null,然后DefaultMessageListenerContainer中的监视线程经过一段时间即能重新建立此连接

在ActiveMQ中使用SingleConnectionFactory遇到的坑的更多相关文章

  1. 【Fine原创】JMeter分布式测试中踩过的那些坑

    最近因为项目需要,研究了性能测试的相关内容,并且最终选用了jmeter这一轻量级开源工具.因为一直使用jmeter的GUI模式进行脚本设计,到测试执行阶段工具本身对资源的过量消耗给性能测试带来了瓶颈, ...

  2. ActiveMQ中的安全机制 [转]

    本文简单介绍ActiveMQ通过JAAS实现的安全机制.JAAS(Java Authentication and Authorization Service)也就是java认证/授权服务.这是两种不同 ...

  3. Golang中WaitGroup使用的一点坑

    Golang中WaitGroup使用的一点坑 Golang 中的 WaitGroup 一直是同步 goroutine 的推荐实践.自己用了两年多也没遇到过什么问题.直到一天午睡后,同事扔过来一段奇怪的 ...

  4. spring+activemq中多个consumer同时处理消息时遇到的性能问题

    最近在做数据对接的工作,用到了activemq,我需要从activemq中接收消息并处理,但是我处理数据的步骤稍微复杂,渐渐的消息队列中堆的数据越来越多,就想到了我这边多开几个线程来处理消息. 可是会 ...

  5. ActiveMQ中Broker的应用与启动方式

    Broker:英语有代理的意思,在activemq中,Broker就相当于一个Activemq实例. 1. 命令行启动实例: 1.activemq start使用默认的activemq.xml启动 E ...

  6. ActiveMQ 中的链表

    ActiveMQ 中的消息在内存中时,以链表形式保存,以 PendingList 表示,每一个消息是 PendingNode. PendingList 主要有2种实现:OrderedPendingLi ...

  7. Vue中的slot(占坑,预留位置)

    Vue中的slot(占坑,预留位置) 子模板不使用slot 子模板使用slot 子模板使用使用name属性,且传递data 文件名:Slots.vue //slot组件 <template> ...

  8. 微信小程序中scroll-view的几个坑

    微信小程序中scroll-view的几个坑 1:设置scroll-x时,却不能横向滚动,因为view是block组件,但是这里用了flex就不能滚动了(想用flex布局,请开启属性enable-fle ...

  9. Spring Boot 监听 Activemq 中的特定 topic ,并将数据通过 RabbitMq 发布出去

    1.Spring Boot 和 ActiveMQ .RabbitMQ 简介 最近因为公司的项目需要用到 Spring Boot , 所以自学了一下, 发现它与 Spring 相比,最大的优点就是减少了 ...

随机推荐

  1. 以绝对优势立足:从CDN和云存储来聊聊云生态的崛起

    以绝对优势立足:从CDN和云存储来聊聊云生态的崛起 前面几期文章我们介绍了混合云模式,以及面向应用层的云架构解决方案的Spring Cloud.接下来,我们就以蘑菇街的两个具体案例,来分享一下基于混合 ...

  2. 敏捷史话(十一):敏捷宣言“间谍”——Steve Mellor

    Steve Mellor 是敏捷宣言的签署人之一,他自称是作为" 间谍"去参加雪鸟会议的. 起初收到会议邀请时,Steve 非常惊讶,因为他所做的工作一直都是关于建模方面的,很少将 ...

  3. Distributed | Raft

    1. 复制状态机 一致性算法是在复制状态机的背景下产生的.在这种方法下,一组服务器的状态机计算相同状态的相同副本,即使某些服务器宕机,也可以继续运行. 复制状态机通常使用复制日志实现,每个服务器存储一 ...

  4. Kubernetes Secrets

    Secrets 背景信息 Kubernetes版本 [09:08:04 yhf@test ~]$ kubectl version Client Version: version.Info{Major: ...

  5. 女娲造人引发思考之Java设计模式:工厂模式

    目录 工厂模式的几种形态 简单工厂模式 示例 结构 优缺点 女娲抟土造人 工厂方法模式 结构 女娲举绳造人 抽象工厂模式 结构 女娲造万物 工厂模式的几种形态 工厂模式专门负责将大量有共同接口的类实例 ...

  6. 前端进阶(1)Web前端性能优化

    前端进阶(1)Web前端性能优化 Web前端性能优化, 不仅能够改善站点的用户体验,并且能够节省相当的资源利用.下面将从1)服务器.2)html内容.3)css. 4)javascript. 5)图片 ...

  7. Score UVA - 1585

    ​ There is an objective test result such as "OOXXOXXOOO". An 'O' means a correct answer of ...

  8. ML.NET 示例:图像分类模型训练-首选API(基于原生TensorFlow迁移学习)

    ML.NET 版本 API 类型 状态 应用程序类型 数据类型 场景 机器学习任务 算法 Microsoft.ML 1.5.0 动态API 最新 控制台应用程序和Web应用程序 图片文件 图像分类 基 ...

  9. Powershell免杀

    Powershell免杀 0x01 前言 前几天搞一个站的时候,进入内网,想让内网一台机子powershell上线.然后被杀软拦截了,极其的不讲武德,想着找我极强的朋友们白嫖个免杀的方法. 后面还是没 ...

  10. 病毒木马查杀实战第011篇:QQ盗号木马之专杀工具的编写

    前言 由于我已经在<病毒木马查杀第004篇:熊猫烧香之专杀工具的编写>中编写了一个比较通用的专杀工具的框架,而这个框架对于本病毒来说,经过简单修改也是基本适用的,所以本文就不讨论那些重叠的 ...