卡夫卡流中幂等性与正当性之间的区别

时间:2019-11-16 19:12:51

标签: apache-kafka apache-kafka-streams

我正在查看文档,了解到我们可以启用idempotence=true

来完成一次准确的交易
  

幂等:幂等生产者对一个   制片人针对一个主题。基本上每条消息都发送   有石匠人的担保,如果有   错误

那么,如果我们已经有了幂等性,那为什么我们要在Kafka Stream中一次只需要另一个属性呢?幂等与完全一次之间究竟有什么区别

为什么普通Kafka Producer中无法使用一次精确属性?

2 个答案:

答案 0 :(得分:12)

Kafka流从端到端的角度提供一次精确的语义(从一个主题进行消费,处理该消息,然后生成另一个主题)。但是,您仅提及了生产者的幂等属性。那只是整个图片的一小部分。

让我改一下这个问题:

  

为什么我们需要在消费者端实现一次准确的投放语义   虽然我们已经保证在   生产者方面?

答案::由于一次准确的传递语义不仅在生产步骤中,而且在处理的整个流程中也是如此。为了在语义上实现一次准确交付,必须满足生产和消费的一些条件。

这是一般情况:进程A产生到主题T的消息。同时,进程B尝试使用主题T的消息。我们要确保进程B永远不会处理一条消息两次。

生产者部分:我们必须确保生产者从不产生两次消息。我们可以使用Kafka Idempotent Producer

消费部分: 这是消费者的基本工作流程:

  • 第1步:消费者成功地从Kafka的主题中提取了消息M。
  • 第2步:消费者尝试执行作业,作业成功返回。
  • 第3步:消费者将邮件的偏移量提交给Kafka经纪人。

以上步骤只是一条快乐的路。现实中出现了很多问题。

  • 场景1:步骤2上的作业成功执行,但使用者崩溃了。由于这种意外情况,使用者尚未提交该消息的抵消额。使用者重新启动后,该消息将被使用两次。
  • 场景2:使用者在步骤3中提交偏移量时,由于硬件故障(例如:CPU,内存冲突等)而崩溃,在重新启动时,使用者无法知道它是否已成功提交偏移量或不是。

因为可能会发生很多问题,所以作业的执行和提交偏移必须是 atomic (原子的),以确保在消费者端实现一次准确的交付语义。这并不意味着我们不能,但是要确保一次准确的交付语义就需要付出很多努力。 Kafka Stream支持工程师的工作。

请注意::Kafka Stream提供“一次精确的流处理”。它是指从一个主题进行消费,在Kafka主题中实现中间状态并产生一个状态。如果我们的应用程序依赖于其他一些外部服务(数据库,服务...),则必须确保我们的外部依赖关系可以保证在这种情况下仅发生一次。

TL,DR:一次,要实现全流程,需要生产者和消费者之间的合作。

参考:

答案 1 :(得分:5)

在分布式环境中,故障是很常见的情况,可以随时发生。在Kafka环境中,代理可能崩溃,网络故障,处理失败,发布消息时失败或无法使用消息等。 这些不同的情况导致了不同类型的数据丢失和重复。

故障场景

A(确认失败):生产者成功发布了消息,重试次数> 1,但由于失败而未能收到确认。在这种情况下,生产者将重试相同的消息,可能会导致重复消息。

enter image description here

B(生产者进程在批处理消息中失败):生产者发送了一批失败的消息,但发布的成功很少。在这种情况下,一旦生产者重新启动,它将再次批量重新发布所有消息,这将在Kafka中引入重复消息。 enter image description here

C(触发并忘记失败)生产者发布的消息,其中retry = 0(触发并忘记)。如果失败,发布的消息将不知道并发送下一条消息,这将导致消息丢失。 enter image description here

D(批处理消息中的消费者失败)消费者从Kafka接收到一批消息,并手动提交其偏移量(enable.auto.commit = false)。如果消费者在提交给Kafka之前失败,则下次消费者将再次使用相同的记录,这些记录将在消费者端复制副本。

