Jms 如何设置ActiveMQ Artemis重新交付策略以不阻塞队列

Jms 如何设置ActiveMQ Artemis重新交付策略以不阻塞队列,jms,spring-jms,activemq-artemis,Jms,Spring Jms,Activemq Artemis,背景 我在ApacheArtemis 2.7.0.redhat-00056上有一个JMS消息队列。代理配置了10分钟的重新交付延迟。如果我将消息发布到队列中,但在使用者上失败,那么它将作为预定消息返回队列,并在10分钟内交付。发布的任何后续消息都会被直接处理,因此队列不会被调度的消息阻塞 如果一系列消息快速连续发送,那么发生的情况是它们都失败并被安排在10分钟的时间内。在这种情况下,看起来Artemis试图保持消息顺序 文档 关于重新交付的文件说明如下: 其他后续消息将定期传递,只有被取消的消息

背景

我在ApacheArtemis 2.7.0.redhat-00056上有一个JMS消息队列。代理配置了10分钟的
重新交付延迟。如果我将消息发布到队列中,但在使用者上失败,那么它将作为预定消息返回队列,并在10分钟内交付。发布的任何后续消息都会被直接处理,因此队列不会被调度的消息阻塞

如果一系列消息快速连续发送,那么发生的情况是它们都失败并被安排在10分钟的时间内。在这种情况下,看起来Artemis试图保持消息顺序

文档

关于重新交付的文件说明如下:

其他后续消息将定期传递,只有被取消的消息将在延迟后异步发送回队列

问题

在我看来,这似乎是不一致的,如果您连续发布消息,则Artemis似乎会保留顺序,而如果消息之间存在轻微延迟,则队列不会阻塞,并且只有失败的消息会被延迟调度(根据文档)

我试图找到一个解决方案,这样,如果一条消息失败,需要在10分钟内重新发送,它就不会阻止后续消息

示例

它不需要任何特殊的东西来重建这个。如上所述,您只需要将一些消息快速连续地发送到代理上具有重新交付策略的队列。我一直在使用一个基本示例进行测试,如下所示:

Spring启动应用程序,启动时生成五条消息

@SpringBootApplication
public class ArtemisTestApplication
{

    private Logger logger = LoggerFactory.getLogger(ArtemisTestApplication.class);

    @Autowired
    private JmsTemplate jmsTemplate;

    @PostConstruct
    public void init()
    {
        send("Message1");
        send("Message2");
        send("Message3");
        send("Message4");
        send("Message5");
    }

    public void send(String msg)
    {
        logger.debug("Sending message :{}", msg);
        jmsTemplate.convertAndSend("jms.queue.TestQueue", msg);
    }

    public static void main(String[] args)
    {
        SpringApplication.run(ArtemisTestApplication.class, args);
    }

}
使用消息并引发错误以触发重新交付策略

@Component
public class TestConsumer
{
    private Logger logger = LoggerFactory.getLogger(TestConsumer.class);

    @JmsListener(destination = "jms.queue.TestQueue")
    public void receive(TextMessage message) throws JMSException
    {
        logger.debug("Message received: {}", message.getText());
        throw new RuntimeException("Force redelivery policy");
    }
}
该应用程序是使用生成的。除了给它起个名字外,唯一值得注意的是消息传递下的artemis依赖性

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-artemis</artifactId>
    </dependency>
在代理上,我用重新交付策略配置了队列。注意:我在这里将延迟设置为0,问题仍然存在,在第一条消息尝试三次并移动到DLQ之前,所有消息都被阻止。如果将延迟更改为正数,则会看到所有五封邮件都安排在稍后发送

<address-settings>      
    <address-setting match="jms.queue.TestQueue">            
        <dead-letter-address>DLQ</dead-letter-address>                      
        <redelivery-delay>0</redelivery-delay>    
        <max-delivery-attempts>3</max-delivery-attempts>
    </address-setting>    
  </address-settings>

<addresses>     
    <address name="DLQ">            
        <anycast>               
          <queue name="DLQ" />            
        </anycast>         
      </address>       
      <address name="jms.queue.TestQueue">            
        <anycast>               
          <queue name="jms.queue.TestQueue" />            
        </anycast>         
      </address>                      
</addresses>

DLQ
0
3.

我得出结论,这是Artemis的一个缺陷。我为此开了一张罚单,并给其他遇到同样问题的人留下了一条评论


与此同时,我不得不更改我们的客户端应用程序来处理重新交付策略本身。如果读取消息时出错,则我们在消息上增加一个计数器,并将其作为具有所需延迟的新消息写入。然后确认正在使用的消息以解除队列阻塞并允许读取其他消息。我已经在代理上保留了重新交付策略的配置,以防出现超出此逻辑的错误或未捕获的内容。这并不理想,但至少现在它满足了要求。

您能为此提供一个解决方案吗?您也可以说,“在这种情况下,Artemis试图保持消息顺序。”你这一说法的依据是什么?在我昨天读到的各种文章中,我发现一些东西说,阿耳特弥斯将努力维护同一制作人发送的消息的秩序。除此之外,这是一个轶事,我可以看到使用listScheduledMessages操作的消息组,它们都被安排在10分钟后,但是,只有第一个消息被实际传递,当随后的消息最终被传递时,传递计数是1。因此,它的行为确实像是在保留顺序。我已经根据要求添加了一个示例,尽管我认为它在这里没有多大帮助。代码没有什么特别之处,它是快速连续添加一些消息,然后使用重新交付策略使用它们的最基本示例。这是一个关于队列如何工作的问题,而不是我的代码。Artemis将尝试保留由同一生产者发送的消息的顺序,因为这是JMS规范所期望的。然而,诸如集群中消息的重新分发或负载平衡或重新交付延迟之类的事情最终可能会改变顺序。JMS规范没有解决所有这些问题。正如JIRA中所指出的,当前的解决方法是在客户端URL上设置
consumerWindowSize=0
<address-settings>      
    <address-setting match="jms.queue.TestQueue">            
        <dead-letter-address>DLQ</dead-letter-address>                      
        <redelivery-delay>0</redelivery-delay>    
        <max-delivery-attempts>3</max-delivery-attempts>
    </address-setting>    
  </address-settings>

<addresses>     
    <address name="DLQ">            
        <anycast>               
          <queue name="DLQ" />            
        </anycast>         
      </address>       
      <address name="jms.queue.TestQueue">            
        <anycast>               
          <queue name="jms.queue.TestQueue" />            
        </anycast>         
      </address>                      
</addresses>