Kafka消费者自动提交配置会导致潜在的重复或数据丢失!


在New Relic,我们使用Apache Kafka构建了管道的一些关键部分。多年来,我们遇到了很多问题,并设计了管理Kafka集群的最佳实践。我们已经从中学到了很多关于Kafka如何有效而不是那么有效地工作的知识。
我们遇到的一个基本问题涉及Kafka的消费者自动提交配置 - 特别是当消费者服务遇到内存不足(OOM)或其他类型的硬关机时,如何发生数据丢失或数据重复。
让我解释一下这种行为及其对Kafka用户意味着什么。

问题
生产者发送给Kafka分区的每条消息都有一个偏移量:用于标识每条消息的顺序索引号。要跟踪已处理的消息,您的消费者需要提交已处理消息的偏移量。
除非您手动触发提交,否则您最有可能使用Kafka使用者自动提交机制。自动提交是开箱即用的,默认情况下每五秒提交一次。
卡夫卡的消费者不知道你对这个消息做了什么,而且对于提交补偿更加冷漠。就消费者而言,只要收到消息,就会被“处理”。
所以现在想象一下,你的消费者已经吸收了1000条消息并将它们缓存到内存中。然后自动提交触发,提交这1,000条消息的偏移量。但是,假设您的服务现在使用了太多内存,并且在处理完所有消息之前会被OOM终止信号强行关闭。这样就永远不会处理剩余的可能是数百条消息,这就是数据丢失。
相反的情况也是可能的。您可以成功处理这1,000条消息,然后在提交偏移量之前发生问题如硬件故障等。在这种情况下,您将在消费者重新平衡后重新处理另一个实例上的数百条消息,这就是数据重复。

该怎么办呢?
坏消息是这里没有任何简单的修复。从根本上说,这是一致性保证较弱的问题。所有Kafka用户想要的只是一次处理 - 保证您只需消费和处理一次消息。Kafka 0.11版试图解决这个问题,并使事情略微好转。可以使用Kafka 0.11编写一次精确的管道,但要完全消耗一次,您需要在消费者中实现自己的事务语义,以便在完成处理时告诉Kafka(如果出现问题则回滚) 。
此外,在我们的测试中,我们发现Kafka 0.11中消息生成的事务模型没有像我们需要的那样快速处理消息,每条消息最多需要10-100毫秒。这是我们无法承受的额外延迟。
不过,你确实还有一些选择。只要您处于这些框架的限制范围内,几个流式解决方案(如FlinkKafka Streams)就可以提供一次性处理。
另一种选择是“滚动你自己的”一次性策略,它只会自动为已经到达处理管道末尾的消息提交偏移量。这只是对那些最勇敢的类型的建议,因为这样做很难,你可能会遇到比你解决的问题更多的问题。

并且始终可以选择接受数据丢失或重复的风险。可悲的是,这是许多Kafka用户选择的选项,但并不总是一个不合理的选择。您在其中一个场景中实际丢失或重复的数据量相对较小; 自动提交应该只比实际上次提交的消息少几秒。发生的频率也应该很低 - 理想情况下,您的Kafka消费者服务不会获得通常的OOM终止信号。
因此,与在您的所有Kafka消费者服务中解决此问题的工程成本相比,如果您使用的数据允许少量损失,这可能只是您所面临的风险。当然,您始终可以通过构建可靠的服务来降低风险。

向前进
正如我所说,我们在New Relic管理Kafka集群方面拥有丰富的经验。我们发现,随着我们扩展以处理大量数据量,构建高度可靠的服务变得更加困难,我们已经就如何处理Kafka中的这一特定缺陷做出了一些实际的决定,如果我们的某个服务经历过硬关机就执行这些决定。
如果您接受数据丢失的风险,请采取以下步骤将其最小化:

  • 请注意此问题,并将其记录在风险矩阵中。
  • 确保您的服务稳定。提醒您服务的SIGKILL和OOM。
  • 在构建新服务时,请考虑使用直接解决此问题的流媒体系统。