4.1 消息传递三种语义
4.1.1 消费者
- at most once: 消费者fetch消息,然后保存offset,然后处理消息。当消费者保存offset之后,但是在消息处理过程中出现了异常,导致部分消息未能继续处理。那么此后“未处理”的消息将不能被重新fetch到,所以存在丢失数据的情况。想实现“至多一次”的做法是:设置消费者自动提交偏移量,并且设置较小的时间间隔。
- at least once: 消费者fetch消息,然后处理消息,然后保存offset。如果消息处理成功之后,在保存offset阶段,发生异常导致保存操作未能执行成功,这就导致接下来再次fetch时可能获得上次已经处理过的消息。想实现“至少一次”的做法是:设置消费者自动提交偏移量,但设置很长的时间间隔(或者关闭自动提交偏移量),在处理完消息后,手动调用同步模式提交偏移量consumer.commitSync
- exactly once,需要将消费者的消费进度和处理结果保存在同一介质中,再将这两个操作封装进一个原子性操作。
为了保证消息不丢失,通常情况下,选用at least once。
4.1.2 生产者
kafka的ack机制
- 0:producer不等待broker的ack,broker一接收到还没有写入磁盘就已经返回,当broker故障时有可能丢失数据;
- 1:producer等待broker的ack,partition的leader落盘成功后返回ack,如果在follower同步成功之前leader故障,那么将会丢失数据;
- -1:producer等待broker的ack,partition的leader和follower全部落盘成功后才返回ack,数据一般不会丢失,延迟时间长但是可靠性高。
4.1.3 Broker配置
Broker有三个可以影响到Kafka的消息可靠性存储的设置。
- 默认复制因子
default.replication.factor
,设置更高的复制因子意味着更多的空间换可靠性,会牺牲更多的磁盘空间,还会导致在集群活跃节点少于因子数量的时候不能提供服务,不建议进行设置,而建议创建topic时根据数据重要程度指定副本数量。 - 最少同步副本数:
min.insync.replicas
,默认是1,若保证数据更高可靠性,建议设置为2,在副本数大于等于2个的时候,能避免数据处于只有一个节点活跃的状态,而必须等待有另外一个broker恢复可用时才可以继续进行数据传输。 - Unclean leader选举,建议将
unclean.leader.election.enable
属性为false,避免非同步副本变为leader,导致数据丢失和不一致。非同步副本中的消息远远落后于leader,如果选举这种副本作为leader可能会造成数据丢失,但会保证服务继续可用。
4.2 Kafka恢复与备份机制
kafka把每个parition的消息复制到多个broker上,任何一个parition都有一个leader和多个follower,备份个数可以在创建topic的时候指定。leader负责处理所有read/write请求,follower像consumer一样从leader接收消息并把消息存储在log文件中。leader还负责跟踪所有的follower状态,如果follower “落后”太多或失效,leader将会把它从replicas同步列表中删除。当所有的follower都将一条消息保存成功,此消息才被认为是“committed”。
4.3 Kafka与zookeeper的交互机制
当一个kafka broker启动后,会向zookeeper注册自己的节点信息,当broker和zookeeper断开链接时,zookeeper也会删除该节点的信息。除了自身的信息,broker也会向zookeeper注册自己持有的topic和partitions信息。
当一个consumer被创建时,会向zookeeper注册自己的信息,此作用主要是为了“负载均衡”。一个group中的多个consumer可以交错的消费一个topic的所有partitions。简而言之,保证此topic的所有partitions都能被此group所消费,为了性能的考虑,让partition相对均衡的分撒到每个consumer上。每一个consumer都有一个唯一的ID(host:uuid,可以通过配置文件指定,也可以由系统生成),此ID用来标记消费者信息,主要是topic+partition信息。
Producer端使用zookeeper用来“发现”broker列表,以及和Topic下每个partition leader建立socket连接并发送消息。
zookeeper上还存放partition被哪个consumer所消费的信息,以及每个consumer目前所消费的partition中的最大offset(0.9.0.0版本之前)。
在kafka 0.9版本之后,kafka为了减少与zookeeper的交互,减少网络数据传输,自己实现了在kafka server上存储consumer消费的topic,partitions,offset信息。
4.4 Kafka metircs
对kafka的metrics主要是对lags的分析,lags是topic/partition的logSize与consumer消费到的offset之间的差值,即producer产生数据的量与consumer消费数据的量的差值,差值越来越大,说明消费数据的速度小于产生数据的速度。一般可以认定是consumer出了问题。当然也不能只看某一lags大小,更重要的是关注lags的变化的趋势,当趋势越来越大时,可推断consumer的performance越来越差。
在kafka 0.8.1版本之后,可以通过配置把topic/partition的logsize,offset等信息存储在zookeeper上或存储在kafka server上。在做metrics时,注意可能需要分别从两边获取数据。
获取zookeeper上的kafka数据比较简单,可以通过SimpleConsumer配合zookeeper.getChildren方法获取consumerGroup, topic, paritions信息,然后通过SimpleConsumer的getOffsetsBefore方法获取logSize,fetchOffsets获取topic parition的currentOffsets。
获取kafka server上的数据比较麻烦,目前kafka 0.10提供的kafkaConsumer类主要还是关注topic消费,对consumerGroup及Group和topic关系的获取,还没有提供API。不过可以通过kafka-consumer-groups.sh得到group,topic等信息,这个shell文件里面调用了kafka.admin.ConsumerGroupCommand类,这个类确实提供了一个listGroup方法,可惜这个方法的返回值是void,shell文件的输出是打印到控制台的,并没有返回值。再去研究ConsumerGroupCommand是怎么拿到group的,发现它通过AdminClient对象的listAllConsumerGroup获取的group list,所以只要new出来一个AdminClient就能解决问题。
private static AdminClient getAdminClient(){
if(null != adminClient){
return adminClient;
}else{
Time time = new SystemTime(