一、背景
在分布式系统中,zookeeper可以作为服务注册中心,所有提供服务的节点都可以在zookeeper上面注册,并作为一个node被组织起来,如下图:
在RPC框架中,这些服务提供者就是RPC服务的提供者。zookeeper注册中心为每个服务都维持了会话session。为了监测这些服务是否在线,还使用了心跳机制。
对于zookeeper来说,这些RPC服务的提供者就是zookeeper客户端。
网上很多人说zookeeper的配置文件里的第一个参数ticktime
就是指的心跳间隔,如下图,ticktime = 2000
,即2000毫秒。但实际上,通过tcpdump抓包分析和源码阅读,可以看到其实并不是这样的。
二、抓包
zk的服务端和客户端之间是用ping消息来作为心跳消息的。ping是应用层协议,下层直接就是网络层协议ICMP,(ICMP利用的是IP协议传输消息,很有趣,网络层依赖网络层),没有使用TCP或UDP,也就是说,ZK客户端和服务器即使在TCP三次握手或挥手过程出现了问题,也不影响心跳消息的收发。
在RPC项目中,RPC服务提供者就是zk的客户端,所以我们首先运行RPC服务提供者程序。
然后ifconfig
查看网络配置,因为都在本地运行,所以ip地址为回环地址127.0.0.1,网卡名为lo。
然后使用linux提供的抓包工具tcpdump,运行sudo tcpdump -i lo port 2181
。
(-i后接参数,lo是本地网卡名;port后的2181是zk服务器的端口号)
抓包结果如下:
可以看到,ping消息的时间是14:31:19、14:31:29、14:31:39、14:31:49、14:31:59、
也就是间隔10秒发送一次ping消息。
而我们在zookeeper客户端程序初始化zookeeper句柄时,调用函数zookeeper_init()传入的会话超时时间参数是30000,即30秒(这个也是zk默认的会话超时时间),也就是说,zk发送心跳消息的默认时间间隔是默认会话超时时间的1/3,即10000ms
。
其实查看zk的客户端发送ping请求的源码class SendThread也能找到发送ping的时间间隔,部分代码如下:
class SendThread extends ZooKeeperThread{
......
public void run(){
......
final int MAX_SEND_PING_INTERVAL = 10000;
......
}
}
对于前面说的配置文件里的ticktime
,是这样解释的:
Client端的ping心跳检测间隔时间是轮询隔一段时间后向Server端发送ping请求,而Server端的tickTime间隔时间作用是每隔一段时间就判断在Server端的Client连接对象是否已经死亡,如果已经过期死亡则将连接对象进行清除关闭。所以ping心跳检测的意义是Client端告诉服务器我还活着,tickTime意义是定期清除没有告诉Server端还存活的连接。