更新时间:2022-08-13 17:15:11
笔者在一个Websocket中间件产品(Apush,https://market.aliyun.com/products/56928004/cmapi020699.html#sku=yuncode1469900000)的集群管理中使用了zk的EPHEMERAL节点机制。
在编码过程中发现很多可能存在的陷阱,毛估估,第一次使用zk来实现集群管理的人应该有80%以上会掉坑,有些坑比较隐蔽,在网络问题或者异常的场景时才会出现,可能很长一段时间才会暴露出来。
此坑不深,稍微注意一下还是容易发现的,并且采用Curator会减少此类问题的发生,不是完全避免,具体见第6个坑。
zk客户端如果和某台zk服务器断开,会主动尝试与zk集群中其他服务器重新连接,直到sessiontimeout,需要考虑极端的情况下出现sessiontimeout的处理。
zk客户端和zk服务器断开时会收到state为Disconnected的连接事件,此事件一般可以不处理,此事件后续会跟Expired状态的连接事件或者synconnected状态的连接事件。
zk客户端连接重试失败并且达到sessiontimeout时间则会收到Expired状态的连接事件,在此事件中应该由应用程序重试建立zk客户端。
此坑是个深坑,很隐蔽,而且没看到文章来提醒此坑。一般也不会出现问题,除非服务异常终止后立即重启。
一般我们会synconnected状态的连接事件中创建EPHEMERAL节点,注册watch。
synconnected状态的连接事件中处理EPHEMERAL节点可以分三种场景:
1、在第一次连接建立时
2、在断开连接后,sessiontimeout以前客户端自动重连成功
3、老的客户端没有正常调用close进行关闭,并且在此客户端sessiontimeout以前,创建了一个新的客户端
先说明一下第3种场景,session是否过期是由server判断的,如果客户不是调用close来和服务器主动断开,服务端会等客户端重连,直到session timeout。因此可能出现老session未过期,新客户端来建新session的情况。
在第2和第3种场景下,EPHEMERAL节点都会在服务端存在。
第3种场景下,随着残留在zk服务端session的timeout,老的EPHEMERAL节点会被自动删除。
由于zk的每个session都产生一个新的sessionId,为了区分第2、3种场景,必须在每次synconnected状态的连接事件中比较当前sessionId和上次sessionId。
在synconnected状态的连接事件中要同时判断sessionId是否变化以及EPHEMERAL节点是否已经存在。
对sessionId发生了变化且EPHEMERAL节点已经存在的情况要先删除后重建,这个是使用Curator也避免不了的。
原因同第2条,会导致EPHEMERAL节点在sessiontimeout之前都存在。
如果sessiontimeout时间很长的话,会导致整个集群的可用服务列表长时间包含已关闭的服务器。
正确的做法是在synconnected状态的连接事件中进行连接后的处理或者阻塞线程在连接事件中通知取消阻塞。
Curator提供了连接时同步阻塞的功能,可以避免此问题。
所有的zk事件都在EventThread中按顺序执行,如果一个事件长时间执行会导致其他事件无法及时响应。
这个坑真不知道Curator是怎么想的,文档里一般也没提到这个坑。重试次数不够导致机房断网测试时zk的客户端可能永久丢失连接,据说新版本里已经增加了ForeverRetry。最后我放弃了Curator回到原生zk客户端。