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
.
通过GroupCoordinator
,Kafka
实现了自己维护消费者心跳,管理消费者分区方案,管理消费者再均衡等操作。
同时,为了解决频繁写入Zookeeper
消费者位移问题,GroupCoordinator
还负责将消费者元数据存储在_consumer_offset_
主题中。在2.0.0
版本中,_consumer_offset_
存放时间为1天(之前默认为7天),也就是一天后,_consumer_offset
保存的信息就会消失。
通过以上这两个方案,Kafka
将consumer
和ZK
完全拆分出来,解决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_CONFIG
为latest
,那么可能会造成丢失数据的风险。
Zookeeper 的使用场景
由此可以看到ZK
并不是一个分布式数据管理的银弹,它有适合的场景,但是肯定不是所有场景,这也说明,使用ZK
需要了解其原理,分析其优点与缺点,避免采坑,接下来分析下ZK
的设计理念。
ZooKeeper
最早起源于雅虎研究院的一个研究小组。当时在雅虎内部很多大型系统基本都需要依赖一个类似的系统来进行分布式协调,但是这些系统往往都存在分布式单点问题,Zookeeper
的出来就是为了解决分布式协调的最后一步,使得其他系统只要依赖Zookeeper
就能完成分布式协调功能。
最开始Zookeeper
的设计,主要应用在以下两个场景:
- 分布式配置
- 集群管理(主备)
在Zookeeper
的实现中,使用ZAB
协议同步数据,但是有以下值得注意的点:
ZK
每次事务默认都是先落盘(FileChannel.force()
),然后再返回确认,因此,如果IO
数据特别大或者特别频繁,则可能出现IO
瓶颈ZK
中每次同步数据只要超过过半Follower
即可返回成功,同时Follower
也可以处理读请求,因此返回的数据可能是过期的数据。(同一个客户端不会读取到过期的数据)ZK
的通知机制没有任何业务条件,因此如果监听的watcher
过多,则可能出现羊群效应。ZK
满足的是CAP
中的CP
,也就是说ZK
在Leader
崩溃的时候无法写数据,并且ZK
的选举需要选举和同步两个方面,并且选举有固定延迟200ms
。
对于以上三点,传统的分布式业务,一般没有什么影响,比如:
- 分布式配置,因为配置修改的不频繁,因此可以忽略
IO
的问题,同时过期数据最终还是能够读取到,因此问题也不大,最后羊群效应可以通过顺序节点或者其业务本身解决,比如细化监听粒度等。。 - 集群管理,同分布式配置一样,因为不会修改频繁数据,因此
IO
不是问题,同时只要能最终同步到数据,也不会出现业务的影响,而主从结构,从的数量一半还不会达到惊群的数量,同时也可以通过业务方面解决。
这些都是ZK
适合的业务,但是,在后来的经验中,发现ZK
不适合以下情况:
- 比如前面说到的
Kafka
通过ZK
同步消费者offset
,由于kafka
的消费者数量一般会比较多,可以简单计算,一般一个分区就会对应一个消费者,同时一个Topic
下面会有多个Group
,也就是消费者数量约等于topic * group * partition
数量基本上很大,再加上消息不断的生产,然后不断的消费,offset
会频繁的改动,此时ZK
的IO
就有可能成为Kafka
的瓶颈。 -
再比如注册中心
大多数都知道
ZK
广义上来说满足的CP
,同时在选举期间服务不可写,同时ZK
的选举时间比较长,因此最好不要使用ZK
来做注册中心。
总结
这篇文章是在梳理kafka
各个版本差异之后写的,当时发现Kafka
放弃使用ZK
管理consumer
,觉得我们应该从这一次更新同样得到教训,所以简单的梳理了下ZK
和Kafka
的设计Linkedin
公司都会踩的坑,也不知道会有多少人会注意到这个问题。
突然想到就写的文章,思路比较乱。后续有时间再整理 -2021/04/14