美文网首页
Watch机制

Watch机制

作者: 有斧头的IceBear | 来源:发表于2022-03-06 00:12 被阅读0次

    Watch机制

    ZooKeeper 的客户端可以通过 Watch 机制来订阅当服务器上某一节点的数据或状态发生变化时收到相应的通知;

    「如何实现:」

    我们可以通过向 ZooKeeper 客户端的构造方法中传递 Watcher 参数的方式实现:

    <pre data-tool="mdnice编辑器" style="margin: 10px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgb(43, 43, 43); font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 2px; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">new ZooKeeper(String connectString, int sessionTimeout, Watcher watcher) </pre>

    上面代码的意思是定义了一个了 ZooKeeper 客户端对象实例,并传入三个参数:

    • connectString 服务端地址

    • sessionTimeout:超时时间

    • Watcher:监控事件

    这个 Watcher 将作为整个 ZooKeeper 会话期间的上下文 ,一直被保存在客户端 ZKWatchManager 的 defaultWatcher 中。

    除此之外,ZooKeeper 客户端也可以通过 getData、exists 和 getChildren 三个接口来向 ZooKeeper 服务器注册 Watcher,从而方便地在不同的情况下添加 Watch 事件:

    <pre data-tool="mdnice编辑器" style="margin: 10px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; color: rgb(43, 43, 43); font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 2px; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">getData(String path, Watcher watcher, Stat stat) </pre>

    触发通知的条件:

    图片

    上图中列出了客户端在不同会话状态下,相应的在服务器节点所能支持的事件类型。

    • 例如在客户端连接服务端的时候,可以对数据节点的创建、删除、数据变更、子节点的更新等操作进行监控。

    「当服务端某一节点发生数据变更操作时,所有曾经设置了该节点监控事件的客户端都会收到服务器的通知吗?」

    答案是否定的,Watch 事件的触发机制取决于会话的连接状态和客户端注册事件的类型,所以当客户端会话状态或数据节点发生改变时,都会触发对应的 Watch 事件。

    「订阅发布场景实现」

    提到 ZooKeeper 的应用场景,你可能第一时间会想到最为典型的发布订阅功能。

    发布订阅功能可以看作是一个一对多的关系,即一个服务或数据的发布者可以被多个不同的消费者调用。

    一般一个发布订阅模式的数据交互可以分为消费者主动请求生产者信息的拉取模式,和生产者数据变更时主动推送给消费者的推送模式。

    ZooKeeper 采用了两种模式结合的方式实现订阅发布功能。

    下面我们来分析一个具体案例:

    在系统开发的过程中会用到各种各样的配置信息,如数据库配置项、第三方接口、服务地址等,这些配置操作在我们开发过程中很容易完成,但是放到一个大规模的集群中配置起来就比较麻烦了。

    通常这种集群中,我们可以用配置管理功能自动完成服务器配置信息的维护,利用ZooKeeper 的发布订阅功能就能解决这个问题。

    我们可以把诸如数据库配置项这样的信息存储在 ZooKeeper 数据节点中。

    /confs/data_item1

    • 服务器集群客户端对该节点添加 Watch 事件监控,当集群中的服务启动时,会读取该节点数据获取数据配置信息。

    • 而当该节点数据发生变化时,ZooKeeper 服务器会发送 Watch 事件给各个客户端,集群中的客户端在接收到该通知后,重新读取节点的数据库配置信息。

    我们使用 Watch 机制实现了一个分布式环境下的配置管理功能,通过对 ZooKeeper 服务器节点添加数据变更事件,实现当数据库配置项信息变更后,集群中的各个客户端能接收到该变更事件的通知,并获取最新的配置信息。

    要注意一点是,我们提到 Watch 具有一次性,所以当我们获得服务器通知后要再次添加 Watch 事件。

    会话机制

    ZooKeeper 的工作方式一般是通过客户端向服务端发送请求而实现的。

    而在一个请求的发送过程中,首先,客户端要与服务端进行连接,而一个连接就是一个会话。

    在 ZooKeeper 中,一个会话可以看作是一个用于表示客户端与服务器端连接的数据结构 Session。

    这个数据结构由三个部分组成:分别是会话 ID(sessionID)、会话超时时间(TimeOut)、会话关闭状态(isClosing)

    • 会话 ID:会话 ID 作为一个会话的标识符,当我们创建一次会话的时候,ZooKeeper 会自动为其分配一个唯一的 ID 编码。

    • 会话超时时间:一般来说,一个会话的超时时间就是指一次会话从发起后到被服务器关闭的时长。而设置会话超时时间后,服务器会参考设置的超时时间,最终计算一个服务端自己的超时时间。而这个超时时间则是最终真正用于 ZooKeeper 中服务端用户会话管理的超时时间。

    • 会话关闭状态:会话关闭 isClosing 状态属性字段表示一个会话是否已经关闭。如果服务器检查到一个会话已经因为超时等原因失效时, ZooKeeper 会在该会话的 isClosing 属性值标记为关闭,再之后就不对该会话进行操作了。

    「会话状态」

    在 ZooKeeper 服务的运行过程中,会话会经历不同的状态变化。

    这些状态包括:

    正在连接(CONNECTING)、已经连接(CONNECTIED)、正在重新连接(RECONNECTING)、已经重新连接(RECONNECTED)、会话关闭(CLOSE)等。

    当客户端开始创建一个与服务端的会话操作时,它的会话状态就会变成 CONNECTING,之后客户端会根据服务器地址列表中的服务器 IP 地址分别尝试进行连接。如果遇到一个 IP 地址可以连接到服务器,那么客户端会话状态将变为 CONNECTIED。

    如果因为网络原因造成已经连接的客户端会话断开时,客户端会重新尝试连接服务端。而对应的客户端会话状态又变成 CONNECTING ,直到该会话连接到服务端最终又变成 CONNECTIED。

    在 ZooKeeper 服务的整个运行过程中,会话状态经常会在 CONNECTING 与 CONNECTIED 之间进行切换。

    最后,当出现超时或者客户端主动退出程序等情况时,客户端会话状态则会变为 CLOSE 状态。

    「会话异常」

    在 ZooKeeper 中,会话的超时异常包括客户端 readtimeout 异常和服务器端 sessionTimeout 异常。

    • 在我们平时的开发中,要明确这两个异常的不同之处在于一个是发生在客户端,而另一个是发生在服务端。

    而对于那些对 ZooKeeper 接触不深的开发人员来说,他们常常踩坑的地方在于,虽然设置了超时间,但是在实际服务运行的时候 ZooKeeper 并没有按照设置的超时时间来管理会话。

    • 这是因为 ZooKeeper 实际起作用的超时时间是通过客户端和服务端协商决定。

    ZooKeeper 客户端在和服务端建立连接的时候,会提交一个客户端设置的会话超时时间,而该超时时间会和服务端设置的最大超时时间和最小超时时间进行比对,如果正好在其允许的范围内,则采用客户端的超时时间管理会话。

    如果大于或者小于服务端设置的超时时间,则采用服务端设置的值管理会话。

    「分桶策略」

    我们知道在 ZooKeeper 中为了保证一个会话的存活状态,客户端需要向服务器周期性地发送心跳信息。

    • 而客户端所发送的心跳信息可以是一个 ping 请求,也可以是一个普通的业务请求。

    ZooKeeper 服务端接收请求后,会更新会话的过期时间,来保证会话的存活状态。

    • 所以在 ZooKeeper 的会话管理中,最主要的工作就是管理会话的过期时间。

    ZooKeeper 中采用了独特的会话管理方式来管理会话的过期时间。

    在 ZooKeeper 中,会话将按照不同的时间间隔进行划分,超时时间相近的会话将被放在同一个间隔区间中,这种方式避免了 ZooKeeper 对每一个会话进行检查,而是采用分批次的方式管理会话。

    这就降低了会话管理的难度,因为每次小批量的处理会话过期也提高了会话处理的效率。

    「ZooKeeper 这种会话管理的好处?」

    ZooKeeper 这种分段的会话管理策略大大提高了计算会话过期的效率,如果是在一个实际生产环境中,一个大型的分布式系统往往具有很高的访问量。

    而 ZooKeeper 作为其中的组件,对外提供服务往往要承担数千个客户端的访问,这其中就要对这几千个会话进行管理。

    在这种场景下,要想通过对每一个会话进行管理和检查并不合适,所以采用将同一个时间段的会话进行统一管理,这样就大大提高了服务的运行效率。

    「底层实现」

    ZooKeeper 底层实现的原理,核心的一点就是过期队列这个数据结构。所有会话过期的相关操作都是围绕这个队列进行的。

    • 可以说 ZooKeeper 底层就是采用这个队列结构来管理会话过期的。

    「一个会话过期队列是由若干个 bucket 组成的。」

    • bucket 是一个按照时间划分的区间。

    • 在 ZooKeeper 中,通常以 expirationInterval 为单位进行时间区间的划分,它是 ZooKeeper 分桶策略中用于划分时间区间的最小单位。

    • 在 ZooKeeper 中,一个过期队列由不同的 bucket 组成。

    • 每个 bucket 中存放了在某一时间内过期的会话。

    将会话按照不同的过期时间段分别维护到过期队列之后,在 ZooKeeper 服务运行的过程中,具体的执行过程如下图所示。

    图片

    首先,ZooKeeper 服务会开启一个线程专门用来检索过期队列,找出要过期的 bucket,而 ZooKeeper 每次只会让一个 bucket 的会话过期,每当要进行会话过期操作时,ZooKeeper 会唤醒一个处于休眠状态的线程进行会话过期操作,之后会按照上面介绍的操作检索过期队列,取出过期的会话后会执行过期操作。

    相关文章

      网友评论

          本文标题:Watch机制

          本文链接:https://www.haomeiwen.com/subject/dwcirrtx.html