So you want to be a zookeeper翻译

来源:互联网 发布:c语言随机数的产生 编辑:程序博客网 时间:2024/05/16 06:25

原文地址http://www.ngdata.com/so-you-want-to-be-a-zookeeper/  翻译的不够好,后续会进行润色


这篇文章是关于一些我了解的zookeeper的具体的事情。如果你正在写一个分布式的应用或需要在进程之间进行一些调节,例如配置,组成员关系,选主,锁等等,Apache ZooKeeper是有用的。

Zookeeper有非常好的文档,我已经在如下文章中合适的地方以我自己的观点提供了关键点,帮助某些人缩短学习曲线。

连接丢失(Connection loss)和会话失效 (session expiration)

当使用zookeeper的时候,你需要考虑的一个焦点问题是如何处理连接丢失和会话失效。因为zookeeper是中心调节服务,应用程序一旦和zookeeper失去连接会处于不确定的状态。

为了能在不稳定的网络环境 和 你连接的具体的zookeeper服务器死掉 的情况下生存,zookeeper产生了”会话失效“的概念。如果在会话超时时间内,重连上zookeeper集群中的任意一台服务器,那么你的应用创建的临时节点和监听事件将不会丢失。Troubleshooting page 中描述了各种各样连接问题。

一旦连接断开,zookeeper会通过发送一个断开连接的事件(Disconnected event)给所有的监听者的方式报告给应用程序。一旦连接重新建立,它会报告一个连接上的事件(SyncConnected event)。即使连接断开非常短的时间,你也会收到这两次事件。

如果应用程序在会话超时的时间后,连上了一台zookeeper服务器,它将会收到会话过期事件。需要注意的事情是这个事件是由zookeeper服务器产生的。如果zookeeper客户端等了很久,甚至几个小时(远远超过会话过期时间),你的应用除了最初的断开连接事件不会收到其他任何事件。一开始我不理解如果它不能够在会话过期时间内重新建立连接,为什么不能zookeeper的客户端本地产生这个事件。事实证明是有原因的(这里也有)。(大概解释一下把,就是假如zookeeper集群由于断电等整个挂了时,假如挂了20分钟,重启启动起来时各种事件和临时节点都会恢复)。

如何处理连接断开事件几乎是每个应用程序需要处理的。如果应用程序是由一些工作者(worker)组成的,它是可以暂时停止的直到连接建立。如果应用程序是用来选主的(leader),它可以继续假设哪个是leader吗?请参考post on active & passive leaders和这个来获得一些指导。如果应用是一个server,它是否该继续接受客户端的连接?你需要来决定各种事情,这有一个最佳实践,一旦你深入到邮件列表讨论中,你会发现大量与断开连接的讨论。

会话过期没有连接断开常见,并且是难以恢复的,因为你需要重新创建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)如果不可能的话。


0 0
原创粉丝点击