前言
和我断断续续合作过快一年半的同事要离职啦,跟他一起处理过很多Zookeeper
的问题,作文以记之。转载请注明出处。
华为云设备接入服务是面向多租户的服务。架构为微服务化结构。
使用Zookeeper的使用场景
image-20210410093126548分布式缓存清理
为了更快地响应,我们将一些不常变更的元数据信息做了内存缓存,在我们还没有做业务配置中心之前,缺乏通知能力,这部分是通过Zookeeper
来实时通知缓存的清理和刷新的
实现一致性哈希算法
用户数量大的情况下,部分业务很难由一个微服务实例承接(如海量用户的证书加载等),通过一致性哈希算法或类似的算法来将业务均分到不同的微服务实例上。
严格分布式锁
用户开户,注册设备的时候,需要保证严格不重复,通过Zookeeper
的强一致性,来实现分布式锁。
自建Pulsar依赖Zookeeper
很多开源软件都依赖Zookeeper
,如Flink
、Hadoop
、Pulsar
等,我们自建Zookeeper
集群来满足开源组件对Zookeeper
的依赖
微服务注册中心
相比其他微服务引擎,如华为云的ServiceStage
,阿里云的MSE
,已有的Zookeeper
集群作为微服务的注册中心,既能满足微服务数量较少时的功能需求,并且更加节约成本
数据库连接均衡
image-20210410100426706之前,我们微服务连接数据库的地址的策略是随机选择,上面是我随便画了一个随机可能造成的场景,我们假设有微服务B和微服务C,微服务C实例虽多,但对数据库的操作并不多。微服务B在运行期间,操作数据库更加频繁。上图的连接方式就会造成数据库Data2节点的连接数和CPU居高不下,成为集群的瓶颈。我们的灵感来自于Kafka
中partition
的分配算法,微服务B1连接了Data1和Data2,那么B2就去连Data3和Data4,如果还有B3,就再去连Data1和Data2节点。微服务C1的连接,再从Data1和Data2节点开始。但是因为微服务的数量和数据库实例的数量乘以2(因为每个微服务建立2个连接)并不一定整除,可能会造成Data1节点和Data2节点负载不均衡。所以我们在这个的基础上,加上第一个微服务实例选择数据库节点,从随机起点出发,这样子也保证了Data1节点和Data2节点的均衡。如下图
部署
服务端部署方式
我们所有微服务和中间件均采用容器化部署,个数选择3节点(没有learner)规格。使用statefulset搭配pvc即公有云上的云硬盘进行部署。为什么使用statefulset进行部署?statefulset非常适合用于像Zookeeper这样有持久化存储需求的服务,每个Pod可以和对应的存储资源绑定,保证数据的持久化,同时也简化了部署,如果想使用deploy的部署模式,需要规划、固定每个pod的虚拟机部署。Zookeeper
本身对云硬盘的要求并不高,普通IO,几十G存储就已经能够支撑Zookeeper
平稳运行了。Zookeeper
本身运行的资源,使用量不是很大,在我们的场景,规格主要取决于Pulsar的topic数量,如果Pulsar
的topic不多,那么0.5核、2G内存已经能保证Zookeeper
平稳运行了。
客户端连接方式
客户端使用域名的方式进行连接,如zookeeper-0.zookeeper:2181,zookeeper-1.zookeeper:2181,zookeeper-2.zookeeper:2181
重要监控指标
-
readlantency、updatelantency
zk的读写延迟
-
approximate_data_size
zk中数据的平均大小估计
-
outstanding_requests
等待
Zookeeper
处理的请求数 -
znode_count
Zookeeper
当前的znode
总数 -
num_alive_connections
Zookeeper
当前活跃的连接数
碰到的问题
readiness合理设置
这是碰到的最有趣的问题,readiness接口是k8s判断pod是否正常的依据,那么对于Zookeeper集群来说,最合理的就是,当这个Zookeeper节点加入集群,获得了属于自己的Leader或Follower状态,就算pod正常。可是,当初次部署的时候,只有一个节点可用,该节点一个实例无法完成选举流程,导致无法部署。
综上,我们把readiness的策略修改为:
image-20210410111218429PS:为了让readiness检查不通过时,Zookeeper集群也能选主成功,需要配置publishNotReadyAddresses为true,示例如下
apiVersion: v1
kind: Service
metadata:
name: zookeeper
spec:
selector:
app: zookeeper
clusterIP: None
sessionAffinity: None
publishNotReadyAddresses: true
ports:
- protocol: TCP
port: 2181
name: client
- protocol: TCP
port: 2888
name: peer
- protocol: TCP
port: 3888
name: leader
jute.maxbuffer超过上限
jute.maxbuffer,这个是znode中存储数据大小的上限,在客户端和服务端都需要配置,根据自己在znode上存储的数据合理配置
zookeeper的Prometheus全0监听
不满足华为的安全要求,不满足网络监听最小可见原则。修改策略,添加一个可配置参数来配置监听的IP metricsProvider.httpHost
,提交PR,待合入 https://github.com/apache/zookeeper/pull/1574/files
客户端版本号过低,域名无法及时刷新
客户端使用域名进行连接,但在客户端版本号过低的情况下,客户端并不会刷新新的ip,还是会用旧的ip尝试连接。升级客户端版本号到curator-4.3.0、zookeeper-3.6.2版本后解决。
总结
Zookeeper对我们来说,目前还是一个不可替代的组件。Zookeeper本身在网上运行也很稳定,我们对现网的Zookeeper升级频率大约是半年一次。更多的工作是在上面做一些加固,满足安全规范、适配我们的部署等工作。
网友评论