原文地址http://www.ngdata.com/so-you-want-to-be-a-zookeeper/ 翻译的不够好,后续会进行润色
这篇文章是关于一些我了解的zookeeper的具体的事情。如果你正在写一个分布式的应用或需要在进程之间进行一些调节,例如配置,组成员关系,选主,锁等等,Apache ZooKeeper是有用的。
Zookeeper有非常好的文档,我已经在如下文章中合适的地方以我自己的观点提供了关键点,帮助某些人缩短学习曲线。
处理连接丢失异常(ConnectionLoss)
当发生连接丢失的情况,进行任何zookeeper的操作(像getChildren, get/setData, create, delete)将会抛出一个连接丢失异常。你不得不以某种方式处理这些。一种方法是重试这个操作直到成功。例如zookeeper有关锁的代码中的ProtocolSupport的retryOperation方法。这个技巧同样存在于ZKClient library中。
当一个操作抛出连接丢失的异常,你不确定操作是否成功了。这个连接可能在操作到达服务器之前或之后断开了。所以你不得不考虑当重试这个操作的时候,创建操作可能会失败由于NodeExists异常,删除可能会失败由于NoNode异常。
假设你使用顺序临时节点的创建来基于zookeeper实现了一个锁,如果当失败时进行重试,你可能创建了两个节点,但是你不会知道第一个节点的名字。(直接翻译就是这样,但是既然是有顺序的,那么小的应该就是第一个节点啊,不明白作者的意图!)你可以循环这些节点,检查Stat对象的ephemeralOwner 字段。如果你不这样做,简单的退出锁的步骤,你可能会留下一个临时节点,导致其他客户端不可能获取锁。
这个错误处理页面包含了一些关于这个话题处理的细节。
保存多个属性在节点的数据中
考虑到zookeeper的树模型,用带着子节点的节点存储对象的每一个属性可能是诱人的。因为这样允许细粒度的读写监控。
但是这样你不能原子的创建对象,因此客户端可以看到部分创建,修改,删除的对象。如果你想监控对象完整的状态,你需要监控所有单独的节点,这样会变得非常复杂。
所以,我发现把对象的所有属性存储在节点的数据中是简单的,我把它当json存储。
这有一个关于这个主题的邮件:
如果把所有的属性存储在一个节点中不能使人满意,另外有一个好的节点技术,在这里描述了。
单线程的事件
每个zookeeper的客户端实例有一个线程来分发事件到所有的监听者。所以监听者是被顺序调用的。如果你在一个监听者中执行了耗时的操作,其他所有的监听者都会等着。
因此在一个监听者中不要执行任何耗时的操作,例如IO,等待锁等。一个需要注意的情况是:不要在监听者中再次等待事件。
事件线程在编程指导中描述了。
同样的事件多次注册同样的监听器
如果你用一个客户端实例多次监听同样的事件,只会收到一次回调。例如:
Watcher w = ... zooKeeper.getChildren(“/foo”, w); zooKeeper.getChildren(“/foo”, w);当子节点改变时,监听器W只会收到一次通知。可以参见 编程指导。 “A watch object, or function/context pair, will only be triggered once for a given notification.”.
我发现在下面这种情况中非常便利。假设你的监听器被回调了,在回调方法中,你需要重新监听这个事件,但重新监听过程中由于网络断开而抛出了ConnectionLoss 异常,你不知道这个监听是否成功了。如果此时你进行重试直到成功,不像是一个好主意,因为你正在监听方法的回调中。
因此,这种情况,你只需要每次简单的重新监听这个事件在收到SyncConnected 事件时,这样你就不用担心当事件发生时,监听者被调用2次了。
中断异常(InterruptedException)
许多zookeeper的api方法会抛出InterruptedException, 这是一个需要检查的异常。所以你必须处理它。这个起初看起来可能是烦人的,但是InterruptedException实际上是一个非常有用的异常:当一个方法抛出这个异常,它告诉你它是可能终止的。处理InterruptedException异常最好的方法是什么?这有一个文章。
基本来说,你需要确认不要停止中断的线程,既不要抛出这个异常,也不要重置中断标志(Thread.currentThread().interrupt)如果不可能的话。