1、Consumer Groups 提供了topics和partitions的隔离, 如上图Consumer Group A中的consumer-C2挂掉,consumer-C1会接收P1,P2,即一个consumer Group中有其他consumer挂掉后能够重新平衡。如下图:
2、多consumer并发消费消息时,容易导致消息乱序,通过限制消费者为同步,可以保证消息有序,但是这大大降低了程序的并发性。
kafka通过partition的概念,保证了partition内消息有序性,缓解了上面的问题。partition内消息会复制分发给所有分组,每个分组只有一个consumer能消费这条消息。这个语义保证了某个分组消费某个分区的消息,是同步而非并发的。如果一个topic只有一个partition,那么这个topic并发消费有序,否则只是单个partition有序。
一般消息系统,consumer存在两种消费模型:
push:优势在于消息实时性高。劣势在于没有考虑consumer消费能力和饱和情况,容易导致producer压垮consumer。
pull:优势在可以控制消费速度和消费数量,保证consumer不会出现饱和。劣势在于当没有数据,会出现空轮询,消耗cpu。
kafka采用pull,并采用可配置化参数保证当存在数据并且数据量达到一定量的时候,consumer端才进行pull操作,否则一直处于block状态。kakfa采用整数值consumer position来记录单个分区的消费状态,并且单个分区单个消息只能被consumer group内的一个consumer消费,维护简单开销小。消费完成,broker收到确认,position指向下次消费的offset。由于消息不会删除,在完成消费,position更新之后,consumer依然可以重置offset重新消费历史消息。
消息发送语义
producer视角
消息最多发送一次:producer异步发送消息,或者同步发消息但重试次数为0。
消息至少发送一次:producer同步发送消息,失败、超时都会重试。
消息发且仅发一次:后续版本支持。
consumer视角
消息最多消费一次:consumer先读取消息,再确认position,最后处理消息。
消息至少消费一次:consumer先读取消息,再处理消息,最后确认position。
消息消费且仅消费一次。
注意:
如果消息处理后的输出端(如db)能保证消息更新幂等性,则多次消费也能保证exactly once语义。
如果输出端能支持两阶段提交协议,则能保证确认position和处理输出消息同时成功或者同时失败。
在消息处理的输出端存储更新后的position,保证了确认position和处理输出消息的原子性(简单、通用)。
可用性
在kafka中,正常情况下所有node处于同步中状态,当某个node处于非同步中状态,也就意味着整个系统出问题,需要做容错处理。
同步中代表了:
该node与zookeeper能连通。
该node如果是follower,那么consumer position与leader不能差距太大(差额可配置)。
某个分区内同步中的node组成一个集合,即该分区的ISR。
kafka通过两个手段容错:
数据备份:以partition为单位备份,副本数可设置。当副本数为N时,代表1个leader,N-1个followers,followers可以视为leader的consumer,拉取leader的消息,append到自己的系统中
failover:
1. 当leader处于非同步中时,系统从followers中选举新leader
2. 当某个follower状态变为非同步中时,leader会将此follower剔除ISR,当此follower恢复并完成数据同步之后再次进入 ISR。
另外,kafka有个保障:当producer生产消息时,只有当消息被所有ISR确认时,才表示该消息提交成功。只有提交成功的消息,才能被consumer消费。
因此,当有N个副本时,N个副本都在ISR中,N-1个副本都出现异常时,系统依然能提供服务。
假设N副本全挂了,node恢复后会面临同步数据的过程,这期间ISR中没有node,会导致该分区服务不可用。kafka采用一种降级措施来处理:选举第一个恢复的node作为leader提供服务,以它的数据为基准,这个措施被称为脏leader选举。由于leader是主要提供服务的,kafka broker将多个partition的leader均分在不同的server上以均摊风险。每个parition都有leader,如果在每个partition内运行选主进程,那么会导致产生非常多选主进程。kakfa采用一种轻量级的方式:从broker集群中选出一个作为controller,这个controller监控挂掉的broker,为上面的分区批量选主。
一致性
上面的方案保证了数据高可用,有时高可用是体现在对一致性的牺牲上。如果希望达到强一致性,可以采取如下措施: