温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

确保zookeeper一定收到通知消息的方法

发布时间:2020-07-14 21:44:13 来源:网络 阅读:1733 作者:myxxdyl 栏目:开发技术

  zookeeper能够同步同步各节点的znode数据,client可以使用getChildren,getData,exists方法在znode tree路径上设置watch,当watch路径上发生节点create、delete、update的时候,会通知到client。client可以得到通知后,再获取数据,执行业务逻辑操作。

  但是因为没有消息接收后的确认机制,这个通知机制是不可靠的,也就是说znode的修改者并不知道是否所有的client都被通知到了,或者说client也不知道自己是否错过了哪些通知消息。这种现象可能由网络原因引起,也可能是client刚触发了watch事件,还没有来得及重新设置watch,下个事件就发生了(zookeeper只提供了一次性watch)。

  在笔者的使用场景中,这是有问题的。在我们分布式配置管理的场景中,有3份配置副本,管理节点本地数据库中有一份,zookeeper中保存了一份,使用配置的软件引擎进程中保存了一份。当下发配置时,是需要全部软件引擎都生效最新的配置,而如果有某个引擎错过了通知,那么就会漏掉某个配置,导致问题,而到底谁没有成功生效,这些节点都不知道,甚至错过通知的节点自身也不知道。

  因此我提出一种设计,至少让错过通知的节点自身知道错过了消息,并采取主动同步配置的方式,来补救。这样能够保证,配置下发后,至少一段时间后所有软件引擎的都使用了最新的配置。

  这利用了zookeeper自身的几个特性:

  1)zookeeper维护一个全局的操作id,zxid,每一个create,delete,update操作都会使该id加1。

  2)zookeeper为每个路径(znode)节点都保存了它的修改版本dataversion,和最新一次修改zxid——mzxid。

  3)zookeeper保证每个client连接的session中,看到的通知顺序与这些事件发生的先后顺序是严格一致的。

  我们来假设要在/group/policy下增加、删除、修改配置,每个配置有1个节点,配置数量可以很多、而且不固定。client要知道增加了什么配置,修改了什么,删除了什么配置,因此设定了watch。其中A复杂修改这些配置,N1,N2,...Nm这些节点监听通知,并更新软件引擎的变量,使其生效配置。

  首先A在/group/policy下做create、delete、update操作后,都要set 一次 /group/policy节点。这会导致/group/policy的dataversion加1。并且可以知道有几次操作,dataversion就增加几。

  伪代码如下:

  doSomeOperion();

  setData("/group/policy","")

  Ni节点要对/group/policy下节点发生修改的事件进行watch,还要对/group/policy节点自身的修改进行watch。因此如果Ni没有错过通知的话,它将一次触发两个通知:1)配置变化通知;2)/group/policy数据更新通知。

  Ni要在本地保存三个变量current_dataversion,current_zxid

  在Ni client初始化时:

  current_dataversion=/group/policy的dataversion;

  current_zxid=/group/policy的mzxid;

  然后在watch到配置发生变化的回调函数中:

   doSometing(); //生效具体配置

   current_dataversion += 1; //期待/group/policy的下一个dataversion增加1

  在watch到/group/policy的数据发生变化后回调函数中:

   if current_dataversion == /group/policy的dataversion: //意味着没有漏掉消息

      current_zxid = /group/policy的mzxid

   elif next_dataversion < /group/policy的dataversion: //一位置有配置变化的消息没有收到

      遍历/group/policy子节点

        if /group/policy/znodei的mzxid > current_zxid:

          使用znodei中的配置。

      删除已经不存在znode的配置项;

     同步完成;

     next_dataversion = /group/policy的dataversion

     current_zxid = /group/policy的mzxid

  这样就可以保证client能够发现自己错过了消息,并发现哪些znode的修改被自己错过了。那么至少在下一次发生修改配置后,client能够完全与当前配置一致。

  我们可以写一个场景验证下:

  初始时/group/policy下为空,/group/policy的stat为(mzxid=2,dataversion=0)

 

    current_dataversion=0;
    current_zxid=2
 1)create /group/policy/n1(mzxid=3,dataversion=0) 收到通知 current_dataversion+=1 (等于1)
 2)set /group/policy (mzxid=4,dataversion=1) 收到通知 curren_dataversion==/group/policy.dataversion,没有漏掉通知
                                                      current_zxid=/group/policy.mzxid (等于4)
 情形一)                                                     
 3.1) create /group/policy/n2 (mzxid=5,dataversion=0) 没有收到通知 current_dataversion不变(等于1)
 4.1)set /group/policy (mzxid=6,dataversion=2) 收到通知 current_data < /group/policy.dataversion,得知漏掉了通知,并且知道漏掉1个
                                                         同步mzxid大于current_zxid(值为4)的节点(即n2节点)配置;
                                                         删除已经不存在znode的配置;
                                                         current_data = /group/policy.dataversion (等于2)
                                                         current_zxid = /group/policy.zxid (等于5)
  情形二)
  3.2)create /group/policy/n2(mzxid=5,dataversion=0) 收到通知 current_dataversion+=1 (等于2)
  4.2)set /group/policy (mzxid=6,dataversion=2)  没有收到通知  current_zxid(=4)不变。漏掉该消息是没有关系的,再次收到该消息时,会更新current_zxid
  情形三)
  3.3)create /group/policy/n2(mzxid=5,dataversion=0) 没收到通知 current_dataversion不变(等于1)
   4.3)set /group/policy (mzxid=6,dataversion=2)  没有收到通知   current_zxid(=4)不变。
   5)create /group/policy/n3(mzxid=7,dataversion=0) 收到通知 current_dataversion+=1 (等于2)
   6)set /group/policy (mzxid=8,dataversion=3)   收到通知    current_data < /group/policy.dataversion 得知漏掉了通知
                                                              同步mzxid大于current_zxid(值为4)的节点(即n2,n3节点)配置;
                                                              删除已经不存在znode的配置;
                                                              current_data = /group/policy.dataversion (等于3)
                                                              current_zxid = /group/policy.zxid (等于8)

通过这种方式,可以让client端知道自己错过了通知,至少在下次收到/group/policy节点更新通知时,能够重新同步配置。因此可以保证client之间迟早会变得同步。

更进一步,可以额外再增加时钟来触发对/group/policy节点的检查。这样就可以保证一个时钟间隔之后,client肯定是同步的。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI