如果我们执行流处理,我们希望确保输出数据首先作为数据流可用。在您的示例中,这意味着我们将 Kafka 作为主要接收器写入。
现在你有两个选择:
- 具有从该 Kafka 主题读取并写入 Redis 和 HBase 的辅助作业。这就是 Kafka 方式,因为 Kafka Streams 不支持直接写入这些系统中的任何一个,并且您设置了 Kafka 连接作业。然后可以针对特定的接收器定制这些辅助作业,但它们会增加额外的操作开销。(这是您提到的一些备份选项)。
- 使用 Spark 和 Flink,您还可以选择在工作中直接使用辅助接收器。您可以添加额外的处理步骤以将 Kafka 输出转换为更适合接收器的形式,但在配置作业时您会受到更多限制。例如在 Flink 中,您需要对 Kafka 接收器和 Redis/HBase 接收器使用相同的检查点设置。不过,如果设置成功,您只需要运行一个流式作业而不是 2 或 3 个。
迟到的事件
现在的问题是如何处理迟到的数据。最好的解决方案是让框架通过水印来处理。也就是说,只有在框架确定没有迟到的数据到达时,才会在所有接收器上提交数据。如果这不起作用,因为您确实需要处理迟到的事件,即使它们很晚才到达并且仍然希望获得临时结果,那么您必须使用更新事件。
更新事件
(根据 OP 的要求,我将在更新事件中添加更多详细信息)
在 Kafka Streams 中,默认情况下,元素是通过连续细化机制发出的。这意味着,窗口聚合一旦有任何有效数据点就会发出结果,并在接收新数据时更新该结果。因此,任何迟到的事件都会被处理并产生更新的结果。虽然这种方法很好地减轻了用户的负担,因为他们不需要了解水印,但它有一些严重的缺点,导致 Kafka Streams 开发人员在 2.1 及更高版本中添加了Suppression。
主要问题是它对向下的用户处理中间结果提出了相当大的挑战,这也在关于 Suppression 的文章中进行了解释。如果结果是临时的还是“最终的”(从某种意义上说,所有预期的事件都已处理)并不明显,那么许多应用程序就更难实现。特别是,需要在消费者端复制窗口操作以获得“最终”值。
另一个问题是数据量被炸毁。如果您有很强的聚合因子,那么使用基于水印的发射将在第一次操作后大大减少您的数据量。但是,随着每条记录触发所有中间步骤的新(中间)记录,连续细化将增加一个恒定的体积因子。
最后,对您来说特别有趣的是,如果您有更新事件,如何将数据卸载到外部系统。理想情况下,您将连续或定期卸载具有一定时间延迟的数据。该方法在消费者端再次模拟基于水印的发射。
混合选项
可以将水印用于初始发射,然后将更新事件用于后期事件。然后为所有“准时”事件减小音量。例如,Flink 提供允许延迟以使窗口再次触发延迟事件。
这种设置使卸载数据变得更加容易,因为只有在实际发生延迟事件时才需要将数据重新发送到外部系统。应该调整系统,因为迟到的事件是一种罕见的情况。