Zookeeper开发(02)建立会话实现Watcher

建⽴ZooKeeper会话

ZooKeeper的API围绕ZooKeeper的句柄(handle)⽽构建,每个API调⽤都需要传递这个句柄。这个句柄代表与ZooKeeper之间的⼀个会话。在下面的图中,与ZooKeeper服务器已经建⽴的⼀个会话如果断开,这个会话就会迁移到另⼀台ZooKeeper服务器上。只要会话还存活着,这个句柄就仍然有效,ZooKeeper客户端库会持续保持这个活跃连接,以保证与ZooKeeper服务器之间的会话存活。如果句柄关闭,ZooKeeper客户端库会告知ZooKeeper服务器终⽌这个会话。如果ZooKeeper发现客户端已经死掉,就会使这个会话⽆效。如果客户端之后尝试重新连接到ZooKeeper服务器,使⽤之前⽆效会话对应的那个句柄进⾏连接,那么ZooKeeper服务器会通知客户端库,这个会话已失效,使⽤这个句柄进⾏的任何操作都会返回错误。

《Zookeeper开发(02)建立会话实现Watcher》

创建ZooKeeper句柄的构造函数如下所⽰:

《Zookeeper开发(02)建立会话实现Watcher》

其中:

connectString

包含主机名和ZooKeeper服务器的端口。我们之前通过zkCli连接ZooKeeper服务时,已经列出过这些服务器。

sessionTimeout

以毫秒为单位,表⽰ZooKeeper等待客户端通信的最长时间,之后会声明会话已死亡。⽬前我们使⽤15000,即15秒。这就是说如果ZooKeeper与客户端有15秒的时间⽆法进⾏通信,ZooKeeper就会终⽌客户端的会话。需要注意,这个值⽐较⾼,但对于我们后续的实验会⾮常有⽤。ZooKeeper会话⼀般设置超时时间为5~10秒。

watcher

⽤于接收会话事件的⼀个对象,这个对象需要我们⾃⼰创建。因为Wacher定义为接口,所以我们需要⾃⼰实现⼀个类,然后初始化这个类的实例并传⼊ZooKeeper的构造函数中。客户端使⽤Watcher接口来监控与ZooKeeper之间会话的健康情况。与ZooKeeper服务器之间建⽴或失去连接时就会产⽣事件。它们同样还能⽤于监控ZooKeeper数据的变化。最终,如果与ZooKeeper的会话过期,也会通过Watcher接口传递事件来通知客户端的应⽤。

实现一个watcher

为了从ZooKeeper接收通知,我们需要实现监视点。⾸先让我们进⼀步了解Watcher接口,该接口的定义如下:

《Zookeeper开发(02)建立会话实现Watcher》

这个接口没有多少内容,我们不得不⾃⼰实现,但现在我们只是简单地输出事件。所以,让我们从⼀个名为Master的类开始实现⽰例:

《Zookeeper开发(02)建立会话实现Watcher》

①在构造函数中,我们并未实例化ZooKeeper对象,⽽是先保存hostPort留着后面使用。Java最佳实践告诉我们,⼀个对象的构造函数没有完成前不要调用这个对象的其他⽅法。因为这个对象实现了Watcher,并且当我们实例化ZooKeeper对象时,其Watcher的回调函数就会被调用,所以我们需要Master的构造函数返回后再调用ZooKeeper的构造函数。

②使用Master对象来构造ZooKeeper对象,以便添加Watcher的回调函数。

③这个简单的示例没有提供复杂的事件处理逻辑,⽽只是将我们收到的事件进⾏简单的输出。

④我们连接到ZooKeeper后,后台就会有⼀个线程来维护这个ZooKeeper会话。该线程为守护线程,也就是说线程即使处于活跃状态,程序也可以退出。因此我们在程序退出前休眠⼀段时间,以便我们可以看到事件的发⽣。

当我们编译完Master.java这个⽂件后,运⾏它并查看结果:

《Zookeeper开发(02)建立会话实现Watcher》
《Zookeeper开发(02)建立会话实现Watcher》

ZooKeeper客户端API产⽣很多⽇志消息,使⽤户可以了解发⽣了什么。⽇志⾮常详细,可以通过配置⽂件来禁⽤这么详细的⽇志,不过在开发时,这些消息⾮常有⽤,甚⾄在正式部署后,发⽣了某些我们不希望发⽣的事情,这些⽇志消息也是⾮常有价值的。

①前面⼏⾏日志消息描述了ZooKeeper客户端的实现和环境。

②当客户端初始化⼀个到ZooKeeper服务器的连接时,⽆论是最初的连接还是随后的重连接,都会产⽣这些日志消息。

