Zookeeper watch倾斜?

记一次Zookeeper生产问题定位

新手向,大佬莫搞

1.问题背景

了解Druid想必都知道Druid对Zookeeper是强依赖,Druid的任务调度,数据负载均衡等等都是组件服务之间通过Zookeeper通信实现的。为此,在生产环境上我们也自运维了一个Zookeeper集群,专门用来为Druid服务。相应的,我们也部署了一些监控脚本,以实时关注Zookeeper集群的健康状态。今天的这个问题也是通过这些告警信息反映出来的。
问题的主要现象是,Zookeeper集群会偶尔告警,ZNode超过阈值,ZWatch超过阈值。这个情况之前也有出现过,但是因为部门这边没有人深入的了解过Zookeeper组件,加之这个阈值不会持续,所以这个问题的研究虽然提上了日程,但是迟迟没有花时间去解决。最近这方面的告警愈发的频繁了,考虑到我们Druid集群的数据量越来越大,出于对组件服务稳定性和服务能力的保障,我们决定对这个问题进行一次“深入”地研究。

2. 动手之前的疑惑...

即使没有用过Zookeeper,相信大家也都应该了解它是干嘛的。它是一个开源的分布式协调服务,主要用于解决分布式场景下数据一致性问题。 那么对于它的客户端而言,看到的数据应该是一致的,那么既然是一致的,为什么我们的告警只有那么一两台显示ZNode和ZWatch超过阈值呢?呵!莫非我的认知是错误的?嗯,得验证!
所以我的第一个目标,确认一个问题,ZNode和ZWatch在各个服务节点上都一致?

3. 查看集群指标

目前Zookeeper集群的监控信息获取大致有是三个途径:

  • 四字命令:一些由四个字母组成的命令,可以通过socket发送给zookeeper服务器以返回需要的指标信息,例如我们关心的znode和zwatch数量,客户端连接数等等;
  • JMX: 可以通过jdk自带的jconsle查看服务进程的相关信息;
  • AdminServer: 在zookeeper 3.5.0中,Zookeeper内嵌了jetty服务,以提供http接口给管理员。遗憾的是我们的生产集群用的是zookeeper 3.4.6,所以,我全程没用这个东西。 JMX 我也没用,因为我们出现的问题跟节点压力应该无关,所以进程的一些信息只是通过简单的java自带命令行工具查看了。

3.1 部分四字命令

下面是定位过程中会用到的一些四字命令说明:

命令 作用
mntr 这个命令一般是用户集群监控用的,返回集群大部分的指标信息,感觉从每个命令中抽出一部分指标组成返回的结果
stat 返回Zookeeper服务节点的一些状态信息和客户端的连接状况
srvr 返回zookeeper服务节点的指标信息,跟stat相似,但是没它那么详细,但是看起来更加简洁
ruok 检测zookeeper指定的zookeeper服务器是否能够正常服务,如果服务正常就会返回imok。值得这个只是检测单个节点的服务,即使得到结果是imok也不能表名这个节点在集群中能够提供正常服务。imok只能表明它作为单个zookeeper服务是OK的。
wchs 查看节点watch数量

以下是每个命令返回的结果展示:

  • Mntr


    Zookeeper watch倾斜?_第1张图片
    Mntr
  • Stat


    Zookeeper watch倾斜?_第2张图片
    Stat
  • Srvr


    Zookeeper watch倾斜?_第3张图片
    Srvr
  • Ruok


    Ruok
  • Wchs


    Wchs

3.2 Zxid和Mode

上一节的一些命令都会带有这两个属性,这里想对他们做稍微详细一点的说明。
Mode
在Zookeeper集群中,服务的角色类型一共有三种,分别是Leader,FollowerObserver。其中三个节点都可以为客户端提供读服务,但是只有Leader能够提供写服务。而Follower和Observer的区别在于Observer参与Zookeeper集群的Leader选举和"过半写成功"机制。所以Observer能够在影响集群的其他性能的情况下,提升集群的读性能。

Zxid
作为分布式协调服务,Zookeeper实现了顺序一致性。顺序一致性要求Zookeeper对于客户端请求的事务操作要严格按照先后顺序执行,而且不同的事务之间本就可能有先后顺序的依赖关系。例如C事务的执行和提交需要A,B事务的完成。
ZAB(Zookeeper原子广播协议)的事务提交过程类似于二阶段提交,其消息广播协议具有FIFO特性,这样可以确保事务消息在网络广播的发送和接受过程中具有顺序性。在广播的过程中,Zookeeper的leader会根据客户端的事务请求生成一个Proposal,同时为这个Proposal生成一个全局单调递增的Zxid(事务ID)。Zxid是一个64位数,其中低32位表示的是事务操作顺序。高32位可以的集群Leader的"朝代",即epoch,也是递增的,每次有新的leader产生,它就会从本地的事务日志中取出最大的Zxid,解析出到高32位,并进行加1操作,之后再将低32位置为0。它表示了leader的更迭,当然还有其他的用处,这里与我们需要定位的问题没有太大联系,不做赘述。
ZAB通过Zxid对Proposal进行排序与处理,以确保顺序一致性。Leader会为每个Follower维护一个FIFO的事务队列,确保每个事务都是按照该Zxid递增处理的。
所以我们可以通过Zxid判断集群之前的数据同步等相关的操作是否有异常。

