本篇以实际例子来介绍 camel 的异常处理
抛异常 在路由运行期间使用 throwException 方法直接抛异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class NewExceptionTest extends CamelTestSupport { @Override protected RouteBuilder createRouteBuilder () throws Exception { return new RouteBuilder () { @Override public void configure () throws Exception { from("direct:start" ) .throwException(new RuntimeException ("Forbidden" )) .end() .to("mock:done" ); } }; } @Test public void testNoError () throws Exception { getMockEndpoint("mock:done" ).expectedMessageCount(1 ); template.sendBodyAndHeader("direct:start" , "Hello Camel" , "name" , "Camel" ); assertMockEndpointsSatisfied(); } }
运行日志结果
1 2 3 4 5 6 7 8 org.apache.camel.CamelExecutionException: Exception occurred during execution on the exchange: Exchange[ID-MacBook-Pro-local-1649419373115-0-1] at org.apache.camel.util.ObjectHelper.wrapCamelExecutionException(ObjectHelper.java:1842) at org.apache.camel.util.ExchangeHelper.extractResultBody(ExchangeHelper.java:745) at org.apache.camel.impl.DefaultProducerTemplate.extractResultBody(DefaultProducerTemplate.java:515) at org.apache.camel.impl.DefaultProducerTemplate.extractResultBody(DefaultProducerTemplate.java:511) at org.apache.camel.impl.DefaultProducerTemplate.sendBodyAndHeader(DefaultProducerTemplate.java:189) at org.apache.camel.impl.DefaultProducerTemplate.sendBodyAndHeader(DefaultProducerTemplate.java:183)
捕获并处理异常 这里分为 3 个步骤
使用 onException 方法捕获异常,其类似 try catch 的 catch 动作
使用 handled 方法将异常标记为已处理
自定义处理异常,处理时,可以在 exchange 中使用Exchange.EXCEPTION_CAUGHT
属性字段捕获当前的错误异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Override protected RouteBuilder createRouteBuilder () throws Exception { return new RouteBuilder () { @Override public void configure () throws Exception { onException(Exception.class) .handled(true ) .process((exchange -> { Throwable caused = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Throwable.class); log.error("error:" +caused.getMessage()); })); from("direct:start" ) .throwException(new RuntimeException ("Forbidden" )) .end() .to("mock:done" ); } }; }
日志输出
1 2 2022-04-08 20:20:46,176 [main ] ERROR NewExceptionTest2$1 - error:Forbidden 2022-04-08 20:20:46,177 [main ] INFO MockEndpoint - Asserting: mock://done is satisfied
捕获异常并继续 有些场景,异常捕获之后并不影响正常流程,路由的后续流程可以继续运行,使用continued方法
设置为 true,表示忽略该异常并继续运行,实际上有点类似该异常并没有发生,如下代码示例
1 2 3 4 5 6 onException(IllegalArgumentException.class).continued(true ); from("direct:start" ) .to("mock:start" ) .throwException(new IllegalArgumentException ("Forced" )) .to("mock:result" );
异常返回 当异常被捕获并处理完以后,但由于并没有返回结果,原路由依然在等待结果,所以可以在异常处理完毕之后,使用 to 方法,返回自定义的响应,这样该路由整体流程是完整的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Override protected RouteBuilder createRouteBuilder () throws Exception { return new RouteBuilder () { @Override public void configure () throws Exception { onException(Exception.class) .handled(true ) .process((exchange -> { Throwable caused = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Throwable.class); log.error("error:" +caused.getMessage()); })).to("mock:done" ); from("direct:start" ) .throwException(new RuntimeException ("Forbidden" )) .end() .to("mock:done" ); } }; }
处理和继续有什么区别?
如果 handled 为真,那么抛出的异常会被处理,Camel 不会继续在原来的路由中路由,而是抛异常。但是,您可以在其中配置将使用的路由 onException。如果您需要创建一些自定义响应消息返回给调用者,或者因为抛出异常而进行任何其他处理,您可以使用此路由。
如果 continue 为真,那么 Camel 将捕获异常,实际上只是忽略它并继续在原始路由中路由。但是,如果您在其中配置了 onException 路由,它将首先路由该路由,然后再继续在原始路由中路由。
失败重试 可以在捕获异常后采取失败重试策略,maximumRedeliveries 表示要重试的次数,如果是 2,那么该方法一直失败的话,则是一共执行 3 次,即一共执行 maximumRedeliveries+1 次,如下代码清单示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 protected RouteBuilder createRouteBuilder () throws Exception { return new RouteBuilder () { @Override public void configure () throws Exception { onException(Exception.class) .maximumRedeliveries(2 ); from("direct:start" ) .process((exchange -> { index++; log.info("doProcess:" +index); if (index<3 ) { throw new RuntimeException ("Forbidden" ); } log.info("doProcess end" ); })) .end() .to("mock:done" ); } }; }
日志输出结果
1 2 3 4 2022-04-08 20:40:36,303 [main ] INFO NewExceptionTest3$1 - doProcess:1 2022-04-08 20:40:37,316 [main ] INFO NewExceptionTest3$1 - doProcess:2 2022-04-08 20:40:38,324 [main ] INFO NewExceptionTest3$1 - doProcess:3 2022-04-08 20:40:38,324 [main ] INFO NewExceptionTest3$1 - doProcess end
失败延迟重试 通过设置 redeliveryDelay 方法,支持延迟重试,比如网络不好的场景,不需要太频繁的重试,在上面例子基础上添加 redeliveryDelay 方法,设置为每 5 秒重试一次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 protected RouteBuilder createRouteBuilder () throws Exception { return new RouteBuilder () { @Override public void configure () throws Exception { onException(Exception.class) .maximumRedeliveries(2 ) .redeliveryDelay(5000 ); from("direct:start" ) .process((exchange -> { index++; log.info("doProcess:" +index); if (index<3 ) { throw new RuntimeException ("Forbidden" ); } log.info("doProcess end" ); })) .end() .to("mock:done" ); } }; }
日志输出结果
1 2 3 4 2022-04-08 20:49:54,816 [main ] INFO NewExceptionTest3$1 - doProcess:1 2022-04-08 20:49:59,830 [main ] INFO NewExceptionTest3$1 - doProcess:2 2022-04-08 20:50:04,835 [main ] INFO NewExceptionTest3$1 - doProcess:3 2022-04-08 20:50:04,835 [main ] INFO NewExceptionTest3$1 - doProcess end
重试终止条件 retryWhile 方法可以传入一个谓语,即满足该条件以后才会发起重试,当返回 false,重试将会终止如下示例,将重试次数放在 retryWhile 中检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 protected RouteBuilder createRouteBuilder () throws Exception { return new RouteBuilder () { @Override public void configure () throws Exception { onException(Exception.class) .maximumRedeliveries(5 ) .retryWhile((exchange -> { if (index>2 ) { return false ; } return true ; })); from("direct:start" ) .process((exchange -> { index++; log.info("doProcess:" +index); throw new RuntimeException ("Forbidden" ); })) .end() .to("mock:done" ); } }; }
TRY … CATCH … FINALLY
以传统 TRY … CATCH … FINALLY 的方式捕获异常
1 2 3 4 5 6 7 8 9 from("direct:start" ) .doTry() .process(new ProcessorFail ()) .to("mock:result" ) .doCatch(IOException.class, IllegalStateException.class) .to("mock:catch" ) .doFinally() .to("mock:finally" ) .end();
全局异常捕获 使用 errorHandler 方法设置全局异常
1 2 3 4 errorHandler(defaultErrorHandler() .maximumRedeliveries(3 ) .redeliveryDelay(5000 ) .onExceptionOccurred(myProcessor));
参考: