Kafka在0.11.0.0之前的版本中只支持At Least OnceAt Most Once语义,尚不支持Exactly Once语义。

但是在很多要求严格的场景下,如使用Kafka处理交易数据,Exactly Once语义是必须的。我们可以通过让下游系统具有幂等性来配合Kafka的At Least Once语义来间接实现Exactly Once。但是:

  • 该方案要求下游系统支持幂等操作,限制了Kafka的适用场景
  • 实现门槛相对较高,需要用户对Kafka的工作机制非常了解
  • 对于Kafka Stream而言,Kafka本身即是自己的下游系统,但Kafka在0.11.0.0版本之前不具有幂等发送能力

因此,Kafka本身对Exactly Once语义的支持就非常必要。

操作原子性

操作的原子性是指,多个操作要么全部成功要么全部失败,不存在部分成功部分失败的可能。

实现原子性操作的意义在于:

  • 操作结果更可控,有助于提升数据一致性
  • 便于故障恢复。因为操作是原子的,从故障中恢复时只需要重试该操作(如果原操作失败)或者直接跳过该操作(如果原操作成功),而不需要记录中间状态,更不需要针对中间状态作特殊处理

实现事务机制的几个阶段

幂等性发送

上文提到,实现Exactly Once的一种方法是让下游系统具有幂等处理特性,而在Kafka Stream中,Kafka Producer本身就是“下游”系统,因此如果能让Producer具有幂等处理特性,那就可以让Kafka Stream在一定程度上支持Exactly once语义。

为了实现Producer的幂等语义,Kafka引入了Producer ID(即PID)和Sequence Number。每个新的Producer在初始化的时候会被分配一个唯一的PID,该PID对用户完全透明而不会暴露给用户。

对于每个PID,该Producer发送数据的每个<Topic, Partition>都对应一个从0开始单调递增的Sequence Number

类似地,Broker端也会为每个<PID, Topic, Partition>维护一个序号,并且每次Commit一条消息时将其对应序号递增。对于接收的每条消息,如果其序号比Broker维护的序号(即最后一次Commit的消息的序号)大一,则Broker会接受它,否则将其丢弃:

  • 如果消息序号比Broker维护的序号大一以上,说明中间有数据尚未写入,也即乱序,此时Broker拒绝该消息,Producer抛出InvalidSequenceNumber
  • 如果消息序号小于等于Broker维护的序号,说明该消息已被保存,即为重复消息,Broker直接丢弃该消息,Producer抛出DuplicateSequenceNumber

上述设计解决了0.11.0.0之前版本中的两个问题:

  • Broker保存消息后,发送ACK前宕机,Producer认为消息未发送成功并重试,造成数据重复
  • 前一条消息发送失败,后一条消息发送成功,前一条消息重试后成功,造成数据乱序

事务性保证

上述幂等设计只能保证单个Producer对于同一个<Topic, Partition>Exactly Once语义。

另外,它并不能保证写操作的原子性——即多个写操作,要么全部被Commit要么全部不被Commit。

更不能保证多个读写操作的的原子性。尤其对于Kafka Stream应用而言,典型的操作即是从某个Topic消费数据,经过一系列转换后写回另一个Topic,保证从源Topic的读取与向目标Topic的写入的原子性有助于从故障中恢复。

事务保证可使得应用程序将生产数据和消费数据当作一个原子单元来处理,要么全部成功,要么全部失败,即使该生产或消费跨多个<Topic, Partition>

另外,有状态的应用也可以保证重启后从断点处继续处理,也即事务恢复。

为了实现这种效果,应用程序必须提供一个稳定的(重启后不变)唯一的ID,也即Transaction IDTransactin IDPID可能一一对应。区别在于Transaction ID由用户提供,而PID是内部的实现对用户透明。

