【问题标题】:Apache camel using HttpEndpoint Options with http4Apache camel 使用带有 http4 的 HttpEndpoint 选项
【发布时间】:2016-08-11 19:15:34
【问题描述】:

我对骆驼比较陌生。我在实现以下功能时遇到了这个问题。

要求:调用 GET 服务,如果响应状态不是 200,则需要抛出 HttpOperationFailedException ,以便我可以在父路由上使用 onException 处理 204 异常。

我能够使用以下代码实现它:

from("direct:parent")
.onException(HttpOperationFailedException.class)
.onWhen(exchange ->{
    HttpOperationFailedException exe = exchange.getException(HttpOperationFailedException.class);
    if(204 == exe.getStatusCode()){
        return true;
    }
    return false;
})
.setBody(constant(null))
.end()
.to("direct:a");

from("direct:a")
.recipientList("false")
.simple("http4://localhost:8022/test/service?okStatusCodeRange=200-201")
.convertBodyTo(String.class);

但是使用以下代码时不会抛出异常:

from("direct:parent")
.onException(HttpOperationFailedException.class)
.onWhen(exchange ->{
    HttpOperationFailedException exe = exchange.getException(HttpOperationFailedException.class);
    if(204 == exe.getStatusCode()){
        return true;
    }
    return false;
})
.setBody(constant(null))
.end()
.to("direct:a");

from("direct:a")
.to("http4://localhost:8022/test/service?okStatusCodeRange=200-201")
.convertBodyTo(String.class);

有人能解释一下需要更改哪些内容才能使用 to 而不是收件人列表吗?

【问题讨论】:

  • 您可以尝试将 URL 添加为 Exchange.HTTP_URI 标头。根据docs,此标头将覆盖任何直接指定的 URL。这里的区别在于,标头中指定的 URL 不支持配置选项,而 .to(...) URI 将考虑它们。如果使用 HTTP_URI 标头,您还可以在 .to(...) 中使用虚拟 URL 来配置端点的安全性和相关内容,因为要调用的实际目标由 HTTP_URI 标头确定。
  • 我试过了,但是当我添加 Uri 时,调用时不支持收到类似 http4 的消息
  • 我把URI改成了http调用,但是偶数响应还是204,没有抛出HttpOperationFailedException

标签: java apache-camel


【解决方案1】:

当您将 HTTP URI 调用从 direct:parent 传播到 direct:a 但在 direct:parent 中维护嵌套异常子句时,direct:a 中引发的异常不会向上传播到父级路线。但是,您应该将嵌套异常子句重构为全局异常子句。

我创建了一个简单的测试用例,它可以模拟 204 消息上引发的异常,也可以调用真实服务并在 204 响应的情况下失败:

import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.AdviceWithRouteBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.http.common.HttpOperationFailedException;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;

import java.util.HashMap;
import java.util.Map;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;

public class Http4ExceptionHandlingTest extends CamelTestSupport {

  @Produce(uri = "direct:parent")
  protected ProducerTemplate template;

  @Override
  public boolean isUseAdviceWith() {
    return true;
  }

  @Override
  protected RouteBuilder createRouteBuilder() {
    return new RouteBuilder() {
      @Override
      public void configure() throws Exception {

        onException(HttpOperationFailedException.class)
            .onWhen(exchange -> {
              HttpOperationFailedException
                  exe = exchange.getException(HttpOperationFailedException.class);
              return 204 == exe.getStatusCode();
            })
            .log("HTTP exception handled")
            .handled(true)
            //.continued(true)
            .setBody(constant(null));

        from("direct:parent").routeId("parent")
//            .onException(HttpOperationFailedException.class)
//                .onWhen(exchange -> {
//                  HttpOperationFailedException
//                      exe = exchange.getException(HttpOperationFailedException.class);
//                  return 204 == exe.getStatusCode();
//                })
//                .setBody(constant(null))
//            .end()
            .log("Parent start");
            .to("direct:a")
            .log("Parent done");

        from("direct:a").routeId("a")
            .log("a start")
            .to("http4://localhost:8022/test/service?okStatusCodeRange=200-201")
            .convertBodyTo(String.class)
            .log("a done");
      }
    };
  }

  @Test
  public void testExceptionHandling() throws Exception {
    // comment the following line out if you want to invoke the real service instead!
    weaveRoute();

    context.start();

    Object response = template.requestBody("foo");

    assertThat(response, is(nullValue()));
  }

  @Test
  public void testSuccessfulResponse() throws Exception {
    // comment the following line out if you want to invoke the real service instead!
    weaveRoute();

    context.start();

    Object response = template.requestBody("bar");

    assertThat(response, is(equalTo("bar")));
  }

  private void weaveRoute() throws Exception {
    context.getRouteDefinition("a").adviceWith(context, new AdviceWithRouteBuilder() {
      @Override
      public void configure() throws Exception {
        this.interceptSendToEndpoint("http4*")
            .skipSendToOriginalEndpoint()
            .process(exchange -> {
              String body = exchange.getIn().getBody(String.class);
              if ("foo".equals(body)) {
                Map<String, String> headers = new HashMap<>();
                String location = "";
                HttpOperationFailedException exe =
                    new HttpOperationFailedException("http://bla", 204, "No Content", location,
                                                     headers, "response body");
                throw exe;
              }
            });
      }
    });
  }
}

