kafka在0.11.0.0之前的版本中只支持at least once
和at most once
语义,尚不支持exactly once
语义。
但是在很多要求严格的场景下,如使用kafka处理交易数据,exactly once
语义是必须的。我们可以通过让下游系统具有幂等性来配合kafka的at least once
语义来间接实现exactly once
。但是:
因此,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会接受它,否则将其丢弃:
invalidsequencenumber
duplicatesequencenumber
上述设计解决了0.11.0.0之前版本中的两个问题:
上述幂等设计只能保证单个producer对于同一个<topic, partition>
的exactly once
语义。
另外,它并不能保证写操作的原子性——即多个写操作,要么全部被commit要么全部不被commit。
更不能保证多个读写操作的的原子性。尤其对于kafka stream应用而言,典型的操作即是从某个topic消费数据,经过一系列转换后写回另一个topic,保证从源topic的读取与向目标topic的写入的原子性有助于从故障中恢复。
事务保证可使得应用程序将生产数据和消费数据当作一个原子单元来处理,要么全部成功,要么全部失败,即使该生产或消费跨多个<topic, partition>
。
另外,有状态的应用也可以保证重启后从断点处继续处理,也即事务恢复。
为了实现这种效果,应用程序必须提供一个稳定的(重启后不变)唯一的id,也即transaction id
。transactin id
与pid
可能一一对应。区别在于transaction id
由用户提供,而pid
是内部的实现对用户透明。
另外,为了保证新的producer启动后,旧的具有相同transaction id
的producer即失效,每次producer通过transaction id
拿到pid的同时,还会获取一个单调递增的epoch。由于旧的producer的epoch比新producer的epoch小,kafka可以很容易识别出该producer是老的producer并拒绝其请求。
有了transaction id
后,kafka可保证:
transaction id
的新的producer实例被创建且工作时,旧的且拥有相同transaction id
的producer将不再工作。这一节所说的事务主要指原子性,也即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类似。
许多基于kafka的应用,尤其是kafka stream应用中同时包含consumer和producer,前者负责从kafka中获取消息,后者负责将处理完的数据写回kafka的其它topic中。
为了实现该场景下的事务的原子性,kafka需要保证对consumer offset的commit与producer对发送消息的commit包含在同一个事务中。否则,如果在二者commit中间发生异常,根据二者commit的顺序可能会造成数据丢失和数据重复:
at least once
语义,可能造成数据重复。at most once
语义,可能造成数据丢失。pid
与sequence number
的引入实现了写操作的幂等性at least once
语义实现了单一session内的exactly once
语义transaction marker
与pid
提供了识别消息是否应该被读取的能力,从而实现了事务的隔离性transaction marker
)来实现事务中涉及的所有读写操作同时对外可见或同时对外不可见出处:
如对本文有疑问, 点击进行留言回复!!
ScrollView和RecyclerView的滑动事件处理
配置JAVA环境+安装Android Studio全过程+踩坑记录
Android P Camera2当SD卡被拔出来自动切换到内部存储
android 多个edittext 判空监听 让Button动态是否可点击
Android开源项目滚轮选择器WheelPicker的基本用法总结
网友评论