另外,为了保证新的Producer启动后,旧的具有相同Transaction ID的Producer即失效,每次Producer通过Transaction ID拿到PID的同时,还会获取一个单调递增的epoch。由于旧的Producer的epoch比新Producer的epoch小,Kafka可以很容易识别出该Producer是老的Producer并拒绝其请求。

有了Transaction ID后,Kafka可保证:

  • 跨Session的数据幂等发送。当具有相同Transaction ID的新的Producer实例被创建且工作时,旧的且拥有相同Transaction ID的Producer将不再工作。
  • 跨Session的事务恢复。如果某个应用实例宕机,新的实例可以保证任何未完成的旧的事务要么Commit要么Abort,使得新实例从一个正常状态开始工作。

事务机制原理

事务性消息传递

这一节所说的事务主要指原子性,也即Producer将多条消息作为一个事务批量发送,要么全部成功要么全部失败。

为了实现这一点,Kafka 0.11.0.0引入了一个服务器端的模块,名为Transaction Coordinator,用于管理Producer发送的消息的事务性。

Transaction Coordinator维护Transaction Log,该log存于一个内部的Topic内。由于Topic数据具有持久性,因此事务的状态也具有持久性。

Producer并不直接读写Transaction Log,它与Transaction Coordinator通信,然后由Transaction Coordinator将该事务的状态插入相应的Transaction Log

Transaction Log的设计与Offset Log用于保存Consumer的Offset类似。

事务中Offset的提交

许多基于Kafka的应用,尤其是Kafka Stream应用中同时包含Consumer和Producer,前者负责从Kafka中获取消息,后者负责将处理完的数据写回Kafka的其它Topic中。

为了实现该场景下的事务的原子性,Kafka需要保证对Consumer Offset的Commit与Producer对发送消息的Commit包含在同一个事务中。否则,如果在二者Commit中间发生异常,根据二者Commit的顺序可能会造成数据丢失和数据重复:

  • 如果先Commit Producer发送数据的事务再Commit Consumer的Offset,即At Least Once语义,可能造成数据重复。
  • 如果先Commit Consumer的Offset,再Commit Producer数据发送事务,即At Most Once语义,可能造成数据丢失。

总结

  • PIDSequence Number的引入实现了写操作的幂等性
  • 写操作的幂等性结合At Least Once语义实现了单一Session内的Exactly Once语义
  • Transaction MarkerPID提供了识别消息是否应该被读取的能力,从而实现了事务的隔离性
  • Offset的更新标记了消息是否被读取,从而将对读操作的事务处理转换成了对写(Offset)操作的事务处理
  • Kafka事务的本质是,将一组写操作(如果有)对应的消息与一组读操作(如果有)对应的Offset的更新进行同样的标记(即Transaction Marker)来实现事务中涉及的所有读写操作同时对外可见或同时对外不可见
  • Kafka只提供对Kafka本身的读写操作的事务性,不提供包含外部系统的事务性

出处:http://www.jasongj.com/kafka/transaction/

