spring 集成ActiveMQ发送消息Hang住的问题
来源:互联网 发布:苹果mac看视频软件推荐 编辑:程序博客网 时间:2024/04/28 17:46
spring提供了一个简化JMS API的框架,利用spring可以不用关心connection和session的管理,并且提供了JmsTemplate
模板使开发者可以专注于业务上的收发消息
为开发者提供了便利。在使用spring框架集成activeMQ5.12版本时遇到一个问题,activeMQ5.12独立部署于10.137.100.54机器上本地开发
使用配置
<bean id="jmsFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL">
<value>failover:(tcp://10.137.100.54:61616)?timeout=500</value>
</property>
<property name="sendTimeout" value="1000"/>
<property name="clientID" value="useTimeout2000"/>
</bean>
</property>
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactory"/>
<property name="defaultDestination" ref="topicDestination"/>
</bean>
如果54机器上的MQ Broker正常运行Spring能够稳定的管理JMS并且 平稳的收发消息,如果这时停掉Broker的服务
发送消息的线程将会发生阻塞,虽然设置了timeout超时但是丝毫不起作用。
如果发生这种情况将会对整个应用造成性能影响甚至宕机。
在这个问题的分析过程中尝试把activeMQ的客户端版本降为5.11.1结果还是会发生阻塞,最后版本降为5.10.0竟然可以正常抛出异常了。
5.10.0和5.12.0在版本升级的过程中究竟发生了什么变更导致前后出现如此的差异,下面是两个版本的代码
5.10版本代码
org.apache.activemq.transport.failover.FailoverTransport类里面有个方法
@Override
public void oneway(Object o) throws IOException {
Command command = (Command) o;
Exception error = null;
try {
synchronized (reconnectMutex) {
if (command != null && connectedTransport.get() ==null) {
if (command.isShutdownInfo()) {
// Skipping send of ShutdownInfo command when not connected.
return;
} else if (command instanceof RemoveInfo || command.isMessageAck()) {
// Simulate response to RemoveInfo command or MessageAck (as it will be stale)
stateTracker.track(command);
if (command.isResponseRequired()) {
Response response = new Response();
response.setCorrelationId(command.getCommandId());
myTransportListener.onCommand(response);
}
return;
} else if (command instanceof MessagePull) {
// Simulate response to MessagePull if timed as we can't honor that now.
MessagePull pullRequest = (MessagePull) command;
if (pullRequest.getTimeout() != 0) {
MessageDispatch dispatch = new MessageDispatch();
dispatch.setConsumerId(pullRequest.getConsumerId());
dispatch.setDestination(pullRequest.getDestination());
myTransportListener.onCommand(dispatch);
}
return;
}
}
// Keep trying until the message is sent.
for (int i = 0; !disposed; i++) {
try {
// Wait for transport to be connected.
Transport transport = connectedTransport.get();
long start = System.currentTimeMillis();
boolean timedout =false;
while (transport ==null && !disposed && connectionFailure == null
&& !Thread.currentThread().isInterrupted()) {
if (LOG.isTraceEnabled()) {
LOG.trace("Waiting for transport to reconnect..: " + command);
}
long end = System.currentTimeMillis();
if (timeout > 0 && (end - start > timeout)) {
timedout =true;
//5.10.0版本只要超时就退出
if (LOG.isInfoEnabled()) {
LOG.info("Failover timed out after " + (end - start) + "ms");
}
break;
}
try {
reconnectMutex.wait(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
if (LOG.isDebugEnabled()) {
LOG.debug("Interupted: " + e, e);
}
}
transport = connectedTransport.get();
}
if (transport == null) {
// Previous loop may have exited due to use being
// disposed.
if (disposed) {
error = new IOException("Transport disposed.");
} else if (connectionFailure !=null) {
error = connectionFailure;
} else if (timedout ==true) {
error =new IOException("Failover timeout of " + timeout + " ms reached.");
} else {
error = new IOException("Unexpected failure.");
}
break;
}
Tracked tracked = null;
try {
tracked = stateTracker.track(command);
} catch (IOException ioe) {
LOG.debug("Cannot track the command " + command, ioe);
}
// If it was a request and it was not being tracked by
// the state tracker,
// then hold it in the requestMap so that we can replay
// it later.
synchronized (requestMap) {
if (tracked != null && tracked.isWaitingForResponse()) {
requestMap.put(Integer.valueOf(command.getCommandId()), tracked);
} else if (tracked ==null && command.isResponseRequired()) {
requestMap.put(Integer.valueOf(command.getCommandId()), command);
}
}
// Send the message.
try {
transport.oneway(command);
stateTracker.trackBack(command);
} catch (IOException e) {
// If the command was not tracked.. we will retry in
// this method
if (tracked == null) {
// since we will retry in this method.. take it
// out of the request
// map so that it is not sent 2 times on
// recovery
if (command.isResponseRequired()) {
requestMap.remove(Integer.valueOf(command.getCommandId()));
}
// Rethrow the exception so it will handled by
// the outer catch
throw e;
} else {
// Handle the error but allow the method to return since the
// tracked commands are replayed on reconnect.
if (LOG.isDebugEnabled()) {
LOG.debug("Send oneway attempt: " + i + " failed for command:" + command);
}
handleTransportFailure(e);
}
}
return;
} catch (IOException e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Send oneway attempt: " + i + " failed for command:" + command);
}
handleTransportFailure(e);
}
}
}
} catch (InterruptedException e) {
// Some one may be trying to stop our thread.
Thread.currentThread().interrupt();
throw new InterruptedIOException();
}
if (!disposed) {
if (error != null) {
if (error instanceof IOException) {
throw (IOException) error;
}
throw IOExceptionSupport.create(error);
}
}
}
5.12版本代码
@Override
public void oneway(Object o) throws IOException {
Command command = (Command) o;
Exception error = null;
try {
synchronized (reconnectMutex) {
if (command != null && connectedTransport.get() ==null) {
if (command.isShutdownInfo()) {
// Skipping send of ShutdownInfo command when not connected.
return;
} else if (command instanceof RemoveInfo || command.isMessageAck()) {
// Simulate response to RemoveInfo command or MessageAck (as it will be stale)
stateTracker.track(command);
if (command.isResponseRequired()) {
Response response = new Response();
response.setCorrelationId(command.getCommandId());
myTransportListener.onCommand(response);
}
return;
} else if (command instanceof MessagePull) {
// Simulate response to MessagePull if timed as we can't honor that now.
MessagePull pullRequest = (MessagePull) command;
if (pullRequest.getTimeout() != 0) {
MessageDispatch dispatch = new MessageDispatch();
dispatch.setConsumerId(pullRequest.getConsumerId());
dispatch.setDestination(pullRequest.getDestination());
myTransportListener.onCommand(dispatch);
}
return;
}
}
// Keep trying until the message is sent.
for (int i = 0; !disposed; i++) {
try {
// Wait for transport to be connected.
Transport transport = connectedTransport.get();
long start = System.currentTimeMillis();
boolean timedout =false;
while (transport ==null && !disposed && connectionFailure == null
&& !Thread.currentThread().isInterrupted()) {
if (LOG.isTraceEnabled()) {
LOG.trace("Waiting for transport to reconnect..: " + command);
}
long end = System.currentTimeMillis();
if (command.isMessage() && timeout > 0 && (end - start > timeout)) {
//只有发送消息时的超时才会退出,导致最终抛出timeout的异常
timedout =true;
if (LOG.isInfoEnabled()) {
LOG.info("Failover timed out after " + (end - start) + "ms");
}
break;
}
try {
reconnectMutex.wait(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
if (LOG.isDebugEnabled()) {
LOG.debug("Interupted: " + e, e);
}
}
transport = connectedTransport.get();
}
if (transport == null) {
// Previous loop may have exited due to use being
// disposed.
if (disposed) {
error = new IOException("Transport disposed.");
} else if (connectionFailure !=null) {
error = connectionFailure;
} elseif (timedout == true) {
error =new IOException("Failover timeout of " + timeout + " ms reached.");
} else {
error = new IOException("Unexpected failure.");
}
break;
}
Tracked tracked = null;
try {
tracked = stateTracker.track(command);
} catch (IOException ioe) {
LOG.debug("Cannot track the command " + command, ioe);
}
// If it was a request and it was not being tracked by
// the state tracker,
// then hold it in the requestMap so that we can replay
// it later.
synchronized (requestMap) {
if (tracked != null && tracked.isWaitingForResponse()) {
requestMap.put(Integer.valueOf(command.getCommandId()), tracked);
} else if (tracked ==null && command.isResponseRequired()) {
requestMap.put(Integer.valueOf(command.getCommandId()), command);
}
}
// Send the message.
try {
transport.oneway(command);
stateTracker.trackBack(command);
if (command.isShutdownInfo()) {
shuttingDown = true;
}
} catch (IOException e) {
// If the command was not tracked.. we will retry in
// this method
if (tracked == null) {
// since we will retry in this method.. take it
// out of the request
// map so that it is not sent 2 times on
// recovery
if (command.isResponseRequired()) {
requestMap.remove(Integer.valueOf(command.getCommandId()));
}
// Rethrow the exception so it will handled by
// the outer catch
throw e;
} else {
// Handle the error but allow the method to return since the
// tracked commands are replayed on reconnect.
if (LOG.isDebugEnabled()) {
LOG.debug("Send oneway attempt: " + i + " failed for command:" + command);
}
handleTransportFailure(e);
}
}
return;
} catch (IOException e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Send oneway attempt: " + i + " failed for command:" + command);
}
handleTransportFailure(e);
}
}
}
} catch (InterruptedException e) {
// Some one may be trying to stop our thread.
Thread.currentThread().interrupt();
throw new InterruptedIOException();
}
if (!disposed) {
if (error != null) {
if (error instanceof IOException) {
throw (IOException) error;
}
throw IOExceptionSupport.create(error);
}
}
}
可以看出一个显著的区别5.12.0版本多了一个command.isMessage()的判断,也就是说只有是消息类型的操作时才会发生timeout的异常
当创建连接,建立session的过程中5.12.0版本不会抛出异常,而实际上创建connection和session都会执行oneway(Object o)的操作。
再来看看spring JmsTemplate发送消息的代码
public <T> T execute(SessionCallback<T> action, boolean startConnection) throws JmsException {
Assert.notNull(action, "Callback object must not be null");
Connection conToClose = null;
Session sessionToClose = null;
try {
Session sessionToUse = ConnectionFactoryUtils.doGetTransactionalSession(
getConnectionFactory(),this.transactionalResourceFactory, startConnection);
if (sessionToUse ==null) {
conToClose = createConnection();
sessionToClose = createSession(conToClose);
if (startConnection) {
conToClose.start();
}
sessionToUse = sessionToClose;
}
if (logger.isDebugEnabled()) {
logger.debug("Executing callback on JMS Session: " + sessionToUse);
}
return action.doInJms(sessionToUse);
}
catch (JMSException ex) {
throw convertJmsAccessException(ex);
}
finally {
JmsUtils.closeSession(sessionToClose);
ConnectionFactoryUtils.releaseConnection(conToClose, getConnectionFactory(), startConnection);
}
可以看出在发送消息时JmsTemplate会不断地获取获取connection和session这就造成了阻塞的问题。
这个问题应该是伴随着ActiveMQ从5.10.0向上升级的过程中产生的很可能是升级过程中产生的一个新BUG。
- spring 集成ActiveMQ发送消息Hang住的问题
- 在Spring下集成ActiveMQ 实现点到点的消息发送
- spring集成activemq消息中间件
- spring集成ActiveMQ, 消息提供者
- ActiveMQ之spring集成消息转换器MessageConverter
- spring boot 集成activeMQ消息队列
- Spring集成ActiveMQ消息队列使用
- Spring + ActiveMQ实现jms发送消息
- activemq+spring 持久化发送消息
- Spring + ActiveMQ实现jms发送消息
- activemq+spring 持久化发送消息
- activemq+spring 持久化发送消息
- activemq+spring 持久化发送消息
- activemq+spring 持久化发送消息
- Spring + ActiveMQ兑现jms发送消息
- Spring + ActiveMQ兑现jms发送消息
- ActiveMQ 整合 Spring 发送和接收消息
- activeMQ消息队列消息的发送
- 行编辑器 2016年360笔试题目 字符串处理
- 自定义Adapter
- Quartz2D绘图<二>绘制实线和虚线
- 研磨设计模式--单例模式-内部类
- 回调函数
- spring 集成ActiveMQ发送消息Hang住的问题
- xcode升级后ios9.0不能使用http协议的版本的解决方法介绍
- iOS 滚动视图(UIScrollView) UIPageControl
- Android应用多渠道打包脚本
- 静态分析工具PMD使用说明
- 今天学了一个简单的新技能Xcode6以后创建工程后没有.pch文件,所以来个技能--iOS开发
- 【IOS 开发学习总结-OC-24】★★★objective-c——内存管理
- 为什么要加号"+"方法?为什么方法前面要写“+”号?很多人由于书本的术语太专业了,暂时看不懂,理解不到--iOS开发
- 自动检测当前运行环境是(开发环境)debug状态还是(真机)release状态--iOS开发