在我们的系统中,我们正在状态为NEW
的数据库中记录票证,并将其放入kafka队列中以进行进一步处理。处理器从kafka队列中选择那些票证,进行处理并相应地更新状态。我们发现某些票证永远处于NEW
状态。因此,我们在猜测票证是在队列中生成失败还是没有被消耗。
因此,我开始进行详尽的研究,以了解在卡夫卡中我们将以何种方式面对邮件丢失和重复的问题。下面列出了在this post中可以找到的所有可能的消息丢失和重复情况:
- 以不同的方式处理所有副本的数据丢失如何发生
- 等待领导者上线来处理
在所有副本关闭和领导者联机之间发送的消息将丢失。- 通过在线上选举新经纪人作为领导者来处理
如果新经纪人与前任负责人不同步,则在 该经纪人破产的时间以及当选新任领导人时 丢失。随着其他经纪人的加入,他们将看到他们已经承诺 新领导者上不存在的邮件,然后将其删除。领导者掉线而其他副本可能掉线时如何发生数据丢失
在这种情况下,Kafka控制器将检测到领导者的丢失,并从同步副本池中选出新的领导者。这可能需要花费几秒钟的时间,并且会导致客户端出现LeaderNotAvailable错误。但是,只要生产者和消费者处理此可能性并适当地重试,就不会发生数据丢失。消费者可能错过了一条消息
如果将Kafka配置为保留一天的邮件,而使用者关闭的时间超过一天,则该使用者将丢失邮件。评估消费者一致性的不同方法
- 将使用者配置为最多接收一次每条消息时,可能不会处理消息
- 当使用者配置为至少接收一次每条消息时,消息可能重复/处理两次
- 如果使用者配置为仅接收一次消息,则不会多次处理消息或不对其进行处理。
Kafka提供以下保证,只要您要生产一个分区并从一个分区消费。如果您使用两个使用者从同一分区读取或使用两个生产者写入同一分区,则所有保证均关闭。 Kafka对数据一致性和可用性做出以下保证:
- 发送到主题分区的消息将按照发送顺序添加到提交日志中,
- 单个使用者实例将按照在日志中出现的顺序查看消息,
- 所有同步副本将其应用于日志后,该消息即为“已提交”,并且
只要至少有一个同步副本处于活动状态,任何提交的消息都不会丢失。
看了几篇文章后,我觉得我应该做以下事情:
如果消息未排队,生产者应重新发送
为此,生产者应侦听每条发送的消息的确认。如果未收到确认,则可以重试发送消息- 中所述
生产者应与回调异步:
如last example here在生产者重试发送的情况下如何避免重复
为避免队列中出现重复项,请在生产者配置中设置enable.idempotence=true
。这将使生产者确保发送的每个消息恰好一个副本。这需要在生产者上设置以下属性:
max.in.flight.requests.per.connection
<= 5retries
> 0acks=all
(在所有代理都已提交消息后获得确认)生产者应该具有交易性
如here所述。
将交易ID设置为唯一ID:
producerProps.put("transactional.id", "prod-1");
由于我们启用了
idempotence
,Kafka将使用此事务ID作为其算法的一部分来对生产者发送的任何消息进行重复数据删除,以确保幂等性。使用事务语义:init,begin,commit,close
如here所述:producer.initTransactions(); try { producer.beginTransaction(); producer.send(record1); producer.send(record2); producer.commitTransaction(); } catch(ProducerFencedException e) { producer.close(); } catch(KafkaException e) { producer.abortTransaction(); }
消费者应该具有交易性
consumerProps.put("isolation.level", "read_committed");
这可确保消费者在交易完成之前不会阅读任何交易消息。
在使用者中手动提交偏移量
所述
如here
处理记录并自动保存偏移量
通过原子地保存记录处理输出和偏移到任何数据库来表示。为此,我们需要将数据库连接的自动提交设置为false,并在同时处理输出和偏移量之后手动提交。这还需要将enable.auto.commit
设置为false
。从数据库读取初始偏移量(例如,从缓存恢复后读取)
寻求消费者到该偏移量,然后从该位置读取。
(有些疑问可能是主要的,可以通过执行代码来解决。但是我想要有经验的kafka开发人员的话。)
使用者是否只需要为初始读取(在使用者恢复之后/首先读取)或所有读取就从数据库读取偏移量?我觉得它仅需要在重新启动时才从数据库读取偏移量,如here
我们是否必须选择手动分区?这种方法仅适用于自动分区吗?我对此有疑问,因为this example通过明确指定分区来解释在MySQL中存储偏移量。
我们是否同时需要:生产者端kafka事务和消费者端数据库事务(用于自动存储偏移量和处理记录)?我觉得,对于生产者幂等,我们需要生产者具有唯一的事务ID,为此,我们需要使用kafka事务性api(init,begin,commit)。作为对等方,消费者还需要将isolation.level
设置为read_committed
。但是,如果不使用kafka事务,是否可以确保没有消息丢失和重复处理?还是绝对有必要?
我们应该像上面和here
所述那样继续偏移到外部数据库吗?
或按照交易说明here向交易发送偏移量(同样,通过向交易发送偏移量我也没有得到确切的含义)
或按照here中的说明进行同步异步提交。
我认为消息丢失/重复情况1和2由上面我解释的方法的第1点到第4点处理。
我认为消息丢失/重复情形3是由我上面说明的方法的第6点处理的。
我们如何实现消息丢失/重复情形4中所述的不同的消费者一致性方法?是他们的任何配置,还是需要在使用者内部的自定义逻辑中实现?
消息丢失/重复情形5说:“只要您对一个分区进行生产并从一个分区进行消费,Kafka就会提供以下保证。”建立正确的系统时需要关注的事情吗?
在上面提出的方法中,是否有任何考虑不必要/多余的考虑?我也错过了任何必要的考虑吗?我是否错过任何消息丢失/重复的情况?
他们是否还有其他标准/推荐/较可取的方法来确保没有消息丢失和重复处理,而不是我之前所想的?
我是否必须使用kafka API来对上述方法进行编码?还是在kafka API之上构建了高级API,可轻松确保没有消息丢失和重复处理?
考虑到我们所面临的问题(如开头所述),我们在考虑是否可以从kafka存储邮件的文件中恢复任何丢失/未处理的邮件。但是那是不正确的,对吧?
(对这么详尽的帖子深感抱歉,但我想写一个问题,将在一个地方提出所有相关问题,以便对如何在kafka上构建系统有一个全面的了解。)