Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/373.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Apache Camel with RabbitMQ:当端点配置上的autoAck=false时,临时回复队列中的消息未被确认_Java_Rabbitmq_Apache Camel - Fatal编程技术网

Java Apache Camel with RabbitMQ:当端点配置上的autoAck=false时,临时回复队列中的消息未被确认

Java Apache Camel with RabbitMQ:当端点配置上的autoAck=false时,临时回复队列中的消息未被确认,java,rabbitmq,apache-camel,Java,Rabbitmq,Apache Camel,在使用Camel-rabbitmq扩展在Camel中配置支持InOut的路由时,我注意到了一个问题。 当我将主队列配置设置为autoAck=false时,同样的配置也会被复制到临时应答队列(它甚至使用相同的预取(5)设置,在RabbitMQ控制台中很容易看到)。这会导致临时队列中的消息无限期地驻留在那里,直到服务器重新启动 Virtual host Name Features State Ready Unacked Total incoming

在使用Camel-rabbitmq扩展在Camel中配置支持InOut的路由时,我注意到了一个问题。 当我将主队列配置设置为autoAck=false时,同样的配置也会被复制到临时应答队列(它甚至使用相同的预取(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
即使在日志中,我可以清楚地看到收到了回复消息,只是ack似乎没有发送到RabbitMQ以从临时队列中清除消息。我在控制台中检查了两个临时队列上都有消费者,所以我希望骆驼发送ack

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-capable路由的同时防止出现这种情况? 也许我应该在这里提到,没有错误或类似的问题,流程按预期工作,电子邮件处理工作完美,唯一的问题是临时队列上的过时消息

我们的Camel版本是2.20.2 这是我们所有Camel组件的相关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_头
头添加到Camel交换并验证有效负载

EmailRouteBuilder
如下所示:

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

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

            });

}
其中,
super.configure()
调用配置异常处理和死信、启动顺序、重试次数、最大重新交付次数等。这里的代码相当多,但如果您认为其中的某些内容可能是导致此问题的原因,我将发布它。此外,如果您需要我添加任何其他配置,请让我知道

从上面可以清楚地看出,为什么我们需要一个
InOut
路由,其中
autoAck=false
,因为从业务角度来看,丢失电子邮件是不好的,而其余的客户端需要一个基于
EmailProcessor
的响应。如何清除临时队列中的过时消息

编辑 实际上,只有在预取计数耗尽之前,路由才能正常工作,之后它开始抛出异常,REST客户机返回HTTP 500响应

org.apache.camel.ExchangeTimedOutException: The OUT message was not received within: 20000 millis due reply message with correlationID: Camel-ID-VMSYS119-1534407032085-0-284 not received on destination: amq.gen-eUU7BRI3Ooo4F8Me7HrPnA.

根据评论,这是camel-rabbitmq组件中的一个bug,现在已经对主分支应用了修复

吉拉:这里是机票:

该修复程序将在2.21.3、2.22.1、2.23.0及更高版本中提供

编辑:

包括答案中的代码更改

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

更改此选项:

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

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

我想我可能一路上迷路了,但是你能提供详细信息说明为什么你希望回复信息上有确认信息吗?从表面上看,这似乎是不必要的。此外,使用RMQ的回复队列模式,队列通常在消费者断开连接后自动删除。因此,虽然您不必这样做,但至少在本例中,该模式似乎已被打破。嗨,theMayer,感谢您花时间穿越文本和代码之墙!是的,我认为回复队列的实现方式有问题。如果主队列
emailrouteQueue
的autoAck设置为false(以防止丢失消息),则会使用相同的autoAck=false属性创建回复队列,因此回复消息需要ack,否则它们会继续堆积在回复队列中(直到消费者重新启动).这被认为是camel rabbitmq组件JIRA ticket link的一个缺陷:我不想这么说。。。但我认为你应该考虑离开骆驼。这是一个相当大的问题。为什么不自己滚呢?RPC代码非常容易使用。