Apache camel 如果处理了内部路由中的异常,则不执行具有聚合策略的拆分器后的代码(Apache Camel)

Apache camel 如果处理了内部路由中的异常,则不执行具有聚合策略的拆分器后的代码(Apache Camel),apache-camel,Apache Camel,我曾经面对过我无法理解的行为。当使用AggregationStrategy执行拆分时会发生此问题,并且在其中一个迭代期间会发生异常。在另一个路由(每次迭代调用的直接端点)的拆分器内部发生异常。似乎路由执行在拆分器之后停止 下面是示例代码 这是一种为每个客户端构建一个报告并收集文件名以进行内部统计的路由 @组成部分 @所需参数构造函数 @字段默认值(级别=PRIVATE,makeFinal=true) 公共类ReportRouteBuilder扩展了RouteBuilder{ ClientRep

我曾经面对过我无法理解的行为。当使用AggregationStrategy执行拆分时会发生此问题,并且在其中一个迭代期间会发生异常。在另一个路由(每次迭代调用的直接端点)的拆分器内部发生异常。似乎路由执行在拆分器之后停止

下面是示例代码

这是一种为每个客户端构建一个报告并收集文件名以进行内部统计的路由


@组成部分
@所需参数构造函数
@字段默认值(级别=PRIVATE,makeFinal=true)
公共类ReportRouteBuilder扩展了RouteBuilder{
ClientRepository ClientRepository;
@凌驾
public void configure()引发异常{
errorHandler(deadLetterChannel(“direct:handleError”);//处理错误,将错误消息添加到内部错误收集器以进行统计并写入日志
来自(“直接:生成报告”)
.setProperty(“reportTask”,body())//此时,body中有一个reportTask类型的对象,包含生成报表所需的所有数据
.bean(clientRepository,“GetAllClient”)//主体是一个列表
.split(body())
.aggregationStrategy(新文件名ListAggregationStrategy())
.to(“direct:generateReportForClient”)//创建保存在文件系统中的报告。使用相同的错误处理程序
(完)
//如果在拆分期间发生异常,则不会执行拆分器后的代码
.log(“已完成生成报告。已创建的文件${body}”);//body必须列出文件名。
}
}
AggregationStrategy非常简单——它只提取文件名。如果缺少标头,则返回NULL


公共类FileNamesListAggregationStrategy扩展了AbstractListAggregationStrategy{
@凌驾
公共字符串getValue(Exchange){
Message inMessage=exchange.getIn();
返回inMessage.getHeader(Exchange.FILE\u名称,String.class);
}
}
当分割后一切顺利时,正文列表中有一个包含所有文件名的文件。但在路由“direct:generateReportForClient”中发生了一些异常(我为一个客户端添加了错误模拟),聚合体只包含一个文件名,这没关系(所有内容都正确聚合)

但就在剥离之后,路由执行停止,此时主体中的结果(带有文件名的列表)返回给客户端(FluentProducer),该客户端希望ReportTask作为响应主体

它尝试将值列表(聚合结果)转换为ReportTask,并导致
org.apache.camel.NoTypeConversionAvailableException:没有可从类型转换的类型转换器

为什么分裂后路线会中断?已正确处理所有错误并完成聚合

PS我已经阅读了《骆驼行动》一书和关于拆分器的文档,但我还没有找到答案

PPS项目在Spring Boot 2.3.1和Camel 3.3.0上运行

更新 此路由由FluentProducerTemplate启动

ReportTaskProcessedReportTask=producer.to(“direct:generateReports”)
.withBody(报告任务)
.request(ReportTask.class);

问题在于拆分中的错误处理程序+自定义聚合策略

从骆驼行动手册(5.3.5)中:

对拆分器使用自定义聚合策略时出现警告, 重要的是要知道你要负责处理 例外情况。如果不将异常传播回来,则拆分器 将假定您已处理该异常,并将忽略它

在代码中,使用从
AbstractListAggregationStrategy
扩展的聚合策略。让我们看看
AbstractListAggregationStrategy
中的
aggregate
方法:

@覆盖
公共交换聚合(交换旧交换、交换新交换){
名单;
if(oldExchange==null){
list=getList(newExchange);
}否则{
list=getList(oldExchange);
}
if(newExchange!=null){
V值=getValue(newExchange);
if(值!=null){
列表。添加(值);
}
}
返回oldExchange!=null?oldExchange:newExchange;
}
如果第一次交换由错误处理程序处理,我们将在结果交换(
newExchange
)中设置错误处理程序(
exchange.EXCEPTION\u捕获、exchange.FAILURE\u端点、exchange.ERRORHANDLER\u处理和exchange.FAILURE\u处理
)和
exchange.errorHandlerHandled=true
设置的属性数。方法
getErrorHandlerHandled()/setErrorHandlerHandled(Boolean errorHandlerHandled)
可在
ExtendedExchange
界面中找到

在这种情况下,您的拆分将通过交换
errorHandlerHandled=true
完成,并中断路由

原因见

如果handled为true,则将处理抛出的异常并 驼峰将不会继续按原始路线布线,但会中断

为了防止这种行为,您可以将exchange强制转换为
ExtendedExchange
,并在聚合策略
aggregate
方法中设置
errorHandlerHandled=false
。你的路线不会中断,但会继续

@覆盖
公共交换聚合(交换旧交换、交换新交换){
Exchange aggregatedExchange=super.aggregate(旧交易所、新交易所);
((ExtendedExchange)aggregatedExchange).setErrorHandlerHandled(false);
返回聚合交换;
}
棘手的情况是,如果错误处理程序将exchange处理为