Apache Camel 4.3.0与4.9.0异常重试行为差异及兼容方案咨询
我正在进行基于Spring Boot 3.4.0、Java 23的Apache Camel路由迁移项目,使用全局onException和路由级doTry/doCatch异常处理器。发现Camel 4.3.0与4.9.0的异常重试行为存在差异:
- 在4.3.0中,未被路由级处理器捕获的
ClassCastException会触发全局处理器并完成自动重试; - 在4.9.0中,全局
onException处理器未触发,无法捕获该异常。
查阅官方文档得知,存在路由级doTry/doCatch处理器时全局onException不再触发。现咨询:
- 该行为是否在Camel 4.3.0至4.9.0版本间发生变更?
- 在Camel 4.9.0中如何通过原生机制实现自动重试,避免手动迭代?
测试代码
package com.apache.camel.test_exception; import org.apache.camel.LoggingLevel; import org.apache.camel.builder.RouteBuilder; import org.springframework.stereotype.Component; @Component public class ExceptionTest extends RouteBuilder { @Override public void configure() throws Exception { onException(Exception.class) .handled(true) .log("Caught exception") .maximumRedeliveries(3) .redeliveryDelay(3000) .retryAttemptedLogLevel(LoggingLevel.WARN) .retriesExhaustedLogLevel(LoggingLevel.WARN) .log("Exception Handled") .end(); restConfiguration().component("servlet").scheme("http").port(8080); rest("/service/api/v1") .get("/testException") .routeId("rest-exception") .to("direct:exceptionRoute"); from("direct:exceptionRoute") .routeId("exception-route") .log("Received request to test exception") .doTry() .throwException(new ClassCastException()) .doCatch(NullPointerException.class) .log("NullPointer Exception Caught") .endDoTry() .log("Route ended") .end(); } }
Camel 4.3.0日志
2024-12-14T10:52:33.699+05:30 INFO 16524 --- [test-exception] [nio-8080-exec-1] exception-route : Received request to test exception
2024-12-14T10:52:33.704+05:30 WARN 16524 --- [test-exception] [nio-8080-exec-1] o.a.c.p.e.DefaultErrorHandler : Failed delivery for (MessageId: A1B32AE5C117F9F-0000000000000000 on ExchangeId: A1B32AE5C117F9F-0000000000000000). On delivery attempt: 0 caught: java.lang.ClassCastException
2024-12-14T10:52:36.720+05:30 INFO 16524 --- [test-exception] [nio-8080-exec-1] exception-route : Received request to test exception
2024-12-14T10:52:36.720+05:30 WARN 16524 --- [test-exception] [nio-8080-exec-1] o.a.c.p.e.DefaultErrorHandler : Failed delivery for (MessageId: A1B32AE5C117F9F-0000000000000000 on ExchangeId: A1B32AE5C117F9F-0000000000000000). On delivery attempt: 1 caught: java.lang.ClassCastException
2024-12-14T10:52:39.731+05:30 INFO 16524 --- [test-exception] [nio-8080-exec-1] exception-route : Received request to test exception
2024-12-14T10:52:39.731+05:30 WARN 16524 --- [test-exception] [nio-8080-exec-1] o.a.c.p.e.DefaultErrorHandler : Failed delivery for (MessageId: A1B32AE5C117F9F-0000000000000000 on ExchangeId: A1B32AE5C117F9F-0000000000000000). On delivery attempt: 2 caught: java.lang.ClassCastException
2024-12-14T10:52:42.734+05:30 INFO 16524 --- [test-exception] [nio-8080-exec-1] exception-route : Received request to test exception
2024-12-14T10:52:42.735+05:30 WARN 16524 --- [test-exception] [nio-8080-exec-1] o.a.c.p.e.DefaultErrorHandler : Failed delivery for (MessageId: A1B32AE5C117F9F-0000000000000000 on ExchangeId: A1B32AE5C117F9F-0000000000000000). On delivery attempt: 3 caught: java.lang.ClassCastException
2024-12-14T10:52:42.741+05:30 INFO 16524 --- [test-exception] [nio-8080-exec-1] rest-exception : Caught exception
2024-12-14T10:52:42.741+05:30 INFO 16524 --- [test-exception] [nio-8080-exec-1] rest-exception : Exception Handled
Camel 4.9.0日志
2024-12-14T11:16:55.864+05:30 INFO 20908 --- [test-exception] [ main] c.a.c.t.TestExceptionApplication : Started TestExceptionApplication in 3.152 seconds (process running for 3.61)
2024-12-14T11:19:32.081+05:30 INFO 20908 --- [test-exception] [nio-8080-exec-1] exception-route : Received request to test exception
问题解答
1. 版本间行为变更确认
是的,该行为是Camel 4.3.0到4.9.0之间的有意变更。在早期版本中,doTry/doCatch未捕获的异常会冒泡到全局onException处理器,但从Camel 4.4.x版本开始,官方调整了异常处理优先级:当路由中存在doTry/doCatch块时,未被捕获的异常不再触发全局onException,而是由路由的默认错误处理器(如果未禁用)处理,或者直接抛出未捕获异常。这一变更旨在明确异常处理的边界,避免全局处理器意外拦截局部未处理的异常。
2. Camel 4.9.0中实现自动重试的原生方案
针对你的场景,有三种原生方式可以实现自动重试:
方案一:在doTry块内配置局部重试规则
直接在doTry块内部添加错误处理器配置,针对块内步骤触发重试:
from("direct:exceptionRoute") .routeId("exception-route") .log("Received request to test exception") .doTry() .errorHandler(defaultErrorHandler() .maximumRedeliveries(3) .redeliveryDelay(3000) .retryAttemptedLogLevel(LoggingLevel.WARN)) .throwException(new ClassCastException()) .doCatch(NullPointerException.class) .log("NullPointer Exception Caught") .doCatch(ClassCastException.class) .log("ClassCast Exception Caught after retries") .endDoTry() .log("Route ended") .end();
方案二:在路由中复用全局异常配置
将全局异常处理逻辑显式应用到目标路由,覆盖doTry的默认隔离行为:
// 定义全局异常模板 OnExceptionDefinition globalException = onException(Exception.class) .handled(true) .log("Caught exception") .maximumRedeliveries(3) .redeliveryDelay(3000) .retryAttemptedLogLevel(LoggingLevel.WARN) .retriesExhaustedLogLevel(LoggingLevel.WARN) .log("Exception Handled"); // 在路由中应用全局异常配置 from("direct:exceptionRoute") .routeId("exception-route") .log("Received request to test exception") .onException(Exception.class).inheritErrorHandler(globalException).end() .doTry() .throwException(new ClassCastException()) .doCatch(NullPointerException.class) .log("NullPointer Exception Caught") .endDoTry() .log("Route ended") .end();
方案三:禁用doTry的错误处理器隔离
通过inheritErrorHandler(false)取消doTry块的错误处理器隔离,让未捕获异常冒泡到路由级错误处理器,进而触发全局onException:
from("direct:exceptionRoute") .routeId("exception-route") .log("Received request to test exception") .doTry().inheritErrorHandler(false) .throwException(new ClassCastException()) .doCatch(NullPointerException.class) .log("NullPointer Exception Caught") .endDoTry() .log("Route ended") .end();
注意:此方式需确保路由的默认错误处理器未被禁用,且全局onException配置符合预期。
内容的提问来源于stack exchange,提问作者anandrnair89

