开始使⽤ZooKeeper的API

  在之前的章节中,我们使用zkCli工具介绍了ZooKeeper的基本操作。从本章开始,我们将会看到在应用中如何通过API来进行操作。首先介绍一下如何使用ZooKeeper的API进行开发,展示如何创建会话,实现监视点(watcher)。我们还是从主-从模式例子开始进行编码

1.1建立ZooKeeper会话

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

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

ZooKeeper(
String connectString,
int sessionTimeout,
Watcher watcher)

其中:

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

sessionTimeout
  以毫秒为单位,表示ZooKeeper等待客户端通信的最长时间,之后会声
明会话已死亡。目前我们使用15000,即15秒。这就是说如果ZooKeeper与
客户端有15秒的时间无法进行通信,ZooKeeper就会终止客户端的会话。需
要注意,这个值比较大,但对于我们后续的实验会非常有用。ZooKeeper会
话一般设置超时时间为5~10秒。
 watcher
  用于接收会话事件的一个对象,这个对象需要我们自己创建。因为
Wacher定义为接口,所以我们需要自己实现一,然后初始化这个类的
实例并传递ZooKeeper的构造函数中。客户端使用Watcher接口来监控与
ZooKeeper之间会话的健康情况。与ZooKeeper服务器之间建立或失去连接
时就会产生事件。它们同样还能用于监控ZooKeeper数据的变化。最终,如
果与ZooKeeper的会话过期,也会通过Watcher接口传递事件来通知客户端
的应用。

1.1.1实现一个Watcher

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

public interface Watcher {
void process(WatchedEvent event);
}

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

import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.Watcher;
public class Master implements Watcher {
ZooKeeper zk;
String hostPort;
Master(String hostPort) {
this.hostPort = hostPort; ①
}
void startZK() {
zk = new ZooKeeper(hostPort, 15000, this); ②
}
public void process(WatchedEvent e) {
System.out.println(e); ③
}
public static void main(String args[])
throws Exception {
Master m = new Master(args[0]);
m.startZK();
// wait for a bit
Thread.sleep(60000); ④
}
}

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

所谓守护线程是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。

守护线程和用户线程的没啥本质的区别:唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。

运行:

... - INFO [...] - Client environment:zookeeper.version=3.4.5-1392090, ... ①
...
... - INFO [...] - Initiating client connection,
connectString=127.0.0.1:2181 ... ②
... - INFO [...] - Opening socket connection to server
localhost/127.0.0.1:2181. ...
... - INFO [...] - Socket connection established to localhost/127.0.0.1:2181,
initiating session
... - INFO [...] - Session establishment complete on server
localhost/127.0.0.1:2181, ... ③
WatchedEvent state:SyncConnected type:None path:null ④

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

①前面⼏⾏日志消息描述了ZooKeeper客户端的实现和环境。
②当客户端初始化⼀个到ZooKeeper服务器的连接时,⽆论是最初的
连接还是随后的重连接,都会产⽣这些日志消息。
③这个消息展示了连接建立之后,该连接的信息,其中包括客户端所
连接的主机和端⼝信息,以及这个会话与服务器协商的超时时间。如果服
务器发现请求的会话超时时间太短或太长,服务器会调整会话超时时间。
④最后这⾏并不是ZooKeeper库所输出,⽽是我们实现的
Watcher.process(WatchedEvent e)函数中输出的WatchEvent对象。

 1.1.2运行Watcher的示例

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

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

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

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

 注意:ZooKeeper管理连接

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

  

  客户端就像除了休眠外什么都没做一样,而我们通过发生的事件可以看到后台到底发生了什么。我们还可以看看ZooKeeper服务端都发生了什么。ZooKeeper有两种管理接口:JMX和四字母组成的命令。第10章会深入讨论这些接口,现在我们通过stat和dump这两个四字母命令来看看服务器上发生了什么。要使用这些命令,需要先通过telnet连接到客户端端口2181,然后输入这些命令(在命令后输入Enter键)。例如,如果启动Master这个程序后,使用stat命令,我们会看到以下输出信息:

$ telnet 127.0.0.1 2181
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
stat
ZooKeeper version: 3.4.5-1392090, built on 09/30/2012 17:52 GMT
Clients:
/127.0.0.1:39470[1](queued=0,recved=3,sent=3)
/127.0.0.1:39471[0](queued=0,recved=1,sent=0)
Latency min/avg/max: 0/5/48
Received: 34
Sent: 33
Connections: 2
Outstanding: 0
Zxid: 0x17
Mode: standalone
Node count: 4
Connection closed by foreign host.

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

$ telnet 127.0.0.1 2181
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
dump
SessionTracker dump:
Session Sets (3):
0 expire at Wed Nov 28 20:34:00 PST 2012:
0 expire at Wed Nov 28 20:34:02 PST 2012:
1 expire at Wed Nov 28 20:34:04 PST 2012:
0x13b4a4d22070006
ephemeral nodes dump:
Sessions with Ephemerals (0):
Connection closed by foreign host.

  我们从输出信息中看到有⼀个活动的会话,这个会话属于Master程序。我们还能看到这个会话还有多长时间会过期。会话超时的过期时间取决于我们创建ZooKeeper对象时所指定的值。让我结束Master程序,再次使用dump命令来看⼀下活动的会话信息。你会注意到会话过一段时间后才消失。这是因为直到会话超时时间过了以后,服务器才会结束这个会话。当然,客户端会不断延续与ZooKeeper服务器的活动连接的到期时间。当Master结束时,最好的方式是使会话立即消失。这可以通过ZooKeeper.close()方法来结束。一旦调用close方法后,ZooKeeper对象实例所表示的会话就会被销毁。让我们在示例程序中加入close调用:

void stopZK() throws Exception { zk.close(); }
public static void main(String args[]) throws Exception {
Master m = new Master(args[0]);
m.startZK();
// wait for a bit
Thread.sleep(60000);
m.stopZK();
}

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

原文地址:https://www.cnblogs.com/duan2/p/9017305.html