4.6 消息分发语义
在了解了生产者和消费者的工作方式之后,我们来讨论Kafka在生产者和消费者之间提供的语义保证。 显然,有多个可能的消息专题保证可以提供:
- 最多一次——消息可能会丢失,但是永远不会重复传递
- 至少一次——消息永远不会丢失,但是可能会被重新传递
- 恰好一次——这是人们真正想要的,每条消息传递一次
值得注意的是,这会分解成两个问题:发布消息的持久性保证以及消费消息时的保证。
许多系统声称可以提供恰好一次的交付语义,但是阅读细则很重要,这些声明中的大多数具有误导性(即它们不能翻译为消费者或生产者可能失败的情况,有多个消费者进程,或者数据写入磁盘可能失败的情况)。
Kafka的语义很直接。在发布消息时,我们有一个消息被“提交”到日志的概念。一旦提交已经发布的消息,只要把消息复制到分区的broker保持“活动”,它就不会丢失。提交消息的定义,活动分区以及我们尝试处理那些类型的故障的描述将在下一节中详细描述。现在让我们假设一个完美的broker,并且尝试了解对生产者和消费者的保证。如果生产者尝试发布消息并遇到网络错误,则无法确定在提交消息之前或者之后发生了此错误。这类似于使用自动生成的密钥插入数据库表的语义。
在0.11.0.0之前,如果生产者未能收到表明消息已经提交的响应,则除了重新发送请求之外别误选择。这提供了至少一次传递语义,因为如果原始请求实际上请求成功了,则在重新发送期间可以再次将消息写入日志。从0.11.0.0开始,Kafka还支持幂等传递选项,该选项保证重新发送不会在日志中导致重复条目。为了实现这个目的,broker为每个生产者分配一个ID,并使用生产者发送的序列号和每条消息对每条消息进行重复数据删除。同样从0.11.0.0开始,生产者支持使用类似事务的语义向多个主题分发消息的能力:即,所有的消息都被成功写入,或者都没有。这方面的使用示例是Kafka主题之间的一次性处理(如下所述):
并非所有的使用情况都需要这样强有力的保证。对于延迟敏感的情况,我们允许生产者指定它需要的耐久性等级。如果生产者指定它要等待的消息,可以采用10毫秒的量级。但是,生产者还可以指定它想要完全异步地执行发布,或者只想等到领导者(但不一定是追随者)有消息。
现在让我们从消费者的角度来描述语义。所有副本都具有完全相同的日志,具有相同的偏移量。消费者控制其在此日志中的位置。如果消费者永不崩溃,它只能讲此位置存储在内存中,但如果消费者挂了并且我们希望该主题分区被另一个进程接管,则新进程将需要选择适当的位置开始处理。假设消费者阅读一些消息——他有几个选线来处理消息并更新其位置。
- 可以读取消息,然后将其位置保存在日志中,最后处理消息。在这种情况下,消费者进程可能在保存其位置之后,但在保存其消息处理的输出之前崩溃。在这种情况下,接管处理的进程将从保存的位置开始,即使该位置之前的一些消息尚未处理。这对应"最多一次“语义,因为在消费者失败的情况下可能不会处理消息。
- 可以读取消息,处理消息,最后保存其位置。这种情况下,消费者进程可能在处理之后,但在保存其位置之前崩溃。这种情况下,新进程接管它收到的前几条消息时,它们已经被处理过了。这对应于消费者失败情况下的”至少一次“语义。在许多情况下,消息具有主键,因此更新是幂等的(两次接受相同的消息只是用另一个自身的副本覆盖记录)。
那么确切的一次语义呢?当从Kafka主题消费并生成另一个主题时(如在Kafka Stream应用程序中),我们可以利用上面提到的0.11.0.0中的新事物生成器功能。消费者的位置作为主题中的消息存储,因此我们可以在于接收器处理数据的输出主题相同的事务中将偏移量写入Kafka。如果事务中止,则消费者的位置将恢复成旧值,并且输出主题上生成的数据将不会被其他消费者看到,具体取决于其隔离级别。在默认的read_uncommitted隔离级别中,消费者可以看到所有的消息,即使它们是中止事务的一部分,但是在read_committed中,消费者只会从已提交的事物(以及任何不属于数据处理的一部分的消息)。
在写入外部系统时,需要限制消费者的位置与实际存储的输出。实现这一目标的经典方法是在消费者位置的存储和消费者输出的存储之间引入两阶段提交。但是这可以通过让消费者将其偏移量存储在于其输出相同的位置来简单的处理。这样更好,因为消费者可能想要写入的许多输出系统将不支持两阶段提交。一个例子:考虑一个Kafka Connect连接器,它填充HDFS中的数据以及它读取的数据的偏移量,以确保数据和偏移量得到更新或者两者都不更新。我们遵循许多其他数据系统的类似模式,这些数据系统需要这些更强的语义,并且消息没有逐渐以允许重复数据删除。
因此,Kafka支持Kafka Streams中的一次性交付,并且在Kafka主题之间传输和处理数据时,事务性生产者/消费者通常可用于提供一次性交付。对其他目标系统的一次性交付通常需要与此类系统合作,但Kafka提供了偏移量,这使得实现这一点成为可行(另请参见Kafka Connect)。否则,Kafka默认保证至少一次交付,并允许用户通过在处理一批消息之前禁用生产者的重试,并在护理一批数据之前提交消费者中的偏移量,来实现最多一次交付。
网友评论