使用Spring JMS和qpid使用Azure Service Bus消息时标记为延迟的消息

时间:2016-08-26 22:08:08

标签: azureservicebus spring-jms qpid

我可以很好地接收来自我的服务总线订阅的消息,但是当我的监听器发生异常时,似乎最终将状态=已修改并且undeliverableHere = true的处置帧发送到服务总线。服务总线的docs表示它不支持amqp Modified处置。

消息以服务总线中的延迟状态结束,我无法弄清楚如何将消息轻推回活动。

JMS配置:

@Bean
public ConnectionFactory jmsConnectionFactory(MessageStoreProperties properties) throws UnsupportedEncodingException {
    JmsConnectionFactory connectionFactory = new JmsConnectionFactory(properties.getUrlString());
    connectionFactory.setClientID(clientId);
    connectionFactory.setUsername(properties.getUsername());
    connectionFactory.setPassword(properties.getPassword());
    connectionFactory.setRedeliveryPolicy(redeliveryPolicy());
    return new CachingConnectionFactory(connectionFactory);
}

@Bean
public JmsDefaultRedeliveryPolicy redeliveryPolicy() {
    JmsDefaultRedeliveryPolicy policy = new JmsDefaultRedeliveryPolicy();
    policy.setMaxRedeliveries(50);
    return policy;
}

@Bean
public JmsListenerContainerFactory topicContainerFactory(ConnectionFactory connectionFactory) {
    DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
    factory.setConnectionFactory(connectionFactory);
    factory.setSubscriptionDurable(true);
    factory.setPubSubDomain(true);
    factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
    return factory;
}

监听器:

@JmsListener(destination = "crm-customer-event/subscriptions/test-sub", containerFactory = "topicContainerFactory")
public void receiveCustomerEvent(@Payload ExecutionContextDTO dto) {
    logger.debug("Got payload: " + dto);
    throw new RuntimeException("Oops");
}

以下是我在日志中看到的内容:

邮件传输开始

[299309872:1] <- Transfer{handle=0, deliveryId=0, deliveryTag=\x97\x1c\x87\x04\xb7\xea\x86@\xb3n\xbd\x9fg\x81\x00\x11, messageFormat=0, settled=null, more=false, rcvSettleMode=null, state=null, resume=false, aborted=false, batchable=true} (16695) "\x00Sp\xc0\x0a\x05@@p.........

抛出异常,然后JMS在本地重新传递消息50次(为什么会这样做?)

我之后看到的下一个amqp协议框架是

[299309872:1] -> Disposition{role=RECEIVER, first=0, last=0, settled=true, state=Modified{deliveryFailed=true, undeliverableHere=true, messageAnnotations=null}, batchable=false}

在我看来,最后一个Disposition帧导致消息进入Deferred状态。消息上还有一个锁定令牌。即使在TTL通过时,消息仍然停留在订阅中,并且通过REST api对其没有任何帮助。我尝试解锁它(使用PUT)并删除它(使用DELETE)。我也尝试过使用REST api接收它(包括PeekLock和接收和删除变种),看起来它们似乎并不存在。我在订阅上设置了选项,可以在到期后自动将消息移动到死信队列,并且它们永远不会被移动。

使qk发生的qpid-jms的代码是here,并且看起来这部分库不是要扩展的,否则我将自己的实现返回一个不同的确认。

如何获得qpid / JMS

  1. 使用FAILED ack代替MODIFIED_FAILED_UNDELIVERABLE确认
  2. 清除那些延期消息。

1 个答案:

答案 0 :(得分:2)

首先,JMS规范正确地指出,对于从MessageListener回调中抛出的异常,它确实是一个应用程序编程错误,您的应用程序应该自己处理这些错误。

其次,客户端正在使用正确的处理方式来指示消息未能传递到此客户端,远程应支持AMQP 1.0规范中列出的所有处置,我与Microsoft联系并请求他们实现规范更加紧密。

要解决上述问题,您可以在客户端确认模式下接收消息,当您拦截正在抛出的异常时,您可以使用this JIRA Issue中描述的机制配置消息确认。

代码如下所示:

message.setIntProperty("JMS_AMQP_ACK_TYPE", 2);
message.acknowledge();

模式定义为:

ACCEPTED = 1;
REJECTED = 2;
RELEASED = 3;
MODIFIED_FAILED = 4;
MODIFIED_FAILED_UNDELIVERABLE = 5;