③这个消息展示了连接建立之后,该连接的信息,其中包括客户端所连接的主机和端⼝信息,以及这个会话与服务器协商的超时时间。如果服务器发现请求的会话超时时间太短或太长,服务器会调整会话超时时间。

④最后这⾏并不是ZooKeeper库所输出,⽽是我们实现的Watcher.process(WatchedEvent e)函数中输出的WatchEvent对象。

这个例⼦中,我们使用了lombok的@Slf4j注解,如果看不到日志,可以配置一下。

运行watcher示例

如果我们不启动ZooKeeper服务就启动主节点,这样会发⽣什么呢?我们可以试⼀下。停⽌服务,然后运⾏Master,看到了什么?在之前输出中的最后⼀⾏,即WatchedEvent的数据,现在并没有出现。因为ZooKeeper库⽆法连接ZooKeeper服务器,所以我们看不到这些信息了。

现在我们启动服务器,然后运⾏Master,之后停⽌服务器并保持Master继续运⾏。你会看到在SyncConnected事件之后发⽣了Disconnected事件。

当开发者看到Disconnected事件时,有些⼈认为需要创建⼀个新的ZooKeeper句柄来重新连接服务。不要这么做!当你启动服务器,然后启动Master,再重启服务器时看⼀下发⽣了什么。你看到SyncConnected事件之后为Disconnected事件,然后又是⼀个SyncConnected事件。ZooKeeper客户端库负责为你重新连接服务。当不幸遇到⽹络中断或服务器故障时,ZooKeeper可以处理这些故障问题。

我们需要知道ZooKeeper本⾝也可能发⽣这些故障问题。⼀个ZooKeeper服务器也许会故障或失去⽹络连接,类似我们停⽌主节点后所模拟的场景。如果ZooKeeper服务⾄少由三台服务器组成,那么⼀个服务器的故障并不会导致服务中断。⽽客户端也会很快收到Disconnected事件,之后便为SyncConnected事件。

《Zookeeper开发(02)建立会话实现Watcher》

注意:ZooKeeper管理连接,请不要自⼰试着去管理ZooKeeper客户端连接。ZooKeeper客户端库会监控与服务之间的连接,客户端库不仅告诉我们连接发⽣问题,还会主动尝试重新建立通信。⼀般客户端开发库会很快重建会话,以便最小化应用的影响。所以不要关闭会话后再启动⼀个新的会话,这样会增加系统负载,并导致更长时间的中断。

客户端就像除了休眠外什么都没做⼀样,⽽我们通过发⽣的事件可以看到后台到底发⽣了什么。我们还可以看看ZooKeeper服务端都发⽣了什么。ZooKeeper有两种管理接口:JMX和四字母组成的命令。后面会深⼊讨论这些接口,现在我们通过stat和dump这两个四字母命令来看看服务器上发⽣了什么。

要使⽤这些命令,需要先通过telnet连接到客户端端⼜2181,然后输⼊这些命令(在命令后输⼊Enter键)。例如,如果启动Master这个程序后,使⽤stat命令,我们会看到以下输出信息:

《Zookeeper开发(02)建立会话实现Watcher》

我们从输出信息看到有两个客户端连接到ZooKeeper服务器。⼀个是Master程序,另⼀个为Telnet连接。如果我们启动Master程序后,使⽤dump命令,我们会看到以下输出信息:

《Zookeeper开发(02)建立会话实现Watcher》

我们从输出信息中看到有⼀个活动的会话,这个会话属于Master程序。我们还能看到这个会话还有多长时间会过期。会话超时的过期时间取决于我们创建ZooKeeper对象时所指定的值。

结束Master程序,再次使⽤dump命令来看⼀下活动的会话信息。你会注意到会话过⼀段时间后才消失。这是因为直到会话超时时间过了以后,服务器才会结束这个会话。当然,客户端会不断延续与ZooKeeper服务器的活动连接的到期时间。

当Master结束时,最好的⽅式是使会话⽴即消失。这可以通过ZooKeeper.close()⽅法来结束。⼀旦调⽤close⽅法后,ZooKeeper对象实例所表⽰的会话就会被销毁。

让我们在⽰例程序中加⼊close调⽤:

《Zookeeper开发(02)建立会话实现Watcher》

现在我们可以再次运⾏Master程序,运⾏dump命令来看⼀下会话是否还存活。因为Master程序中显⽰关闭了会话,所以ZooKeeper关闭会话前就不需要等待会话超时了。

我们的交流基地,“JAVA互联网技术交流:789650498”欢迎小伙伴们一起来交流:

《Zookeeper开发(02)建立会话实现Watcher》

    原文作者:郭艺宾
    原文地址: https://www.jianshu.com/p/6461508bff75
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