当前位置: 首页 > 后端技术 > Node.js

node-zookeeper-client的运行机制

时间:2023-04-03 13:55:44 Node.js

ZooKeeper(简称zk)是一个开源的分布式协调服务,常被用作分布式服务的注册中心。本文介绍的是Node.js的zksdk——node-zookeeper-client,先了解一下zk相关的基础知识。sessionId:客户端连接会话,默认30s后过期。xid:客户端发送的消息序号。当zk服务器响应时,xid是相同的。zxid:ZooKeeperTransactionId,用于保证zk的分布式一致性Jute:zk的底层序列化工具包:由header和payload组成,序列化后的buffer结构类似于[totallength,header,payload]useexamplesstartwithuseexamples/***xiedacon创建于2019-06-1410:07:46**Copyright(c)2019Souche.com,allrightsreserved.*/'usestrict';constZK=require('node-zookeeper-client');constutil=require('util');constclient=ZK.createClient('127.0.0.1:2181');client.getChildren=util.promisify(client.getChildren);(async()=>{console.log('Result:',awaitclient.getChildren('/xxx'));})().catch((err)=>{console.log(err);});client.connect();以上代码分为3步:调用ZK.createClient('127.0.0.1:2181'),创建zk客户端实例调用client.connect(),与zk服务器建立连接调用client.getChildren('/xxx'),获取/xxx节点下的所有子节点节点1.创建zk客户端zk客户端大致可以分为三部分:客户端:作为暴露API的adapt层,主要用于组装请求。对应文件index.jsconnectionManager:管理客户端与服务端的socket连接,以及相关事件,对应文件lib/connectionManager.jswatcherManager:管理客户端的watch,对应文件lib/watcherManager.js2。建立zk连接并调用client.connect(),在connect方法中调用connectionManager.connect(),将connectionManager状态设置为CONNECTING通过findNextServer()获取一个zk服务器地址作为socket绑定connect、data、drain、close,error事件如果tcp连接建立成功,则触发connect事件,执行onSocketConnected方法清除连接超时定时器。生成ConnectRequest并写入套接字。如果有authinfo,生成AuthPacket放入packetQueue。如果有watches,生成SetWatches,放入packetQueue。如果tcp连接建立失败,会分别触发error和close事件,并执行onSocketError和onSocketClosed方法,清除连接超时定时器。如果连接管理器未重新连接,则将连接管理器状态设置为CLOSED。如果zkserver允许client访问触发数据事件并执行onSocketData方法,此时connectionManager状态为CONNECTING。构造ConnectResponse并解析套接字数据。如果connectResponse.timeOut<=0,表示当前client的session过期:设置connectionManager状态为SESSION_EXPIRED,zkserver稍后会自动关闭socket,即触发5其他条件表示连接建立成功:设置相关数据,设置connectionManager状态为CONNECTED,主动触发socketdrain,发送阻塞请求,make数据流如果zkserver拒绝客户端访问zkserver不会返回任何信息,直接关闭socket,触发5PS:其实并没有触发socketdrain的代码,而是调用了onPacketQueueReadable方法,但是它们的效果是一样的。3、发送消息调用client.getChildren('/xxx')client生成对应的request结构,调用connectionManager.queue,将request放入packetQueue,同时触发socketdrain。在onPacketQueueReadable方法中,对于Auth、Ping等以外的外部请求,设置xid将请求序列化并写入socket,将请求放入pendingQueue。zkserver处理时,会返回处理结果,并触发data事件。此时connectionManager状态为CONECTED。构造一个ReplyHeader并解析套接字数据。如果xid是内部xid,则进行内部处理,否则比较pendingQueue头部请求的xid与当前xid是否一致。如果一致,则构造对应的Response,执行client.getChildren的回调方法。如果不一致,说明客户端出现不可预知的错误,直接关闭socket连接。PS:其实socket并不是触发了drain代码,而是触发了packetQueue的readable事件,但是它们的作用是一样的connectionManager状态机CONNECTING:zk连接正在建立中CONNECTED:zk连接已经建立DISCONNECTED:zk连接已经建立已断开连接CLOSED:zk客户端已关闭CLOSING:客户端关闭期间zkSESSION_EXPIRED:zk客户端会话过期,客户端无法处理,最终会导致客户端关闭AUTHENTICATION_FAILED:zkclientauthauthenticationfailed,客户端无法处理,最终会导致客户端关闭。node-zookeeper-client本身就提供了断线重连的能力。对于长时间断开(超过30s),重新建立连接后,当前客户端session已经过期,connectionManager会进入SESSION_EXPIRED状态。这个时候调用queue方法会抛出SESSION_EXPIRED错误这个问题在zookeeper的文档中也有提到:意思是client被分区掉了ZooKeeper服务超过了session超时,ZooKeeper决定client挂掉了。当客户端收到SESSION_EXPIRED错误时,表示当前客户端已经与ZooKeeper服务断开连接,即会话已经过期,ZooKeeper判断应该关闭当前客户端。如果客户端只是从ZooKeeper读取状态,恢复意味着只是重新连接。在更复杂的应用程序中,恢复意味着重新创建临时节点、争夺领导角色以及重建已发布的状态。如果客户端以只读模式连接到ZooKeeper,那么它只需要重新连接。但在更复杂的情况下,重连意味着重新创建临时节点,参与领导者竞争,重建发布状态。所以node-zookeeper-client的做法也是合理的。当session过期时,直接禁止发送。由调用程序决定是否重连以及重连后做什么。一个简单的客户端连接到zookeeper服务,不会参与zk选举。因此,它最需要做的就是恢复临时节点解决方案。目前的解决方案是session过期自动重连。重连成功后会进入CONNECTED状态并触发connect事件,在事件中重新注册临时节点发现session已经过期,直接重置sessionId,下次重连时会重新获取sessionId发生。当套接字关闭时,pendingQueue中的数据包被移动到packetQueue的头部,以避免丢包。直接从socket发送不经过packetQueue,避免阻塞,如AuthPacket、SetWatches、Pingzk。重连不能重启的原因是在zk集群中,不同的服务节点使用ZAB协议保证分布式一致性。zxid(ZooKeeperTransactionId)是zk用来保证分布式一致性的标识符。直接挂掉或者手动重启都会导致集群zxid被重置。当客户端重连时,由于客户端自己存储的xzid大于zk集群的xzid,此时zk集群会拒绝客户端连接。外部表现是connectionManager疯狂执行connect操作,所有调用都被阻塞,zk日志输出Refusingsessionrequestforclient/192.168.0.1:33400asithasseenzxid0x300000012ourlastzxidis0x0clientmusttryanotherserver虽然zk集群判断zxid有Error,但是直接断开连接的方式确实是要考虑的。因为这样的话,客户端根本不知道发生了什么,只能通过重连来解决,但是重连是连接不上的,这样就形成了死循环的解决方案。由于客户端不需要关心zk集群版本,因此每次连接时直接重新设置zxid即可。相关链接https://github.com/alexguan/n...https://github.com/apache/zoo...https://github.com/apache/zoo...https://www.cnblogs.com/leesf...https://wiki.apache.org/hadoo...https://zhuanlan.zhihu.com/p/。..https://www.cnblogs.com/luxia...https://issues.apache.org/jir...