四. 问题分析定位流程

  1. 查看每个节点的Znode和Zwatch
    通过命令echo mntr | nc 查看了每个Zookeeper节点的状态,发现不同的节点之间Znode的数量基本一致,5台节点中有3台的ZWatch在75W左右。但是,其中有2台节点的ZWatch数量比其他的节点多了10W,在85W左右。
    至此,我们大概可以可以得出结论,ZNode在不同的节点之间都是一样的,但是ZWatch不一定。

但是为什么呢?为什么ZNode在每个节点之间都是一样的,而ZWatch不是?

其实对于前者,我们很好理解。因为Zookeeper保证了单一视图,即无论client连接的是哪个节点 ,他们看到的数据都是一样的(无法确保每时每刻每个客户端看到的视图都是一样的,但是一段时间后最终是一样的)。所以每个节点的数据,这里就是ZNode都是一样的。
而ZWatch作为Zookeeper客户端对服务端添加的一个监听。并不是为了实现单一视图,实际上真正关心这个ZWatch的只有它的创建者,即创建这个Watch的客户端。客户端并不会关心别人创建的ZWatch,同时它们也没有这些ZWatch的回调函数。
查看了一下Zookeeper的客户端代码,就会发现,实际上客户端创建的ZWatch是他们自己通过WatchManager维护的。也就是说ZWatch的数量是由客户端决定的,客户端创建的ZWatch比较多,那么他连接的服务端ZWatch就会多一些。

  1. 确认客户端问题
    在第一步中我们推断出是因为客户端导致的ZWatch数量有差距,但是为什么只有两台比其他的节点多,而其他的节点又比较均衡呢?
    这里我猜测有两个原因:其一,如果每个客户端创建的ZWatch数量相差不多的情况下,客户端连接倾斜的话,则有可能导致ZWatch的倾斜;其二,如果客户端连接没有倾斜的话,那么某些客户端创建的ZWatch数量比较多的话,也会导致其连接的服务端ZWatch倾斜。

针对以上两个猜想,我开始一一验证。首先是客户端连接是否会倾斜?
我们平时编码Zookeeper客户端时都是把服务器列表作为构造参数传递进去的。所以这一块我追了一下Zookeeper客户端的源码,想看看他是怎么处理这个列表的。这一看,透透的!
在创建Zookeeper客户端的时候,ZK会把我们host列表封装到HostProvider对象中,在这个对象中他会把我们的host放到一个列表中,并对这个列表进行shuffle,如下:

Zookeeper watch倾斜?_第4张图片
^_^

之后客户端就会按照这个shuffle后的顺序连接服务端,连接断开时也会拿列表中的下一个host进行连接。即使是同一个list,shuffle后的顺序也不一定是一样的。这就确保了只要服务端地址的配置是一样的,那么每个客户端的连接都是随机的,基本上不会有连接倾斜的情况发生。

从上面的分析我们可以看到,只要配置的服务端host是一致的,客户端基本上不会出现连接倾斜的情况。那么我们就需要验证第二种猜想了,说是猜想,其实从我们上面的分析来看,如果某些客户端创建的ZWatch数量比较多的话,肯定是会导致其连接的服务端ZWatch倾斜。我要做的其实就是找出这些客户端。
我们再回头看看问题。Druid作为Zookeeper的客户端,其每个节点服务都是Zookeeper的客户端。首先是historical节点,生产上historical节点数量远大于5(Zookeeper集群节点数量),所以相对而言,这些节点的连接都会相对均衡一些,即便是有差距,也是在很小的区间内波动。同理,MiddleManger和Broker的数量也是远大于5的。
与他们不同的就是Druid的主节点了,目前生产上的主节点采用的是HA,而Druid的主节点我们可以理解有两个,一个是Coordinator服务,Overlord服务。备用节点不考虑的话,也就是说我们会有2个特殊的客户端。
哎!!!好像跟ZK集群的情况对应上了。是不是这两个服务导致的ZWatch分布不均衡。那我们就得先找到这个两个客户端连接的Zookeeper服务器。
0X01
首先查看主节点的进程,注意要是主节点。jps -ml

主节点

获得主节点进程号。
0X02
查看主节点监听的端口号(ZK客户端) netstat -anp |grep
Zookeeper watch倾斜?_第5张图片
找服务器

这就找到了主节点连接的Zookeeper服务器,另一个主节点同理。
这样就确认了2台Zookeeper服务端。通过 srvr命令查看各个节点Watch数量。果然,这两个节点的ZWatch数量比其他三台要多。与预期相符!

小结:

不结了!就跟上面说的一样,Zookeeper服务在大部分场景下还是比较稳定的。在高并发场景下可能出现问题(反正目前我们是没有遇到过),这种情况下可能需要考虑扩容,或者增加Observer节点。再者,也有可能出现数据同步的问题(我们也没遇到过^ _ ^),可以试试跟官网学习,停服务,删除所有的snapshot文件和transactionLog,重启!
还有就是Zookeeper源码导入和编译测试。需要用ant转成Eclipse的项目才能导入到IDEA,我也不会用ant,网上也有一些东西,以后要是遇到这个问题,可以搜搜,谨记!

你可能感兴趣的:(Zookeeper watch倾斜?)