/zkclient

zookeeper sdk wrapper written in c++

Primary LanguageC++

zkclient

c++ wrapper for libzookeeper async api

保留了一些assert, 用于在生产环境中发现一些没有预期到的state/event,不过从zookeeper c client源码来看,其他未assert的状态不应出现。

搞清楚zookeeper client的一些细节,需要自己看zookeeper client源码,重点关注几个问题:

  • 1,async接口回调失败错误码,watch是否可能生效? 答:否,async接口的watch虽然提交给了server,但最终决定是否生效的决定点在于response里的错误码,只有response里标记ZOK,此时才会active这个watch。 (看源码,关注activateWatcher这个函数)
  • 2,watch通知session event怎么处理? 答:整个zhandle上的watch在会话(和zk的连接)出现问题的情况下,都会被通知session event的,我们只需要关注zookeeper_init的那个watch就OK了,其他各种watch被通知session event并不会让这次watch失效,仅仅是一个通知而已,这一点必须搞清楚,所以在非zookeeper_init的watcher的其他watcher里遇见session event,直接忽略即可。 另外,只要是当前生效的watch,都会在重连zk后发起一次setWatch的request,都会重新恢复起来的。这里的恢复也不是简单的重新注册上,是肯定会根据客户端和服务端的版本变化马上对比,如果有差异马上会通知,这一点对用户完全透明。
  • 3,async接口是否有超时控制? 答:没有,其实zookeeper client只是和一个zk server保持一条连接,通过poll调用完成收发,并且对每个request的response何时回来并不在意,所以如果zk server卡住了,那么client的异步请求将不会回调。另外,zk client和zk server只有ping包的,但是并没有什么真的卵用,只是在服务端太久没动静的情况下打印一个exceed deadline之类的日志。最重要的,zk client只会当与zk server连接异常的情况下才断开连接重连,当然排队的async操作都会被回调CONNECTION LOSS/OPERATION TIMEOUT之类的错误,既然一旦出现问题就断开连接并回调用户失败了,那么自然也不用担心这些请求的watch会莫名其妙的过几秒回调了,这一点可以看一下问题1)中的解释,联系起来看。
  • 4,如何结合起来理解async操作和watch的关系? 答:如果async操作返回成功,那么watch才开始生效(其实服务端已经生效了,只是client端在处理到async response时才active了watch)。如果async操作返回失败,那么watch不会生效(就是说不可能再回调你watch事件了)。同时,非常重要的一个保证是一次watch注册只会回调一次!!这就保证了你在watch回调中,如果你再次调用async操作,等async回调的时候,你才能知道下一次watch是否生效,整个流程又回到了4)的开始,整个流程被严格的保证了串行的async op -> watch -> async op,每一步都是依靠前一步的返回码可以明确知道下一步是否会发生的。这对于编程者对资源和时序的管理非常重要,理解这些才能用对zookeeper,也是理解了zookeeper client作者的设计用意。
  • 5,什么时候会发生session expired错误? 答:关于session event的各种state需要说明一下,当发生session event的时候可能是connected, connecting,associating,expired状态,当遇见connecting/associating状态时,说明连接正在建立还没完成,客户端必须检测这个connecting持续的时间,如果超过了会话的过期时间,那么就没必要建下去了,因为服务端一定也认为这个会话超时了。那么这个会话超时时间是怎么得来的呢,是客户端向服务端协商来的,仅仅在connected的时候调用zoo_recv_timeout才能获取到,这种情景是客户端主动意识到session expired,另外一种是client先前建立了session,然后与zk断开后一段时间都连接不上zk,并且假设客户端没有主动意识到session expired(假设我们没实现这个功能),突然client又连上了zk并试图恢复之前的session,被zk告知session过期了,这时候会被watch通知一个expired的state,这是被动意识到session expired。 之所以要实现主动意识expired,是因为如果client一直连不上zk,那么就永远不会触发watch的session expired stat,所以我们必须自己加一个定时检测,其中session timeout在connected stat时记录下来,在触发session connecting的watch时,记录下连接建立的开始时间,并定期检测client处于connecting/associating状态下的持续时间,如果超过session timeout,那么可以认为session过期,结束程序、
  • 6,session expired后zk client发生了什么? 答:一旦session过期,当前的zhandle是不可继续使用的,最科学的做法就是让程序自杀重启,重建与zk的会话。而session expired状态发生的场景,也通常是zk集群不可用引起的,或者与zk集群的网络彻底中断了一段时间引起的。
  • 7,同步API有什么坑? 答:同步接口返回结果后,用户一般需要做一个逻辑处理-例如保存结果,但是从API返回结果到用户存储结果过程中,可能注册的watch生效并重新拉取了数据, 如果用户处理同步API数据前watch的更新结果到达,那么可能造成用户最终保存了一份旧数据。 这在异步接口中是不会存在的,因为异步接口是在zk的处理线程里依次顺序处理回调和watch通知的,所以不存在后者跑到前者前面的问题。所以,一定要注意这个坑,使用watch最好不要使用同步API,如果非要使用同步API,一定要先加锁再调用,这样就不会出现本末倒置了(锁内调ZK势必影响性能,所以如果要watch最好异步API,或者你只是初始化程序时调用一次同步API,那倒无所谓了)。