Apache flink 在ApacheFlink中处理有毒消息

Apache flink 在ApacheFlink中处理有毒消息,apache-flink,Apache Flink,我正试图找出用ApacheFlink处理有毒消息/未处理异常的最佳实践。我们的工作是对物联网设备的位置数据进行实时事件处理。可能出现以下两种情况: 数据在某些方面不正确-例如无效值 由于一些我们没有预料到的边缘情况,数据触发了一个bug 目前,由于只有一条消息,我所有的数据处理都停止了 我看到了两个建议: 捕获异常-这需要我用捕获每个运行时异常的东西包装每个逻辑片段 使用边输出作为一种DLQ——从我所能看出,这似乎是#1的一种变体,我必须捕获所有异常并将它们发送到边输出 除了用异常处理来包装每一

我正试图找出用ApacheFlink处理有毒消息/未处理异常的最佳实践。我们的工作是对物联网设备的位置数据进行实时事件处理。可能出现以下两种情况:

  • 数据在某些方面不正确-例如无效值
  • 由于一些我们没有预料到的边缘情况,数据触发了一个bug
  • 目前,由于只有一条消息,我所有的数据处理都停止了

    我看到了两个建议:

  • 捕获异常-这需要我用捕获每个运行时异常的东西包装每个逻辑片段
  • 使用边输出作为一种DLQ——从我所能看出,这似乎是#1的一种变体,我必须捕获所有异常并将它们发送到边输出

  • 除了用异常处理来包装每一条逻辑之外,真的没有其他方法可以做到这一点吗?是否没有通用的方法来捕获异常而不让处理继续?

    我认为我们的想法不是捕获所有类型的异常并将它们发送到其他地方,而是使用经过良好测试且功能正常的代码,并仅对无效输入使用死信

    因此,典型的管道是

    source => validate => ... => sink
                      \=> dead letter queue
    
    一旦记录通过validate运算符,您就希望所有错误都冒出来,因为这些运算符中的任何错误都可能导致损坏的聚合和数据,这些数据一旦写入就无法轻松恢复

    验证步骤可以使用您概述的两种方法中的任何一种。通常,边输出具有更好的语义,但最终可能会得到更多的代码


    现在,您可能有一个具有高SLA的服务,并且实际上希望它生成输出,即使它只是为了生成数据而损坏。或者您有一个简单的转换管道,在这里您将错过一些事件,但保留大部分(下游可以处理不完整的数据)。那么您就对了,您需要用try-catch包装所有操作符的代码。然而,您通常仍然只对脆弱的操作符执行此操作,而不是对所有操作符执行此操作。琐碎的操作符应该经过测试,然后信任它工作。此外,您通常只捕获特定类型的异常,以将范围限制为可能发生的预期异常类型

    您可能想知道为什么Flink没有将其作为默认模式合并。据我所知,有两个原因:

  • 如果Flink默默地忽略任何类型的异常并向辅助接收器发送额外的消息,Flink如何确保抛出操作符随后处于正常状态?它如何避免由于未执行清理代码而可能发生的任何泄漏
  • 在Java中,更常见的是让开发人员明确地解释异常和异常处理。这也不是直截了当地去了解需求是什么:您是否只想要输入?是否还要存储异常?可能影响结果的操作员状态如何?在给定的时间窗口内收到过多错误时,Flink是否仍会失败?它很快就成为了一个巨大的功能,因为在一个理想的世界中,高质量的数据被接收和正确处理,根本不应该发生这样的事情
  • 因此,虽然对您的情况来说很容易,因为您确切地知道要存储哪些类型的信息,但要有一个适用于所有目的的解决方案并不容易,特别是因为与通用解决方案相比,用户必须编写的额外代码非常少


    您可以做的是将大多数复杂的逻辑内容提取到单个
    ProcessFunction
    中,并使用您概述的边输出。因为它是一个中心部分,所以只需要编写一次side输出函数。如果执行多次,您可以提取一个helper函数,在该函数中,您将实际代码作为
    runnableithexception
    lambda传递,该函数隐藏了所有的边输出逻辑。确保使用大量的
    finally
    块来确保状态正常


    我还将添加许多IT案例,并使用突变测试来更快地强化您的管道。如果您保持测试数据的内联,那么变体也可能准确地模拟您意外的数据问题,这样您的验证操作符就更完整了。

    我认为想法不是捕获所有类型的异常并将它们发送到其他地方,而是要有经过良好测试和运行的代码,并且只对无效输入使用死信

    因此,典型的管道是

    source => validate => ... => sink
                      \=> dead letter queue
    
    一旦记录通过validate运算符,您就希望所有错误都冒出来,因为这些运算符中的任何错误都可能导致损坏的聚合和数据,这些数据一旦写入就无法轻松恢复

    验证步骤可以使用您概述的两种方法中的任何一种。通常,边输出具有更好的语义,但最终可能会得到更多的代码


    现在,您可能有一个具有高SLA的服务,并且实际上希望它生成输出,即使它只是为了生成数据而损坏。或者您有一个简单的转换管道,在这里您将错过一些事件,但保留大部分(下游可以处理不完整的数据)。那么您就对了,您需要用try-catch包装所有操作符的代码。然而,您通常仍然只对脆弱的操作符执行此操作,而不是对所有操作符执行此操作。琐碎的操作符应该经过测试,然后信任它工作。此外,您通常只捕获特定类型的异常,以将范围限制为可能发生的预期异常类型

    您可能想知道为什么Flink没有将其作为默认模式合并。据我所知,有两个原因:

  • 如果Flink默默地忽略任何类型的异常并向辅助接收器发送额外的消息,Flink如何确保抛出操作符随后处于正常状态?它如何避免由于未执行清理代码而可能发生的任何泄漏
  • 我觉得这更常见