前言
大规模分布式系统需要解决各种类型的协调需求:
-
当集群中有新的进程或服务器加入时,如何探测到它的加入?如何能够自动获取配置参数?
-
当配置信息被某个进程或服务器改变时,如何实时通知整个集群中的其他机器?
-
如何判断集群中的某台机器是否还存活 ?
-
如何选举主服务器,主服务器宕机,如何从备选服务器中选出新的主服务器?
以上问题的本质都是分布式系统下协调管理的问题,目前比较有名的协调系统有Google的Chubby,Yahoo的Zookeeper(对于协调系统来说其客户端往往是分布式集群)。
Chubby
Chubby提供粗粒度锁服务。一个Chubby服务单元大约能为1万台4核CPU机器提供资源的协同管理服务。其主要功能为实现集群之间的同步,以及对整个系统的环境和资源达成一致认知。
Chubby是一种锁服务,分布式集群中的机器通过竞争数据的锁来成为leader,获得锁的服务器将自己的信息写入数据,让其他竞争者可见。其提供的粗粒度服务是指锁的持有时间比较长,Chubby会允许抢到锁的服务器,几小时甚至数天内都充当leader角色。Chubby强调系统的可靠性以及高可用性等,而不追求处理高吞吐量以及在协调系统内存储大量数据。其理论基础是Paxos,通过相互通信并投票,对某个决定达成一致性的认识。
系统架构
一个数据中心一般部署一套Chubby单元,每套Chubby单元由5台服务器组成,通过Paxos产生1台主控服务器和4台备份服务器,备份服务器保存的数据和主控服务器完全相同,在主控服务器宕机的情况下,快速在备份服务器中选出一台作为主控服务器,以保证提供正常的服务,提高整个系统的可用性。
主控服务器在任期时间内(几秒),备份服务器不会投票给别的服务器来选举出新的主控服务器。任期到期后,如果在任期时间内没有发生故障,系统会任免原来的主控服务器继续担任该职务。如果备份服务器发生故障,系统会启动一台新机器替换出故障的机器,并更新DNS,而主控服务器会周期性地检测DNS,一旦发现DNS发生变化,就将消息告知集群中的其他备份服务器。
客户端利用RPC通信和服务器进行交互,对Chubby的读写操作都由主控服务器完成,备份服务器只同步主控服务器中的数据,保证它们的数据和主控服务器的一致。若备份服务器收到来自客户端(分布式集群)的读写请求,它们会告知客户端主控服务器的地址,从而将请求转发给主服务器。
Chubby中主要存储一些管理信息和基础数据,其目的不在于数据存储而是对资源的同步管理,不推荐在Chubby中存储大量数据。同时Chubby提供了订阅机制,即客户端可以订阅某些存储在Chubby上的数据,一旦该数据发生改变,Chubby就会通知客户端。如:将分布式集群的一份配置文件存在Chubby上,集群中所有的机器都订阅这份配置文件,一旦配置文件发生改变,所有的节点都会收到消息,根据配置文件作出改变。
关于缓存
Chubby允许客户端在本地缓存部分数据,大部分数据客户端能够在本地缓存中请求到,一方面降低了请求响应时间,另一方面减小了Chubby的压力。Chubby通过维护一个缓存表来负责维护缓存和主控服务器上数据的一致性。主控服务器在收到修改某一数据请求时会将请求暂时阻塞,并通知所有缓存了该数据的客户端,客户端收到该消息后,给Chubby发送确认收到的回执消息,主控服务器收到所有相关客户端的确认信息后,才会继续执行对数据的修改。
Zookeeper
Zookeeper是一个开源的可扩展的高吞吐分布式协调系统,应用场景十分广泛。
Zookeeper集群服务由多台服务器构成(最少3台),通过选举产生1台主服务器,和多台从服务器。客户端可从任何一台服务器读取到所需数据,但要写入或修改数据必须在主服务器上执行,若客户端连接从服务器执行写入或更新数据,从服务器会将请求转发给主服务器,由主服务器完成数据的写入或更新,并将相应的信息通知给所有从服务器,从服务器据此更新自己的数据,并向主服务发送确认信息,主服务器在接受到半数或以上从服务器的确认信息后,才通知客户端写入或更新操作成功。Zookeeper集群一般由2n+1(奇数)台服务器组成,其最大能容忍n台从服务器发生故障。Zookeeper通过定期保存的快照信息和日志信息来实现其容错能力。
Zookeeper数据一致性问题
Zookeeper的任何一台从服务器也能为客户端提供读服务是其高吞吐量的主要原因,但另一方面也导致了一个问题:客户端可能会读到过期的数据。即客户端在从服务器上读数据时,主服务器已经修改了数据,还来不及将修改后的数据通知从服务器。对于此问题Zookeeper提供了同步操作来解决,客户端需要在从服务器上读取数据时调用同步方法,接收到同步命令的从服务器会向主服务器发起数据同步请求,以此来保证客户端在从服务器上读取的数据和主服务器是一致的。
Zookeeper数据模型
Zookeeper和Chubby的内存数据模型都类似于传统文件系统,由树形的层级目录结构构成,其中的节点称为Znode,其可以是文件或是目录。一般需要整体完成小数据的读写,其原因是避免被用于充当分布式存储系统使用存放大数据(Chubby也是如此)。
Zookeeper和Chubby的节点类型也相同,分为持久型和临时型。临时型节点会在客户端结束请求或发生故障时被删除。持久性节点只有显示地执行删除才能将数据从Zookeeper服务器上删除。
客户端可以在节点上设置观察标志,当节点发生变化时,zookeeper会通知客户端,这一性质对于zookeeper提供的诸多服务至关重要。
Zookeeper的典型应用场景
- 选举领导者
Zookeeper在选举领导时,会创建一个临时节点,该节点上存放领导服务器的相关信息。其他小弟会读取该节点的信息,使得整个集群知道谁是领导,并在临时节点上设置观察标志。若服务器没有读到该临时节点的数据,则表示此时集群中还没有领导,所有人都是平等的,则大家竞争领导,直到某一服务器信息写入临时节点,表示领导的产生。因为设置了观察标志,所有小弟都会收到新的领导是谁的信息。如果有新的服务器加入集群,其会读取临时节点的信息,马山就能知道谁是领导。
- 配置管理
将上层集群的配置文件等信息存储在Zookeeper集群的某个znode里,集群中的所有节点都读取该znode里的配置信息,并设置观察标志。以后如过配置信息发生变化,集群中所有节点都会收到消息,及时作出相应的改变。
- 集群成员管理
上层集群中有新人到来或是有人要走,必须要及时知道才行。Zookeeper服务可设置某一临时节点作为一个集群,集群中的成员作为该临时节点下的临时节点。客户端对这些临时节点设置观察标志,一旦有新的机器加入,或是有机器发生故障退出,就会马上收到zookeeper发来的告知消息。以此来完成集群成员的动态管理。
- 任务分配
客户端在Zookeeper创建一个任务节点,并在该节点上设置观察标志,一旦有新任务到来就在该节点下创建一个子节点,并将信息告知上层集群。上层集群在Zookeeper集群中创建有一工作节点,该节点下有多个机器节点,并对这些机器节点设置观察标志。上层集群收到任务请求后,就根据工作机器的繁忙情况选出一台机器,并在该机器节点下创建一任务节点。工作机器发现这个任务后说明有新的任务分配给自己,就去执行该任务。任务执行完后,工作机器删除自己名下的任务节点。同时会删除任务节点下的相应子节点,代表任务执行完成,客户端一直在监听这一任务节点,会马上知道任务已完成。
Zookeeper和Chubby的异同点
相同点:
- 两者的数据模型相同,都是树形的层级目录结构,类似传统文件系统
- 两者的节点相同,都分为临时节点和持久型节点
- Chubby的订阅和Zookeeper的观察标志类似
- 写或更新数据操作都需要在主服务器上完成
不同点:
- Chubby强调系统的可靠性以及高可用性等,而不追求处理高吞吐量;Zookeeper能处理高吞吐量。
- Chubby只有主节点能提供读数据的服务,Zookeeper中从节点也能提供读数据服务。
- 一致性协议上Chubby使用PAXOS和Zookeeper使用ZAB
- Chubby的主节点有租约,租约到期没问题继续续约,Zookeeper谁是主节点就一直是谁,除非人为改变或发生故障等,没有租约概念