带有RabbitMQ的Apache Camel:在端点配置上,当autoAck = false时,临时应答队列中的消息不会被确认

时间:2018-08-16 11:10:56

标签: java rabbitmq apache-camel

在骆驼中使用骆驼-rabbitmq扩展名配置具有InOut功能的路由时,我注意到了一个问题。 当我将主队列配置设置为autoAck = false时,还将为临时答复队列复制相同的配置(它甚至使用相同的prefetch(5)设置,在RabbitMQ控制台中很容易看到)。这将导致临时队列中的消息无限期地坐在那里,直到服务器重新启动。

Virtual host Name                         Features  State Ready Unacked Total incoming deliver / get ack
/test      amq.gen-Hkdx9mckIfMc6JhDI6d-JA AD Excl   idle    2   5   7   0.00/s 0.00/s   0.00/s
/test      amq.gen-eUU7BRI3Ooo4F8Me7HrPnA AD Excl   idle    2   5   7   0.00/s 0.00/s   0.00/s

即使在日志中,我也可以清楚地看到收到了回复消息,只是没有将确认发送给RabbitMQ来清除临时队列中的消息。而且我已经在控制台中检查到两个临时队列上都有消费者,因此我希望骆驼发送确认。

o.a.c.c.r.RabbitMQMessagePublisher  - Sending message to exchange: emailfeedbackExchange with CorrelationId = Camel-ID-VMS-1534332570964-0-11
o.a.c.c.r.r.ReplyManagerSupport  - Received reply message with correlationID [Camel-ID-VMS-1534332570964-0-11]

问题是,如何在保持autoAck = false和具有InOut功能的路由的同时防止这种情况发生? 也许我应该在这里提到没有错误之类的东西,流程按预期工作并且电子邮件处理正常,唯一的问题是临时队列中的过期消息。

我们的骆驼版本是2.20.2 这是我们拥有的所有骆驼组件的相关Gradle配置:

compile ("org.apache.camel:camel-spring-boot-starter:${camelVersion}")
compile ("org.apache.camel:camel-rabbitmq:${camelVersion}")
compile ("org.apache.camel:camel-amqp:${camelVersion}")

队列和路由配置:

restentrypointroute:
    restEndpoint: /app
    postEndpoint: /email
    outputEmailEndpoint: rabbitmq://vms:5672/emailExchange?connectionFactory=rabbitConnectionFactory&autoDelete=false&queue=emailrouteQueue&exchangeType=topic&autoAck=false&bridgeEndpoint=true&concurrentConsumers=3&threadPoolSize=3&channelPoolMaxSize=3&prefetchCount=5&prefetchEnabled=true

emailroutebuilder:
    serviceName: emailroutebuilder
    inputEndpoint: rabbitmq://vms:5672/emailExchange?connectionFactory=rabbitConnectionFactory&autoDelete=false&queue=emailrouteQueue&exchangeType=topic&autoAck=false&bridgeEndpoint=true&concurrentConsumers=3&threadPoolSize=3&channelPoolMaxSize=3&prefetchCount=5&prefetchEnabled=true
    emailProcessor: bean:emailProcessor
    maximumRedeliveries: 5
    redeliveryDelay: 30000

以下是RestRouteBuilder实现中的相关内容:

@Override
public void configure() throws Exception {

restConfiguration().component("restlet").bindingMode(RestBindingMode.json);

    rest(restEndpoint).post(postEndpoint)
      .type(MyRequest.class)
      .route()
      .startupOrder(Integer.MAX_VALUE - 2)
      .process(this::process)
      .choice()
      .when(header(DELIVERYSTATUS_HEADER)
          .isEqualTo(Status.GENERATED)).to(outputEmailEndpoint)
      .when(header(DELIVERYSTATUS_HEADER)
          .isEqualTo(Status.COMPLETED)).to(outputEmailEndpoint, outputArchiveEndpoint).end()
      .endRest();

process()方法将DELIVERYSTATUS_HEADER头添加到Camel交换并验证有效负载。

EmailRouteBuilder看起来像这样:

public void configure() throws Exception {
    super.configure();

    from("direct:" + getServiceName())
            .to(emailProcessor)
            .process(ex -> {
                ex.setOut(ex.getIn());

            });

}

super.configure()调用中配置异常处理和死信,启动顺序,重试计数,最大重新传递等。这里有很多代码,但是如果您认为其中的某些原因可能是这个问题我会发布。另外,如果您需要我添加其他配置,请告诉我。

从上面可以很清楚地知道为什么我们需要使用InOut的{​​{1}}路由,因为从业务的角度来看,丢失电子邮件是很糟糕的,而REST客户端需要基于{{1 }}。只是如何摆脱临时队列中的过时消息?

编辑 实际上,该路由只有在预取计数用完之前才能正常工作,然后开始引发异常,并且REST客户端将HTTP 500响应返回。

autoAck=false

1 个答案:

答案 0 :(得分:1)

根据评论,事实证明这是camel-rabbitmq组件中的错误,并且现已对master分支进行了修复。

这里的吉拉票:https://issues.apache.org/jira/browse/CAMEL-12746

此修复程序将在2.21.3、2.22.1、2.23.0及更高版本中可用。

编辑:

在答案中包含代码更改。

TemporaryQueueReplyManager第139行-始终以true的自动确认模式启动临时队列的使用者。

更改此内容

private void start() throws IOException {         
    tag = channel.basicConsume(getReplyTo(), endpoint.isAutoAck(), this);     
}

对此:

private void start() throws IOException {
     tag = channel.basicConsume(getReplyTo(), true, this);
 }