骆驼消息重新发送不符合预期

时间:2014-03-28 17:03:30

标签: apache-camel

我在Camel中有一个路由,我想在发生异常时重试,但是我想设置一个属性,以便路由可以在第二次尝试稍微不同时尝试在重试时再次发生错误。这是一条路线,说明了我现在正在尝试的想法。

from("direct:onExceptionTest")
    .onException(Exception.class)
        .maximumRedeliveries(1)
        .log("Retrying")
        .setProperty("retrying", constant(true))
    .end()
    .log("Start")   
    .choice()
        .when(property("retrying").isNull())
            .log("Throwing")
            .throwException(new Exception("Hello world"))
        .end()
    .end()
    .log("Done")

显然这不是真正的路线;整个choice主体只是在某些情况下模拟我的组件错误。我希望看到以下消息记录:

Start
Throwing
Retrying
Start
Done

但我实际看到的是:

Start
Throwing
Retrying
Failed delivery for (MessageId: ... on ExchangeId: ...). Exhausted after delivery attempt: 2 caught: java.lang.Exception: Hello world. Processed by failure processor: FatalFallbackErrorHandler[Pipeline[[Channel[Log(onExceptionTest)[Retrying]], Channel[setProperty(retrying, true)]]]]

我尝试将handled(true)添加到异常处理程序中,但所有这一切都会抑制错误消息。我没有看到第二个“开始”或“完成”日志消息。

为什么我的路线不像我期望的那样行事,我需要做些什么来让它按照我想要的方式行事呢?

更新

@ProgrammerDan指出问题在于重新发送不是为了我想要实现的目标,这可以解释为什么我的路线不起作用!所以我需要在我的处理程序中完成工作,但是我的路由调用了一个Web服务并且还有其他一些步骤,我不想在处理程序中复制所有这些。我想出了这个,它按预期工作,但它涉及从一开始就再次调用自己的路线。这是一个坏主意吗?我会用这种方法陷入困境吗?

from("direct:onExceptionTest")
    .onException(Exception.class)
        .onWhen(property("retrying").isNull()) // don't retry forever
        .log("Retrying")
        .setProperty("retrying", constant(true))
        .handled(true)
        .to("direct:onExceptionTest") // is recursion bad?
    .end()
    .log("Start")   
    .choice()
        .when(property("retrying").isNull())
            .log("Throwing")
            .throwException(new Exception("Hello world"))
        .end()
    .end()
    .log("Done")

2 个答案:

答案 0 :(得分:3)

使用onRedeliveryProcessor设置属性:

String KEY = "retrying";

from("direct:onExceptionTest")
     .onException(RuntimeException.class)
         .onRedelivery(new Processor() { // Sets a processor that should be processed before a redelivery attempt.
             @Override
             public void process(final Exchange exchange) throws Exception {
                 LOG.info("Retrying");
                 exchange.setProperty(KEY, true);
             }
        })
        .maximumRedeliveries(1)
        .handled(true)
    .end()
    .log("Start")
    .process(new Processor() {
        @Override
        public void process(final Exchange exchange) throws Exception {
            LOG.info("No problem");
        }
    })
    .process(new Processor() {
        @Override
        public void process(final Exchange exchange) throws Exception {
            if (exchange.getProperty(KEY) == null) {
                LOG.info("Throwing");
                throw new RuntimeException("Hello World");
            }
            else {
                LOG.info("No throwing");
            }
        }
    })
    .log("Done");

打印

[                          main] route1                         INFO  Start
[                          main] OnExceptionHandler             INFO  No problem
[                          main] OnExceptionHandler             INFO  Throwing
[                          main] OnExceptionHandler             INFO  Retrying
[                          main] OnExceptionHandler             INFO  No throwing
[                          main] route1                         INFO  Done

正如@ProgrammerDan所指出的那样,只有失败的处理器被重新执行,而不是第一个没有任何问题的处理器。

修改

如果必须重新执行所有处理,那么您可以使用doTrydoCatch的子路线,如下所示:

from("direct:onExceptionTest")
    .doTry()
        .to("direct:subroute")
    .doCatch(RuntimeException.class)
        .setProperty(KEY, constant(true))
        .to("direct:subroute")
    .end()
    .log("Done");

from("direct:subroute")
    .log("Start")
    .process(new Processor() {
        @Override
        public void process(final Exchange exchange) throws Exception {
            LOG.info("No problem");
        }
    })
    .process(new Processor() {
        @Override
        public void process(final Exchange exchange) throws Exception {
            if (exchange.getProperty(KEY) == null) {
                LOG.info("Throwing");
                throw new RuntimeException("Hello World");
            }
            else {
                LOG.info("No throwing");
            }
        }
    });

来自Camel Docs

  

使用doTry .. doCatch .. doFinally时,常规的Camel错误处理程序不适用。这意味着任何onException或类似的东西都不会触发。原因是doTry .. doCatch .. doFinally实际上是它自己的错误处理程序,它的目的是模仿和工作,就像try / catch / finally如何在Java中工作一样。 / p>

答案 1 :(得分:2)

关于骆驼的重新传递机制,需要考虑几点。首先,查看the docs on the topic,这可能会挑战您对Camel如何处理重新传递的假设。我已经联系到的一点是,Camel尝试在失败时重新启动,它不会从路径的开头重新开始(正如您所假设的那样)。如果我正确理解文档(我有一段时间没有尝试过这种模式)你基本上是在告诉它多次重试抛出一个异常,我怀疑你想要测试它。

其次,我建议直接在onException()处理器链中进行备用处理,如演示a little further down in the same docs。基本上,您可以指定通过自定义处理器处理邮件的方式,并使用handled(true)stop()表示无需进一步处理。

总而言之,重新传递通常意味着处理典型的端点传输故障,例如间歇性连接丢失,接收服务器瞬间不可用等等,最有意义的是“再试一次”并对合理的成功期望。如果您需要更复杂的逻辑来处理重试,请在onException()处理器链中使用自定义处理器或一系列处理器。