我们的应用程序已构建并运行于: -
spring.version = 3.2.8.RELEASE
spring.amqp.version = 1.2.0.RELEASE
rabbitmq.version = 3.1.3(客户端)
RabbitMQ Server版本为3.1.5
我们想将rabbitmq服务器从3.1.5升级到3.3.5,我们成功地做到了。
现在我们想升级应用程序以使用最新版本的spring-amqp,RabbitMQ java客户端,因此我们升级了以下组件: -
spring.version = 3.2.8.RELEASE
spring.amqp.version = 1.3.0.RELEASE
rabbitmq.version = 3.2.4(客户端)
RabbitMQ Server版本为3.3.5
然而,在升级到spring-amqp到1.3.0后,我们的应用程序开始挂起。基本上我们在应用程序启动期间启动了许多监听器容器,并且启动每个监听器容器现在需要60秒才能进入下一步
挖掘深度后我发现,程序在SimpleMessageListenerContainer类的run()方法中挂起: -org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer
public void run() {
boolean aborted = false;
int consecutiveIdles = 0;
int consecutiveMessages = 0;
try {
try {
SimpleMessageListenerContainer.this.redeclareElementsIfNecessary(); // Here is where the programing thread hangs.
this.consumer.start();
this.start.countDown();
}
如上所述代码,线程在redeclareElementsIfNecessary()方法中挂起,并且此方法仅在此版本的spring-rabbit中引入。我不知道为什么它会挂在那里,无论我传递给这个SimpleMessageListenerContainer的任何建议参数,它似乎都没有工作。
如果我使用新的RabbitMQ服务器3.3.5恢复到spring-amqp 1.2.0版本,似乎一切正常,但是新的spring-amqp客户端无法正常工作。
我现在被困在这里几天了。 Spring / Rabbitmq大师在那里,你能帮我解决这个问题吗?
感谢您的快速回复,但似乎代码没有达到这一点,它只是挂在您提供的代码段上方, 我在下面评论代码挂在哪里
Set<String> queueNames = this.getQueueNamesAsSet();
Map<String, Queue> queueBeans = ((ListableBeanFactory) applicationContext).getBeansOfType(Queue.class); // The code started to hung here
for (Entry<String, Queue> entry : queueBeans.entrySet()) {
Queue queue = entry.getValue();
if (queueNames.contains(queue.getName()) && queue.isAutoDelete()
&& this.rabbitAdmin.getQueueProperties(queue.getName()) == null) {
if (logger.isDebugEnabled()) {
logger.debug("At least one auto-delete queue is missing: " + queue.getName()
+ "; redeclaring context exchanges, queues, bindings.");
}
this.rabbitAdmin.initialize();
break;
}
}
实际上我们只升级到了spring-amqp的最新版本,即
spring.version = 3.2.8.RELEASE
spring.amqp.version = 1.3.6.RELEASE
rabbitmq.version = 3.3.4 (client)
RabbitMQ Server version is 3.3.5
然而,我们遇到了完全相同的问题,所以只是为了找出问题从哪个版本开始我跑到1.3.0的低版本,似乎问题是从spring-amqp本身的1.3.0版本开始。这就是原因。
我附上了所请求的信息,包括仅基于spring-amqp 1.3.6的线程转储。
这是程序挂起的监听器容器的配置,你可以看到我们有自己的SimpleMessageLinstenerContainer,它充当了acutal spring的SimpleMessageListenerContainer的包装器, 我还附上了这个自定义包装文件供您参考。
<bean id="tlogOutOfCycleMessageListenerPrototype" class="com.myorg.ips.cnccommon.support.amqp.SimpleMessageListenerContainer" scope="prototype">
<property name="channelTransacted" value="true" />
<property name="transactionManager" ref="transactionManager" />
<property name="concurrentConsumers" value="1" />
<property name="taskExecutor" ref="tlogOutOfCycleMessageListenerPool" />
<property name="messageListener" ref="tlogMLAOutOfCycle" />
<property name="errorHandler" ref="tlogOutOfCycleMessageHandler" />
<property name="autoStartup" value="false" />
<property name="instanceNameForLogging" value="site1TlogOutOfCycleMessageListener"/>
<!-- A dummy connection factory which will never be used -->
<property name="connectionFactory" ref="switchCompositeConnectionFactoryPrototype"/>
</bean>
我们的包装类SimpleMessageListenerContainer.java
package com.myorg.ips.cnccommon.support.amqp;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.slf4j.cal10n.LocLogger;
import org.springframework.util.ErrorHandler;
import com.myorg.ips.amqp.SwitchSiteSupport;
import com.myorg.ips.logging.LoggerFactory;
import com.myorg.ips.system.config.InitialisableSiteAware;
import static com.myorg.ips.logging.SystemWideLogMessages.ERROR_AMQP_FAILED_TO_START_LISTENER;
import static com.myorg.ips.logging.SystemWideLogMessages.INFO_AMQP_STOPPING_LISTENER;
/**
*
* Wrapper for the Spring SimpleMessageListenerContainer which simply allows us to delay (or prevent startup). Can also restart on command.
*
*/
public class SimpleMessageListenerContainer extends org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer implements InitialisableSiteAware {
private static final LocLogger logger = LoggerFactory.getLogger(SimpleMessageListenerContainer.class);
private boolean autoStart = true;
private ErrorHandler exposedErrorHandler;
private boolean springBeanInitialisationAttempted = false;
private boolean springBeanInitialised = false;
private String instanceNameForLogging;
@Override
public void initialize() {
// Do nothing -- we will instead perform the Spring bean initialisation later on via the factory bean, after the connection factory has been set
springBeanInitialisationAttempted = true;
springBeanInitialised = false;
}
@Override
public void initialise() {
SwitchSiteSupport.initialiseIfSiteAware(getMessageListener());
SwitchSiteSupport.initialiseIfSiteAware(getErrorHandler());
// If this object is a Spring bean, we should now complete the initialisation that the Spring framework attempted earlier
if (springBeanInitialisationAttempted && !springBeanInitialised) {
springBeanInitialised = true;
super.initialize();
if (isAutoStartup()) {
start();
}
}
}
@Override
public void configureForSite(final MultiHostConnectionFactory configuredConnectionFactory) {
setConnectionFactory(configuredConnectionFactory);
SwitchSiteSupport.configureIfSiteAware(getMessageListener(), configuredConnectionFactory);
SwitchSiteSupport.configureIfSiteAware(getErrorHandler(), configuredConnectionFactory);
setInstanceNameForLogging(SwitchSiteSupport.replaceWithSiteAlias(instanceNameForLogging, configuredConnectionFactory));
}
@Override
//CHECKSTYLE:OFF Unfortunately the parent springframework class throws and exception, so so do we
protected void doStart() throws Exception {
//CHECKSTYLE:ON
if (autoStart) {
logger.debug("Starting message listener " + instanceNameForLogging);
super.doStart();
logger.debug("Started message listener " + instanceNameForLogging);
}
}
/**
* Start this listener
*/
public void start() {
autoStart = true;
try {
doStart();
//CHECKSTYLE:OFF Unfortunately the parent springframework class throws and exception, so that is what we catch
} catch (Exception e) {
//CHECKSTYLE:ON
logger.error(ERROR_AMQP_FAILED_TO_START_LISTENER, e);
}
}
/**
* Stop listener
*/
public void stop() {
logger.info(INFO_AMQP_STOPPING_LISTENER, getBeanName());
autoStart = false;
doStop();
}
/**
* Stop and start this listener
*/
public void restart() {
stop();
start();
}
/**
* Store the errorHandler in a subclass-specific property so that we can retrieve it later
* @param errorHandler errorHandler
*/
@Override
public void setErrorHandler(final ErrorHandler errorHandler) {
this.exposedErrorHandler = errorHandler;
super.setErrorHandler(errorHandler);
}
/**
* Return the exposed errorHandler
* @return errorHandler
*/
public ErrorHandler getErrorHandler() {
return exposedErrorHandler;
}
public void setInstanceNameForLogging(final String instanceNameForLogging) {
this.instanceNameForLogging = instanceNameForLogging;
}
@Override
public String toString(){
return ToStringBuilder.reflectionToString(this);
}
}
答案 0 :(得分:0)
反正好抓!
我现在正在使用Spring AMQP 1.4的代码。
你介意分享:
ListenerContainer
的配置redeclareElementsIfNecessary()
实际上现在代码看起来像是:
if (queueNames.contains(queue.getName()) && queue.isAutoDelete()
&& this.rabbitAdmin.getQueueProperties(queue.getName()) == null) {
if (logger.isDebugEnabled()) {
logger.debug("At least one auto-delete queue is missing: " + queue.getName()
+ "; redeclaring context exchanges, queues, bindings.");
}
this.rabbitAdmin.initialize();
break;
}
因此,它可能只会在auto-delete
队列中解除。
或者你有另一张照片吗?..
<强>更新强>
根据你的ThreadDump。这是非法的:
at com.vocalink.ips.amqp.AmqpMessageListenerManager.initialise(AmqpMessageListenerManager.java:106)
at com.vocalink.ips.amqp.SwitchSiteSupport.initialiseIfSiteAware(SwitchSiteSupport.java:29)
at com.vocalink.ips.system.config.AbstractSiteAwareComponentCachingFactory.createAndConfigureSiteAwareComponent(AbstractSiteAwareComponentCachingFactory.java:51)
您无法在初始化阶段start
组件。或者将它留给容器,或者只是在运行时在某个地方手动执行start
,此时已经创建了所有bean。
例如,您可以使用ApplicationListener<ContextRefreshedEvent>
。