Java 如果所有侦听器都已销毁,为什么ActiveMQ Artemis会自动删除地址?

Java 如果所有侦听器都已销毁,为什么ActiveMQ Artemis会自动删除地址?,java,jms,spring-jms,activemq-artemis,Java,Jms,Spring Jms,Activemq Artemis,我们正在开发一个微服务系统,该系统使用ActiveMQ Artemis作为服务之间的通信方式。由于要求能够在运行时停止侦听器,我们不能使用spring artemis提供的@JmsListener。在挖掘互联网并发现spring在场景背后使用了MessageListenerContainer之后,我们想出了一个想法,维护一个MessageListenerContainer我们自己的列表 @Bean(name=“commandJmsListenerContainerFactory”) 公共默认jm

我们正在开发一个微服务系统,该系统使用ActiveMQ Artemis作为服务之间的通信方式。由于要求能够在运行时停止侦听器,我们不能使用spring artemis提供的
@JmsListener
。在挖掘互联网并发现spring在场景背后使用了
MessageListenerContainer
之后,我们想出了一个想法,维护一个
MessageListenerContainer
我们自己的列表

@Bean(name=“commandJmsListenerContainerFactory”)
公共默认jmsListenerContainerFactory命令jmsListenerContainerFactory(
DefaultJmsListenerContainerFactoryConfigurer(配置器){
var factory=new DefaultJmsListenerContainerFactory();
configurer.configure(工厂、连接工厂);
factory.setPubSubDomain(false);
返回工厂;
}
//使用
私有映射命令队列;
public void subscribeCommandQueue(字符串queueName,CommandListener命令){
commandQueue.computeIfAbsent(队列名称,键->{
var endPoint=新的SimpleJMListenerEndpoint();
setDestination(queueName);
endPoint.setMessageListener(消息->{
试一试{
var body=message.getBody(String.class);
execute(commandMessageConverter.deserialize(body));
}捕获(JME){
抛出新的RuntimeException(“处理队列的消息时出错:“+queueName,e”);
}
});
var container=commandJmsListenerContainerFactory.createListenerContainer(端点);
// https://stackoverflow.com/questions/44555106/defaultmessagelistenercontainer-not-reading-messages-from-ibm-mq
//对于实现手动创建的InitializingBean的Spring类的每个对象,我们需要调用AfterPropertieSet使对象“工作”
container.afterPropertieSet();
container.start();
返回容器;
});
}
公开作废开始(){
commandQueue=新的ConcurrentHashMap();
}
公共停车场(){
commandQueue.values().forEach(DefaultMessageListenerContainer::destroy);
commandQueue.clear();
}
在测试时,我注意到在调用
stop()
销毁所有侦听器之后,Artemis控制台中的队列和地址也被删除。对于持久订阅,情况并非如此

@Bean(name=“eventJmsListenerContainerFactory”)
公共默认jmsListenerContainerFactory事件jmsListenerContainerFactory(
Caching连接工厂Caching连接工厂,
DefaultJmsListenerContainerFactoryConfigurer(配置器){
cachingConnectionFactory.setClientId(UUID.randomUUID().toString());
var factory=new DefaultJmsListenerContainerFactory();
configurer.configure(工厂,cachingConnectionFactory);
factory.setPubSubDomain(true);
factory.setSubscriptionDurable(true);
返回工厂;
}
//用法与第一个块代码相同,只是我们将多播订阅存储在另一个映射中
私有地图事件主题;
运行单元测试并销毁两个映射的所有侦听器后,只保留了
测试事件主题
地址及其队列,删除了
测试命令队列
。为什么两个队列的行为不同

还有,什么是正确的行为?我们担心自动删除会删除队列中尚未发送的邮件。另一方面,如果我们一次又一次地运行测试,那么
测试事件主题下的新队列将继续创建。我认为这是因为行
cachingConnectionFactory.setClientId(UUID.randomuid().toString())。但对于持久订阅,不设置clientId会导致错误


应用程序中使用的连接工厂是spring artemis创建的
CachingConnectionFactory
,默认情况下,当核心JMS客户端发送消息或创建消费者时,代理将根据需要自动创建地址和队列。默认情况下,当不再需要这些资源时(即,当队列没有使用者和消息时,或当地址不再绑定任何队列时),这些资源也将自动删除。这由
broker.xml
中的以下设置控制:

  • 自动创建队列
  • 自动删除队列
  • 自动创建地址
  • 自动删除地址
需要明确的是,默认情况下,自动删除不应导致任何消息丢失,因为只有当队列具有0个使用者和0条消息时,才应删除队列。但是,您始终可以将自动删除设置为
false
以确保100%安全


表示持久JMS主题订阅的队列将不会被删除,因为它们将在使用者脱机时保留并收集消息。换句话说,如果使用订阅的客户端关闭而没有首先明确删除订阅,则持久主题订阅将保留。这就是持久订阅的全部意义——它们是持久的。任何客户端都可以使用持久主题订阅,前提是它使用相同的客户端ID连接并使用相同的订阅名称。但是,除非持久订阅是“共享”持久订阅,否则一次只能连接一个客户端。共享持久主题订阅是在JMS 2.0中添加的。

因此,即使保存客户端的服务实例关闭、终止或重新启动,持久主题队列仍然存在?我不认为任何其他新创建的相同或其他服务的实例可以重用主题队列,因此持久主题队列可能不是我们用例的选项。我更新了我的答案以回应您的评论。希望