理解Apache kafka 的设计元素和原则(三)

消息传递语义

kafka 为生产者和消费者提供了 semantic guarantee
显然,可以有很多种消息传递担保:

  • at most one – 消息可能丢失,但是只会被发送一次
  • at least one – 消息不会丢失,但是可能会被重发多次
  • exactly one – 消息只会被成功发送一次

值得注意的是,问题被拆成两个: 发送消息保证 和 消费消息保证。很多系统都声称提供 “exactly once” 传递语义,但是绝大多数都是误导性的,他们不考虑生产或消费可能失败的情况,也不考虑多消费者、磁盘写入失败等情况。

kafka 语义 相当直接,当生产者发送消息的时候,我们对这条消息标记 “committed” 并提交到broker的日志。一旦发送的消息被提交,那么只要有一个复制了消息写入分区的broker 存货,消息就不会丢失。如果生产者在发布消息的时候遇到网络错误,那么就不能确定是提交之前还是之后发生了错误。这和向带自增主键的数据库表插入数据的语义很相似。

在 0.11.0.0版本之前,如果生产者没有收到消息已被提交的回应,那么它只能重新发送 消息, 这就是 “at least once ” 语义,尽管当原始消息实际上成功写入的时候,消息可能会在重发的时候再次写入日志。从 0.11.0.0版本开始,kafka 生产者开始支持一种 能够保证重发的消息不会导致日志中存在重复消息的 幂等传递选项。为了实现此功能, broker 给每个生产者分配一个 ID 并且通过每条消息携带的序列号删除重复数据。也是从本版本开始,生产者开始支持使用事务式语义向多个主题分区发送消息(消息保持事务性),主要的应用场景是 kafka 主题之间的 “exactly-once” 处理过程。

也不是所有的场景都需要强保障,对于一些延迟敏感的场景,我们允许生产值指定耐久水平(may be timeout),如果生产者制定了它想要的消息提交回应等待时间,这可能需要10ms的量级。然而生产者也可以指定它想要完全异步执行操作,或者只想要等待 leader 接收到消息(无论follower是否同步到此消息)。

接下来看看消费者对语义的观点。所有的副本在同样的偏移量上都具备相同的日志。由消费者来控制自己在日志上的位置。

如果消费者能够保持存货状态从不销毁,那它就可以只是将位置保存在内存中。但是如果此消费者崩溃,我们就希望由另一个消费者来接手此主题分区并且新的进程需要选择一个合适的位置来开始处理过程。这样,位置仅仅是存储在内存中是不够的。
针对此问题,消费者有几个选项来处理消息和更新位置:

  • 读取消息,将位置存储在log, 然后在消费消息。这种方式可能在保存偏移量后,尚未保存消息处理结果之前的时候消费者崩溃。这样的话,后面接手的进程(消费者)将会遗漏一小部分的消息未被消费。“at most once”
  • 读取消息,消费消息,之后再存储位置信息。这种情况下,消费者可能在处理消息后,但尚未保存位置之前崩溃,这样当新的进程接手的时候,位置后吗的一小部分消息实际上已经被处理过了。这对应着 “at least once” 语义。

在很多情况下,每条消息都有一个唯一主键,所以更新时幂等的(接收两次,但是只copy一次)

那么,当我们需要从kafka的一个主题消费消息然后将结果数据发送给另一个主题时,如何实现 exactly once 语义?
我们可以利用上面提到的 0.11.0.0版本的新事务特性。消费者的位置position 也同消息一样存储在主题中,所以我们可以在输出主题接收到处理过的消息时在 同一个事务中将 偏移量写入kafka.。如果事务被中止,消费者的位置position将被回溯到上一次的值,并且,输出主题中的数据对于其他消费者将不可见,这取决于他们的隔离级别。 默认的“read_uncommitted”隔离级别,所有的消息对消费者可见,即使这些消息属于某个中断的事务的一部分;但是对于“read_committed”隔离级别,此消费者将仅仅返回事务中被提交的消息。
Ps: 上一段不太理解,仅将原文档翻译

当需要将数据写入到另一个第三方系统,限制在于需要准确记录协调实际存储到输出的位置(消费者偏移量)。经典的解决方案是,在保存消费者位置和消费者输出之间采用 two-phase commit。这可以通过让消费者将偏移量和输出存储在同一个地方来简单的实现。这种解决方式更加方便,因为很多第三方系统并不支持 two-phase commit。对于需要遵循更强大语义的数据系统和一些消息没有主键以供删除重复数据的系统,我们遵循类似的模式。

所以,高效的kafka在kafka流中支持 exactly-once 传递,并且,事务性生产者和消费者可用于在 kafka主题之间传递和处理数据是提供exactly-once 传递。在与其他目标系统之间实现excatly-once 传递通常需要同其他系统协作配合,但是kafka提供的offset维护使得这种实现相当灵活。此外,kafka 默认采用 at-least-once 策略,并且允许用户通过 disable 生产者的重发 和 在消费者消费一批消息之前就提交offset的方式来实现 at-most-once 策略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值