Spring Boot结合Solace动态创建JMS队列监听器异常排查
Spring Boot JMS动态监听器Solace绑定异常解决方案
你在使用Spring Boot+JMS动态创建多队列监听器时,通过Test Container运行Solace容器,出现以下异常,监听器仅偶尔能捕获消息:
10:51:38.633 WARN Setup of JMS message listener invoker failed for destination 'toto' - trying to recover. Cause: SMF AD bind response error [condition = amqp:internal-error]
问题根源分析
- 重复创建容器工厂:循环中每次调用
jmsFactory()生成新的DefaultJmsListenerContainerFactory,每个工厂使用随机clientId,导致Solace收到大量重复/变化的订阅请求,触发内部错误。 - 持久订阅配置冗余:你监听的是队列(
pubSubDomain=false),但设置了setSubscriptionDurable(true),该配置仅适用于主题的持久订阅,队列本身具备持久化特性,此配置会引发不必要的Solace绑定逻辑。 - 容器工厂未复用:Spring的
@Bean方法默认是单例,但循环内直接调用方法会绕过单例机制,创建多个工厂实例,导致资源浪费和配置冲突。
修复方案及代码示例
- 移除队列不需要的持久订阅、随机clientId配置,将容器工厂设为单例复用
- 在循环外部统一设置容器工厂,所有动态端点共享同一个实例
- 优化连接工厂注入方式,符合Spring最佳实践
- 添加错误处理器,统一处理监听器异常
修改后的代码:
import lombok.extern.slf4j.Slf4j; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import org.apache.qpid.jms.JmsConnectionFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jms.annotation.JmsListenerConfigurer; import org.springframework.jms.config.DefaultJmsListenerContainerFactory; import org.springframework.jms.config.JmsListenerContainerFactory; import org.springframework.jms.config.JmsListenerEndpointRegistrar; import org.springframework.jms.config.SimpleJmsListenerEndpoint; import org.springframework.jms.support.converter.MappingJackson2MessageConverter; import org.springframework.jms.support.converter.MessageType; import org.springframework.util.ErrorHandler; import javax.jms.JMSException; @Slf4j @Configuration public class ListenersConfiguration implements JmsListenerConfigurer { private final JmsConnectionFactory connectionFactory; // 构造注入替代字段注入,符合Spring最佳实践 public ListenersConfiguration(JmsConnectionFactory connectionFactory) { this.connectionFactory = connectionFactory; } @Bean JmsListenerContainerFactory<?> jmsFactory() { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); factory.setSessionTransacted(false); factory.setPubSubDomain(false); // 配置消息转换器 ObjectMapper objectMapper = new ObjectMapper() .findAndRegisterModules() .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); MappingJackson2MessageConverter messageConverter = new MappingJackson2MessageConverter(); messageConverter.setObjectMapper(objectMapper); messageConverter.setTargetType(MessageType.TEXT); factory.setMessageConverter(messageConverter); // 添加错误处理器,统一处理异常 factory.setErrorHandler(error -> log.error("监听器处理异常", error)); return factory; } @Override public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) { // 复用单例容器工厂 JmsListenerContainerFactory<?> containerFactory = jmsFactory(); registrar.setContainerFactory(containerFactory); dbbRoutes.getRoutes().forEach(route -> { SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint(); endpoint.setId("jms-listener-" + route.getInput()); // 使用有意义的ID便于排查问题 endpoint.setDestination(route.getInput()); endpoint.setMessageListener(message -> { try { log.info("Received ID: {} Destination {}", message.getJMSMessageID(), message.getJMSDestination()); } catch (JMSException e) { log.error("读取消息异常", e); } }); registrar.registerEndpoint(endpoint); }); } }
额外注意事项
- 确保Solace容器的AMQP端口(默认5672)已正确映射到Test Container
- 验证目标队列(如
toto)已提前创建,或配置Solace允许自动创建队列(需确保连接账号具备对应权限) - 若仍有异常,可通过
factory.setConcurrency("1-1")设置单线程监听,避免Solace连接过载
内容的提问来源于stack exchange,提问作者elpazio
相关产品推荐
相关产品推荐

