ZooKeeper解析:分布式系统工程师的瑞士军刀

jopen 9年前


【51CTO.com快译】所谓分布式系统,指的是一组通过发送消息实现协作、从而共同达成同一目标的资源集合。正如知名计算机科学家 Leslie Lamport所指出之定义:“所谓分布式系统,其中任意一台计算设备——即使使用者并未直接使用甚至对其存在毫不知情——出现故障,亦会影响到其它设备 的正常运作。”

而这条定义也恰恰概括了我们在分布式系统当中经常遇到的一类问题。事实上,在云计算时代之下,资源的汇聚已经成为满足负载对于计算及存储实际需求的一种必然手段。这类系统的特点在于包含大量需要管理的资源,而其中故障的出现频率与整体规模则成正比关系。

在分布式系统当中,故障属于一类常规事态,而非意外状况——这意味着我们必须时刻做好心理准备。有鉴于此,相关社区专门创建出专门的工具,旨在帮助开发人员应对这方面问题,而Apache ZooKeeper正是其中之一。

ZooKeeper是一款极具实用性、现场测试能力并拥有广泛用户群体的中间件,专门用于构建分布式应用程序。在OpenStack当 中,ZooKeeper也成为Nova ServiceGroup API后端中的组成部分。最近,ZooKeeper还与Ceilometer相集成,从而为Central Agent带来更为理想的高可用性水平——当然,这方面话题我们以后将另行讨论。

我们为什么需要ZooKeeper?

一般来讲,当大家设计一款分布式应用程序时,常常会发现需要将各类流程加以协同才能执行预期任务。在大多数情况下,这种协作关系依赖于最基本的分布式协作机制。

Heat是一款OpenStack编排程序。大家可以利用它创建出一系列云资源,而这类资源会由一个模板文件负责指定,这就是堆栈的概念。 Heat允许用户对堆栈进行更新,但更新过程必须以原子方式进行,否则可能会导致资源复制或者相关性破坏等冲突。这类问题在并发更新场景下非常常见,而为 了解决此类问题,Heat会在对堆栈进行更新之前首先设置一套所谓分布式互斥锁。

在这类原型基础之上进行开发是项极为困难的工作,而且经常带来令人头痛的麻烦。事实上,在分布式系统当中反复出现的这些问题早已成为技术圈中的共 识。为了简化开发人员的日常工作,雅虎实验室创造出了Apache ZooKeeper项目,旨在为这些协作因素提供一套集中式API。归功于ZooKeeper的帮助,现在我们已经能够轻松实现多种不同协议,包括分布式 锁、屏障以及队列等等。

ZooKeeper应用程序的架构与优势

一款ZooKeeper应用程序由一台或者多台ZK服务器支撑而成,我们可以将其称为一个“集合”,在应用程序端则为一组ZK客户端。

ZooKeeper解析:分布式系统工程师的瑞士军刀

其设计思路在于,该分布式应用程序的每一个节点都通过使用一个ZK客户端在应用层级使用相关API,这意味着应用的运行将依赖于ZooKeeper服务器实现。

这种架构方案拥有以下几项突出优势:

  • 我们可以立足于应用层提取大部分分布式同步负载,从而实现一套所谓KISS(即Keep It Simple, Stupid,保持一切简单且具傻瓜式特性)架构。
  • 常见的各类分布式协作元能够实现开箱即用,因此开发人员无需再自行对其加以处理。
  • 开发人员不需要处理服务故障等状况,因为整套体系拥有出色的弹性。ZooKeeper以应用程序神经中心的姿态存在,因为它负责控制整个协作 机制,因此众多组件都需要依附于它以实现作用。出于这些理由,ZooKeeper在设计中引入了出色的分布式算法,从而提供开发人员所需要的高可靠性与可 用性保障。一个ZooKeeper集合基于群体形式存在,且通常由三到五台服务器构成。

ZooKeeper集合能够在多种场景之下发挥作用,下面让我们从实践角度出发一同来了解。

实践场景中的ZooKeeper

ZooKeeper的API非常简单而且直观,其数据模型基于以内存树形式存储的分层命名空间。该树中的各项元素被称为znode,以文件形式容纳数据并能够如目录一般拥有子znode。

首先,大家需要确保自己的运行环境满足系统配置要求,接下来我们就要着手部署一台ZK服务器了:

  1. $ wget http:  
  2. $ tar xzf zookeeper-3.4.6.tar.gz 
  3. $ cd zookeeper-3.4.6 
  4. $ cp conf/zoo_sample.cfg conf/zoo.cfg 
  5. $ ./bin/zkServer.sh start 

现在ZooKeeper服务器已经能够以独立模式运行了,且会在默认情况下监听127.0.0.1:2181。如果大家需要部署一整套服务器集合,则可以点击此处阅读其相关管理指南。

ZooKeeper命令行界面

我们可以利用ZooKeeper命令行界面(./bin/zkCli.sh)完成一些基础性操作。其使用方式与shell控制台非常相似,操作感受也与文件系统相当接近。

下面列出root znode“/”中的全部子znode:

  1. [zk: localhost:2181(CONNECTED) 0] ls / 
  2. [zookeeper] 

创建一个路径为“/myZnode”的znode,其相关数据则为“myData”:

  1. [zk: localhost:2181(CONNECTED) 1] create /myZnode myData 
  2. Created /myZnode 
  3. [zk: localhost:2181(CONNECTED) 2] ls / 
  4. [myZnode, zookeeper] 

删除一个znode:

  1. [zk: localhost:2181(CONNECTED) 3]  delete  /myZnode 

大家可以输入“help”命令来查看更多操作命令。在本次示例当中,我们将使用应用程序编程接口(简称API)来编写一款分布式应用程序。

Python ZooKeeper API

我们的这套ZooKeeper服务器是由Java编程语言构建而成,且绑定了多种由不同语言编写而成的客户端集合。在今天的文章中,我们将通过Kazzo这一Python捆绑客户端来了解该API。

我们可以在虚拟环境下轻松完成Kazoo的安装工作:

  1. $ pip install kazoo 

首先,我们需要接入一个ZooKeeper集合:

  1. from kazoo  import  client as kz_client 
  2.   
  3. my_client = kz_client.KazooClient(hosts= '127.0.0.1:2181'
  4.   
  5. def my_listener(state): 
  6.      if  state == kz_client.KazooState.CONNECTED: 
  7.         print( "Client connected !"
  8.   
  9. my_client.add_listener(my_listener) 
  10. my_client.start(timeout=5) 

在以上代码当中,我们利用KazooClient类创建了一个ZK客户端。其中的“hosts”参数负责定义该ZK服务器地址,并以逗号加以分隔,因此如果某台服务器出现故障,那么该客户端将自动尝试接入其它服务器。

Kazoo能够在连接状态出现变化时向我们发出通知,根据当前具体状况,这项功能可以非常实用地触发我们预设的各类操作。举例来说,当连接无法顺利建立时,该客户端应当停止发送命令,而这正是add_listener()方法的作用所在。

而start()方法则能够在确认会话创建完毕之后,在客户端与一台ZK服务器之间建立起连接。每台服务器都会追踪每个客户端中的一项会话,这种特性在实际分布式协作元方面起到非常重要的基础性作用。