enter image description here

完全一次语义

  

在这种情况下,即使生产者尝试重新发送消息,它也会导致   消息将被发布一次,并被消费者完全消费一次。

要在Kafka中实现精确一次语义,它使用以下3个属性

  1. enable.idempotence = true(地址a,b和c)
  2. MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION = 5(生产者每次连接总是有一个飞行中请求)
  3. isolation.level = read_committed(地址d)

启用幂等(enable.idempotence = true)

  

等幂传递使生产者能够准确地将消息写入Kafka   一次在主题的生存期内转到主题的特定分区   一个生产者,没有数据丢失和每个分区的订单。

“”注意,启用幂等性要求MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION小于或等于5,RETRIES_CONFIG大于0且ACKS_CONFIG为'all'。如果用户未明确设置这些值,则将如果设置了不兼容的值,将抛出ConfigException“

为了实现幂等性,Kafka在生成消息时使用唯一的ID(称为产品ID或PID和序列号)。生产者在发布的每个消息上保持递增的序列号,这些消息具有唯一的PID。代理始终将当前序列号与前一个序列号进行比较,如果新序列号不比上一个序列号大+1,则它会拒绝,这会避免重复;如果消息中丢失了更大的序列号,则会拒绝同时显示 enter image description here

在失败的情况下,代理将序列号与前一个序列号进行比较,如果序列不增加,+ 1将拒绝该消息。 enter image description here

交易(隔离级别)

交易使我们能够自动更新多个主题分区中的数据。事务中包含的所有记录都将被成功保存,或者没有保存成功,它允许您将同一个事务中的消费者补偿与已处理的数据一起提交,从而允许端到端的一次精确语义

生产者无需等待将消息写入kafka,其中生产者使用beginTransaction,commitTransaction和abortTransaction(如果失败) 消费者使用隔离级别,即read_committed或read_uncommitted

  • read_committed:消费者将始终仅读取已提交的数据。
  • read_uncommitted:按偏移顺序读取所有消息,而无需等待 进行交易

如果具有isolation.level = read_committed的使用者到达了尚未完成的事务的控制消息,则它将直到该生产者提交或中止该事务或发生事务超时之前,不会再从该分区传递任何消息。事务超时由生产者使用配置transaction.timeout.ms(默认为1分钟)来确定。

生产者和消费者完全相同

在正常情况下,我们的生产者和消费者是分开的。生产者必须具有幂等性并同时管理事务,以便消费者可以使用isolation.level读取read_committed以使整个过程成为原子操作。 这样可以确保生产者将始终与源系统同步。即使生产者崩溃或事务中止,它也始终是一致的,并且一次将消息或一批消息发布为一个单元。

同一使用者将一次接收消息或一批消息。

  

在精确一次语义生成器和消费者中将出现为   原子操作,它将作为一个单元运行。要么发布并   一次被消耗掉或中止。

在Kafka流中恰好一次

Kafka Stream使用来自主题A的消息,处理该消息并将其发布到主题B,一旦发布,请使用commit(commit主要在后台运行)将所有状态存储数据刷新到磁盘。

Kafka Stream中的“一次”是“读取-处理-写入”模式,可确保将这些操作视为原子操作。由于Kafka Stream可以满足生产者,消费者和交易的需求,因此Kafka Stream都带有特殊的参数processing.guarantee,它可以完全地_once或at_least_once,使得不单独处理所有参数变得容易。

  

Kafka Streams原子地更新消费者补偿,本地状态存储,   状态存储changelog主题和生产以输出所有主题   一起。如果这些步骤中的任何一个失败,则所有更改都是   回滚。

processing.guarantee:完全_一次自动提供以下参数,您无需明确设置

  1. isolation.level = read_committed
  2. enable.idempotence = true
  3. MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION = 5