Kafka & Zookeeper

Zookeeper作为分布式配置中心,为很多大型分布式系统提供分布式配置,集群管理。然而Zookeeper不是万能的,如果写入数据量过大,或者值修改过于频繁,则Zookeeper可能会成为瓶颈,下面从Kafka & Zookeeper优化谈一谈Zookeeper使用的注意事项。


Kafka依赖Zookeeper遇到的问题

在早起版本的Kafka中,分布式的数据共享基本都依赖于Zookeeper实现,在Zookeeper中,Kafka存储了以下数据:

  • topic的注册信息
  • partition的注册信息
  • broker的注册信息,副本信息,ISR集合,leader_epoch等信息
  • consumer的注册信息
  • consumer对应的分区信息
  • consumer offset 消费者位移情况
  • 其他配置信息

Kafka中,ZK不仅仅用来作为配置中心,其提供的watch机制也为Kafka提供了便捷的监控功能,比如:

  • consumer节点下,当consumer节点列表变化的时候,也会立即触发再均衡,重新分配分区关系
  • controller leader选举,当leader挂掉后,能立即争抢创建临时节点,创建成功的broker作为leader
  • 监听主题和分区分配变化

然而,如果在一个大型业务系统中,或则Kafka作为一个PAAS服务运行的时候,可能会存在消费者数量非常多的时候,此时Zookeeper的缺点则可能成为Kafka的性能瓶颈,比如:

  • 太多的consumer监听节点会导致惊群效应
  • 频繁的更新Zookeeper节点数据,可能会有性能问题,比如consumer offset存放在ZK
  • 脑裂问题: ZK的弱一致性和强一致性问题与性能的平衡,ZK follower可能保存的是过期的数据,过期的offset会读取到重复的数据,想要获取最新数据需要调用sync,会拉低ZK的性能。

因此,在新版Kafka中,针对consumer的管理,kafka新增了 GroupCoordinator来对consumer进行管理,GroupCoordinator功能如下:

  • 通过_consumer_offset主题管理consumer offset
  • 通过自己维护心跳管理consumer
  • 通过管理consumer来实现再均衡

GroupCoordinator 由管理此group_consumer_offset_主题对应的Leader分区所在的Broker节点管理。_consumer_offset_Kafka的内置主题,用来存储消费者的消费位移,默认有50个分区,3个副本。通过group.id % 50就能找到对应的Broker.

通过GroupCoordinatorKafka实现了自己维护消费者心跳,管理消费者分区方案,管理消费者再均衡等操作。

同时,为了解决频繁写入Zookeeper消费者位移问题,GroupCoordinator还负责将消费者元数据存储在_consumer_offset_主题中。在2.0.0版本中,_consumer_offset_存放时间为1天(之前默认为7天),也就是一天后,_consumer_offset保存的信息就会消失。

通过以上这两个方案,KafkaconsumerZK完全拆分出来,解决ZK所带来的问题。

为什么Kafka能这么做?

分布式协调无法就三点问题:

  • 选主
  • 数据同步
  • 服务监控

对于消费者来说,没有选主的需求,因为挂了之后会再均衡,而通过Kafka Leader Broker担任的GroupCoordinator 能够很方便的进行再均衡操作,同时。kafka将再均衡信息存储在Consumer Leader上面,因此Broker挂掉并不需要从新同步信息,而Consumer挂掉势必会再次触发再均衡,因此也避免了信息同步的问题。

再一个服务监控,GroupCoordinator 通过心跳维护Consumer健康信息,原理和ZK一样,只是自己实现了一遍而已。

最后便是存储offset信息,Kafka自己作为MQ,本身就有存储的功能,因此将offset存储在Topic中也是一个很好的选择,_consumer_offset默认50个分区,3个副本,可以很方便的负载均衡。

这里一定要注意,在2.0.0版本中,offset只会保存一天,如果配置了AUTO_OFFSET_RESET_CONFIGlatest,那么可能会造成丢失数据的风险。


Zookeeper 的使用场景

由此可以看到ZK并不是一个分布式数据管理的银弹,它有适合的场景,但是肯定不是所有场景,这也说明,使用ZK需要了解其原理,分析其优点与缺点,避免采坑,接下来分析下ZK的设计理念。

ZooKeeper 最早起源于雅虎研究院的一个研究小组。当时在雅虎内部很多大型系统基本都需要依赖一个类似的系统来进行分布式协调,但是这些系统往往都存在分布式单点问题,Zookeeper的出来就是为了解决分布式协调的最后一步,使得其他系统只要依赖Zookeeper就能完成分布式协调功能。

最开始Zookeeper的设计,主要应用在以下两个场景:

  • 分布式配置
  • 集群管理(主备)

Zookeeper的实现中,使用ZAB协议同步数据,但是有以下值得注意的点:

  • ZK每次事务默认都是先落盘(FileChannel.force()),然后再返回确认,因此,如果IO数据特别大或者特别频繁,则可能出现IO瓶颈
  • ZK中每次同步数据只要超过过半Follower即可返回成功,同时Follower也可以处理读请求,因此返回的数据可能是过期的数据。(同一个客户端不会读取到过期的数据)
  • ZK的通知机制没有任何业务条件,因此如果监听的watcher过多,则可能出现羊群效应。
  • ZK满足的是CAP中的CP,也就是说ZKLeader崩溃的时候无法写数据,并且ZK的选举需要选举和同步两个方面,并且选举有固定延迟200ms

对于以上三点,传统的分布式业务,一般没有什么影响,比如:

  • 分布式配置,因为配置修改的不频繁,因此可以忽略IO的问题,同时过期数据最终还是能够读取到,因此问题也不大,最后羊群效应可以通过顺序节点或者其业务本身解决,比如细化监听粒度等。。
  • 集群管理,同分布式配置一样,因为不会修改频繁数据,因此IO不是问题,同时只要能最终同步到数据,也不会出现业务的影响,而主从结构,从的数量一半还不会达到惊群的数量,同时也可以通过业务方面解决。

这些都是ZK适合的业务,但是,在后来的经验中,发现ZK不适合以下情况:

  • 比如前面说到的Kafka通过ZK同步消费者offset,由于kafka的消费者数量一般会比较多,可以简单计算,一般一个分区就会对应一个消费者,同时一个Topic下面会有多个Group,也就是消费者数量约等于 topic * group * partition 数量基本上很大,再加上消息不断的生产,然后不断的消费,offset会频繁的改动,此时ZKIO就有可能成为Kafka的瓶颈。

  • 再比如注册中心

    大多数都知道ZK广义上来说满足的CP,同时在选举期间服务不可写,同时ZK的选举时间比较长,因此最好不要使用ZK来做注册中心。

总结

这篇文章是在梳理kafka各个版本差异之后写的,当时发现Kafka放弃使用ZK管理consumer,觉得我们应该从这一次更新同样得到教训,所以简单的梳理了下ZKKafka的设计Linkedin公司都会踩的坑,也不知道会有多少人会注意到这个问题。

突然想到就写的文章,思路比较乱。后续有时间再整理 -2021/04/14