【Kafka】Exactly Once语义与事务的更多相关文章

  1. Kafka设计解析(八)- Exactly Once语义与事务机制原理

    原创文章,首发自作者个人博客,转载请务必将下面这段话置于文章开头处. 本文转发自技术世界,原文链接 http://www.jasongj.com/kafka/transaction/ 写在前面的话 本 ...

  2. Kafka设计解析(八)Exactly Once语义与事务机制原理

    转载自 技术世界,原文链接 Kafka设计解析(八)- Exactly Once语义与事务机制原理 本文介绍了Kafka实现事务性的几个阶段——正好一次语义与原子操作.之后详细分析了Kafka事务机制 ...

  3. kafka 幂等生产者及事务(kafka0.11之后版本新特性)

    1. 幂等性设计1.1 引入目的生产者重复生产消息.生产者进行retry会产生重试时,会重复产生消息.有了幂等性之后,在进行retry重试时,只会生成一个消息. 1.2 幂等性实现1.2.1 PID ...

  4. 基于Kafka消息驱动最终一致事务(二)

    实现用例分析 上篇基于Kafka消息驱动最终一致事务(一)介绍BASE的理论,接着我们引入一个实例看如何实现BASE,我们会用图7显示的算法实现BASE.

  5. 基于Kafka消息驱动最终一致事务(一)

    基本可用软状态最终一致事务 本用例分两个数据库分别是用户库和交易库,不使用分布式事务,使用基于消息驱动实现基本可用软状态最终一致事务(BASE).现在说明下事务逻辑演化步骤,尊从CAP原则,即分布式系 ...

  6. Kafka 幂等生产者和事务生产者特性(讨论基于 kafka-python | confluent-kafka 客户端)

    Kafka 提供了一个消息交付可靠性保障以及精确处理一次语义的实现.通常来说消息队列都提供多种消息语义保证 最多一次 (at most once): 消息可能会丢失,但绝不会被重复发送. 至少一次 ( ...

  7. 使用kafka消息队列解决分布式事务(可靠消息最终一致性方案-本地消息服务)

    微服务框架Spring Cloud介绍 Part1: 使用事件和消息队列实现分布式事务 本文转自:http://skaka.me/blog/2016/04/21/springcloud1/ 不同于单一 ...

  8. Hibernate(四)结构-基础语义和事务

    一.基础语义 核心: Configuration SessionFactory Session 二.Configuration Configuration类负责管理Hibernate的配置信息,Hib ...

  9. Kafka集群的安装和使用

    Kafka是一种高吞吐量的分布式发布订阅的消息队列系统,原本开发自LinkedIn,用作LinkedIn的活动流(ActivityStream)和运营数据处理管道(Pipeline)的基础.现在它已被 ...

随机推荐

  1. LocalDateTime的一些用法

    包括获取当前时间,指定特定时间.进行时间的加减等 LocalDateTime localDateTime3 = LocalDateTime.now(); LocalDate.now(); LocalT ...

  2. 用Python玩转微信

    Python玩转微信 大家每天都在用微信,有没有想过用python来控制我们的微信,不多说,直接上干货!  这个是在 itchat上做的封装  http://itchat.readthedocs.io ...

  3. 用java多线程模拟数据库连接池

    模拟一个ConnectionDriver,用于创建Connection package tread.demo.threadpool; import java.lang.reflect.Invocati ...

  4. 简述 gevent模块的作用和应用场景。

    当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成, 再在适当的时候切换回来继续执行.由于IO操作非常耗时,经常使程序处于等待状态, 有了geve ...

  5. [codevs1566]染色

    题目描述 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一 ...

  6. Nacos

    欢迎来到 Nacos 的世界! Nacos 致力于帮助您发现.配置和管理微服务.Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现.服务配置.服务元数据及流量管理. Nacos 帮助您 ...

  7. Windows快速批量删除大量文件的命令

    命令分别如下: rmdir [drive:]path [/S] [/Q] del [drive:]path [/S] [/Q] 其中rmdir 与rd命令相同. /S 表示除目录本身外,还将删除指定目 ...

  8. 这样配置:让你的 IDEA 好用到飞起来

    阅读本文大概需要 7 分钟. 来源:blog.csdn.net/fly910905/article/details/77868300 1.设置maven 1.在File->settings-&g ...

  9. VS C++代码能正确编译 但还是显示红色波浪线 提示"无法打开源文件"

    症状细节 我发现很多第三方库的环境部署教程,都会教读者配置 属性VC++目录 -> 包含目录 比如OpenCV的环境配置教程. 这样配置之后能通过编译: 但是,在IDE里,会有一些烦人的红波浪线 ...

  10. c# 自定义按钮,渐变颜色(含中心向四周渐变,单方向渐变)

    废话不多言,直接代码: public class RoundButton : Button { bool clickBool = false; //1.设置圆形 //2.设置渐变色 //3.设置too ...