我已将原始嵌套异常子句留在父路由中,以便您比较结果。全局异常处理程序获得了一个额外的 .handled(true)` 语句,它刚刚脱离了当前路由。因此,该文档声明如下:

如果handled为真,那么抛出的异常会被处理,Camel不会在原来的路由中继续路由,而是爆发。但是,您可以在 onException 中配置一个路由,该路由将被使用。如果您需要创建一些自定义响应消息返回给调用者,或者因为抛出异常而进行任何其他处理,您可以使用此路由。 (Source)

不将捕获的异常设置为.handled(true) 实际上会导致堆栈跟踪的呈现,而不是继续执行。

我添加了更多日志语句以可视化异常处理中的行为。执行上面的代码,你会得到如下输出:

[INFO ] -  - Parent start [            ] [parent] [              ] [main] 
[INFO ] -  - a start [            ] [a] [              ] [main] 
[INFO ] -  - HTTP exception handled [            ] [a] [              ] [main] 

除了.handled(true),您还可以使用.continued(true) 来继续执行记录:

如果 continue 为真,那么 Camel 将捕获异常,实际上只是忽略它并继续在原始路由中路由。但是,如果您在 onException 中配置了路由,它将首先路由该路由,然后再继续在原始路由中路由。

在全局异常子句中使用启用的.continued(true) 和禁用的.handled(true) 运行测试将产生以下日志:

[INFO ] -  - Parent start [            ] [parent] [              ] [main] 
[INFO ] -  - a start [            ] [a] [              ] [main] 
[INFO ] -  - HTTP exception handled [            ] [a] [              ] [main] 
[ERROR] -  - Failed delivery for (MessageId: ...). Exhausted after delivery attempt: 1 caught: null. Handled and continue routing.

Message History
---------------------------------------------------------------------------------------------------------------------------------------
RouteId              ProcessorId          Processor                                                                        Elapsed (ms)
[parent            ] [parent            ] [direct://parent                                                               ] [         8]
[parent            ] [log9              ] [log                                                                           ] [         0]
[parent            ] [to4               ] [direct:a                                                                      ] [         8]
[a                 ] [log7              ] [log                                                                           ] [         1]
[a                 ] [to3               ] [http4://localhost:8022/test/service?okStatusCodeRange=200-201                 ] [         8]
[                  ] [process2          ] [Processor@0x3c7f66c4                                                          ] [         8]
[a                 ] [log6              ] [log                                                                           ] [         1]
[a                 ] [setBody2          ] [setBody[{null}]                                                               ] [         0]

Stacktrace
--------------------------------------------------------------------------------------------------------------------------------------- [            ] [o.a.c.p.DefaultErrorHandler] [              ] [main] 
org.apache.camel.http.common.HttpOperationFailedException: HTTP operation failed invoking http://bla with statusCode: 204, redirectLocation: 
    at at.erpel.messaginghub.services.unit.routes.rest.Http4ExceptionHandlingTest$2.lambda$configure$1(Http4ExceptionHandlingTest.java:103)
    at org.apache.camel.processor.DelegateSyncProcessor.process(DelegateSyncProcessor.java:63)
    ...
[INFO ] -  - a done [            ] [a] [              ] [main] 
[INFO ] -  - Parent done [            ] [parent] [              ] [main] 

从简化日志中可以看出,.continued(true) 不会跳出路由,但会记录消息历史记录以及被忽略异常的堆栈跟踪。

如果父路由中存在嵌套异常子句,而子路由确实遇到异常,则在嵌套异常子句中添加 .handled(true).continuted(true) 无效,因为子路由中捕获的实际异常是没有传播到父嵌套异常子句,因此根本没有处理。

我已经针对模拟和真实服务测试了代码示例,该服务在接收到foo 正文时返回204 响应,在接收到其他任何内容时返回200 响应。因此,?okStatusCodeRange=200-201 配置参数在我的情况下按预期工作。为了完整性:我使用的是 Camel 2.17.0。

【讨论】:

  • 感谢您详细解释,我曾尝试在我的路线中重复使用相同的内容。但是对我来说,如果服务返回 204,则在使用 .to() 时不会抛出 httpOperationFailureException。但是在使用 .recipentList() 时同样有效。还可以看到,在控制台中使用recipentList 时,会显示一条消息,例如配置属性:okStatusCodeRange。但是,使用 .to() 时不显示。我正在使用骆驼 2.17.2。这会导致这个问题吗?
  • 测试 Camel 2.17.2 是否存在问题的最简单方法是暂时退回到 2.17.0,因为这对我有用,如答案所示。您是否尝试过运行测试?测试对你有用吗?如果模拟版本为绿色,您可以在测试中注释掉weaveRoute() 并再次运行测试。然而这一次,应该调用真正的服务而不是使用模拟。如果这也有效,那么问题可能出在其他地方,可能在您未包含在简化场景中的其他一些东西上。
  • 我尝试使用weaveRoute() 运行测试用例并对其进行评论。测试用例工作正常。因此我相信它不是骆驼 2.17.2 中的错误。就像您提到的那样,在实际代码中,我使用 .doCatch(exception) 调用了 .doTry() 内的父路由,这可能是导致问题的原因。将尝试一些重构。非常感谢@Roman Vottner
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-31
  • 1970-01-01
  • 2017-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多