【问题标题】:Global per-block error handling in a Dataflow pipelineDataflow 管道中的全局每块错误处理
【发布时间】:2015-11-04 13:11:05
【问题描述】:

我正在设计一个由多个块组成的长期运行的 Dataflow 管道。项目被馈送到管道的输入块,最终通过它,并在最后显示在 UI 中(作为对用户的礼貌——管道的真正工作是将处理结果保存到磁盘)。

管道块中的 lambda 函数可能会抛出异常,原因有很多(输入错误、网络故障、计算过程中的错误等等)。在这种情况下,我不想让整个管道出错,而是踢出有问题的项目,并将其显示在 UI 中的“错误”下。

最好的方法是什么?我知道我可以将每个 lambda 函数包装在 try/catch 中:

var errorLoggingBlock = new ActionBlock<Tuple<WorkItem, Exception>>(...)

var workerBlock = new TransformBlock<WorkItem, WorkItem>(item => 
{
    try {
        return DoStuff(item);
    } catch (Exception ex) {
        errorLoggingBlock.SendAsync(Tuple.Create(item, ex));
        return null;
    }
}

但我在管道中大约有 10 个块,将代码复制/粘贴到每个块中似乎很愚蠢。另外,我不喜欢返回 null 的想法,因为现在所有下游块都必须检查它。

我的下一个最佳想法是创建一个函数,该函数返回一个为我进行包装的 lambda:

  private Func<TArg, TResult> HandleErrors<TArg, TResult>(Func<TArg, TResult> f) where TArg:WorkItem
  {
     return arg =>
     {
        try {
           return f(arg);
        } catch (Exception ex) {
           errorLoggingBlock.SendAsync(Tuple.Create(item, ex));
           return default(TResult);
        }
     };
  }

但这似乎有点太元了。有没有更好的办法?

【问题讨论】:

  • 你可以看看 Stephen Cleary 的简约 Try 库。它允许通过管道的所有块传递消息,然后观察该消息最后发生的任何异常。

标签: c# error-handling tpl-dataflow


【解决方案1】:

这是一个非常有趣的话题。

您可以在链接块时定义过滤器,这意味着您可以将错误结果转移到错误处理块。为此,块应返回包含其处理结果和至少一个失败/成功指示器的“元”对象。

Railroad Oriented Programming 更好地描述了这个想法,其中链中的每个函数处理成功的结果或将失败的结果转移到“失败的轨道”以进行最终记录。

实际上,这意味着您应该在每个块之后添加两个链接:一个具有转移到错误处理块的过滤条件,另一个是进入流程下一步的默认链接。

您甚至可以结合这两种想法来处理部分故障。部分失败结果将包含失败指示符和有效负载。您可以将结果转移到日志记录块,然后再将其传递到下一步。

我发现明确每个消息的状态要容易得多,而不是尝试通过检查空值、缺失值等来确定其状态。这意味着块应该包装它们的结果在包含状态标志、结果和/或任何错误的“信封”对象中。

【讨论】:

  • 谢谢,我想我理解这个概念,但这并不能回答我的问题。每当我的所有块的处理函数抛出错误时,我如何自动确保它们发出错误信号项?正如我在问题中所说,我可以手动将它们全部包装在 try/catch 中,但必须有更好的方法......
猜你喜欢
  • 1970-01-01
  • 2020-03-10
  • 1970-01-01
  • 2011-12-14
  • 2016-04-08
  • 1970-01-01
  • 1970-01-01
  • 2015-08-26
相关资源
最近更新 更多