3.从Paxos到Zookeeper分布式一致性原理与实践---Paxos 工程实践

 

1.Chubby
	Google Chubby 是一个大名鼎鼎的分布式锁服务,GFS 和 Big Table 等大型系统都用它来解决分布式协作,元数据存储和
  Master 选举等一系列相关的问题。Chubby 的底层一致性实现就是以 Paxos 算法为基础的。

  1.概述
	Chubby 是一个面向松耦合分布式系统的锁服务,通常用于为一个由大量小型计算机构成的松耦合分布式系统提供高可用的分布式锁服务。
  一个分布式锁服务的目的是允许它的客户端进程同步彼此的操作,并对当前所处环境的基本状态信息达成一致。针对这个服务,Chubby 提供
  了粗粒度的分布式锁服务,开发人员不需要使用复杂的同步协议,而是直接调用Chubby的锁服务接口即可实现分布式系统中多个进程之间粗鲁度
  的同步控制,从而保证分布式数据的一致性。
    Chubby 的客户端接口设计非常类似于Unix文件系统结构,应用程序通过 Chubby 的客户端接口,不仅能够对 Chubby 服务器上的整个文件
  进行读写操作,还能够添加文件节点的锁控制,并且能够订阅 Chubby 服务端发出的一系列文件变动的事件通知。

  2.应用场景
  	Google 文件系统中使用 Chubby 锁服务来实现对 GFS Master 服务器的选举。而在 BigTable 中,Chubby 同样用于 Master 选举。
  	GFS : Google 开发的一种面向廉价服务器架构的大型分布式文件系统。
  	BigTable : Google 开发的一种用于进行结构化数据存储和管理的大型分布式存储系统。

  3.设计目的
  	将Chubby设计成一个需要访问中心化节点的分布式锁服务。Chubby 之所以设计成这样一个完整的分布式锁服务,是因为锁服务具有以下4个传统算法库
  所不具备的优点。
    1.对上层应用的入侵性更小
    	对于一个应用程序来说,尤其是上层的业务系统来说,在系统开发初期,开发人员并没有为系统的高可用性做好充分的考虑。事实上,绝大部分的系统
      一开始都是从一个只需哟啊支持很小的负载,并且只需要保证大体可用的原型开始,往往并没有在代码层面为分布式一致性协议的实现留有余地。当系统
      提供的服务日趋成熟,并且得到一定规模的用户认可之后,系统的可用性就会变得越来越重要。于是,集群中副本复制和 Master 选举等一系列提高分布式
      系统的可用性措施,就会被加入到一个已有的系统中。
        在这种情况下,尽管这些措施都可以通过一个封装了分布式一致性协议的客户端库来完成,但相比之下,使用一个分布式锁服务的接口方式对上层应用程序
      的入侵性会更小,并且更易于保持系统已有的程序结构和网络通信模式。

    2.便于提供数据的发布与订阅
    	几乎所有使用Chubby进行Master选举的应用场景中,都需要一种广播结果的机制,用来向所有的客户端公布当前的Master服务器。这就意味着Chubby 应该
      允许其客户端在服务器上进行少量数据的存储和读取---也就是对小文件的读写操作。虽然这个特性也能够通过一个分布式命名服务来实现,但是根据实际的经验
      来看,分布式搜服务本身也非常适合提供这个功能,这一方面能够大大减少客户端依赖的外部服务,另一方面,数据的发布与订阅功能和锁服务在分布式一致性
      特性上是相同的。

    3.开发人员对基于锁的接口更为熟悉
    	对于绝大部分的开发人员来说,在平常的编程过程中,他们对于基于锁的接口都已经非常熟悉了。因此,Chubby 为其提供了一套近乎和单机锁机制一致的分布式
      锁服务接口,这远比提供一个一致性协议的库来得更为友好。

    4.更便捷的构建可靠的服务
    	通常一个分布式一致性算法都需要使用 Quorum 机制来进行数据项值的选定。Quorum 机制是分布式系统中实现数据一致性的一个比较特殊的策略,它指的是在
      一个由若干个机器组成的集群中,在一个数据项值的选定过程中,要求集群中存在过半的机器达成一致,因此 Quorum 机制也被称为 '过半机制'。在 Chubby 中
      通常使用 5 台服务器组成一个集群单元(cell),根据 Quorum 机制,只要整个集群中有3台机器是正常运转的,那么整个集群就可以对外提供正常的服务。相反的,
      如果仅提供一个分布式一致性协议的客户端库,那么这些高可用性的系统部署都将交给开发人员自己来处理,这无疑提高了成本。

    因此,Chubby 被设计成一个需要访问中心化节点的分布式锁服务。同时,在 Chubby 的设计过程中,提出了以下几个设计目标:
    1.提高一个完整的,独立的分布式锁服务,而非仅仅是一个一致性协议的客户端库	
    	提供一个独立的锁服务的最大好处是,Chubby 对于使用它的应用程序的侵入性非常低,应用程序不需要修改已有的程序的结构即可使用分布式一致特性。如
      对于 'Master 选举同时将Master 信息登记并广播'的场景,应用程序只需要向Chubby 请求一个锁,并且在获得锁后向响应的锁文件写入 Master信息即可,
      其他的客户端就可以通过读取这个锁文件来获取 Master 信息。

    2.提高粗粒度的锁服务
    	Chubby 锁服务针对的应用场景是客户端获取锁之后会进行长时间(数小时后者数天),而非用于短暂获取锁的场景。针对这种应用场景,当锁服务短暂失效时(例如
      服务器宕机),Chubby 需要保持所有的锁的持有状态,以避免持有锁的客户端出现问题。这和细粒度锁服务的设计方式有很大的区别,细粒度的锁服务通常设计为为
      锁服务一旦失效就释放所有的锁,因为细粒度锁的持有时间很短,相比而言放弃锁带来的代价比较小。

    3.在提高锁服务的同时,提供对小文件的读写功能
    	Chubby 提供对小文件的读写服务,以使得被选举出来的 Master 可以在不依赖额外服务的情况下,非常方便的向所有客户端发布自己的状态信息。具体的,当一个
      客户端成功获取到一个 Chubby 文件锁而成为 Master 之后,就可以继续向这个文件里写入 Master 信息,其他客户端就可以通过读取这个文件得知当前Master信息。

    4.高可用,高可靠
    	在 Chubby 的架构设计中,允许运维人员通过部署多台机器(一般是5台机器)来组成一个 Chubby 集群,从而保证集群的高可用性。基于对 Paxos 算法的实现,对于
      一个由5台机器组成的Chubby 集群来说,只要保证3台正常运行的机器,整个集群对外服务就能够保持可用。
        另外,由于 Chubby 支持通过小文件读写服务的方式来进行Master选举结果的发布与订阅,因此在 Chubby 的实际应用过程中,必须能够支撑成百上千个 Chubby 客户端
      对于同一个文件进行监视和读取。

    5.提供事件通知机制
    	在实际使用过程中,Chubby 客户端需要实时的感知到 Master 的变化情况,当然这可以通过客户端反复轮询来实现,但是在客户端规模不断增大的情况下,客户端主动轮询
      的实时性效果并不是很理想,且对服务器性能和网络带宽压力都非常大。因此,Chubby 需要有能力将服务器的数据变化情况(例如文件内容变更)以事件的形式通知到所有订阅
      的客户端。


2.Chubby 技术架构
	1.系统结构
		Chubby 的整个系统结构主要是由服务端和客户端2个部分组成,客户端通过RPC调用与服务端进行通信。
		一个典型的Chubby集群,或称为 Chubby cell, 通常是由5台服务器组成。这些副本服务器采用 Paxos 协议,通过投票的方式来选举产生一个活的过半投票的服务器
	  作为 Master。一旦某台服务器称为了Master,Chubby 就会保证在一段时期内不会再有其他服务器成为Master---这段时间被称为Master 租期(Master lease)。在
	  运行过程中,Master 服务器会通过不断续租的方式延长 master 租期,而如果master服务器出现故障,那么余下的服务器就会进行新一轮的master选举,最终产生新的
	  master服务器,开始新的master租期。
	    集群中的每一个服务器都维护着一份服务端数据库的副本,但在实际运行过程中,只有master服务器才能对服务器进行写操作,而其他服务器都是使用Paxos协议从master
	  服务器上同步数据库的数据的更新。
	    Chubby 客户端如何定位到 Master 服务器的。Chubby 客户端通过向记录有 chubby 服务端机器列表的 dns 来请求获取所有的chubby 服务器列表,然后逐个发起请求
	  询问该服务器是否是Master。在这个询问过程中,那些非 Master 的服务器,则会将当前 master 所在的服务器标识反馈给客户端,这样客户端就能够非常快的定位到master
	  服务器了。
	    一旦客户端定位到master服务器之后,只要该 master 正常运行,那么客户端就会将所有的请求都发送到该 master 服务器。针对写请求,chubby master 会采用一致性
	  协议将其广播给集群中所有的副本服务器,并且在过半的服务器接受了该写请求之后,再响应给客户端正确的应答。而对于读请求,则不需要在集群内部进行广播处理,直接由
	  master 服务器单独处理。
	    在 chubby 运行过程中,服务器难免故障。如果当前master 服务器崩溃了,那么集群中的其他服务器在 master 租期到了后,重新开始新一轮的master选举。通常,进行
	  一次 master 选举大概要花费几秒钟的时间。而如果是集群中任意一台非master服务器崩溃,那么整个集群是不会停止工作的,这个崩溃的服务器会在恢复之后自动加入到chubby
	  集群中去。新加入的服务器首先需要同步 chubby 最新的数据库数据,完成数据同步之后,新的服务器就可以加入到正常的 Paxos运作流程中与其他服务器副本一起协作工作。
	    如果集群中的一个服务器发生崩溃并在几小时后仍然无法恢复工作,那么就需要加入新的机器,并同时更新dns列表。chubby 服务器的更换方式非常简单,只需要启动chubby
	  服务端程序,然后更新dns上的机器列表(即使用新机器的ip地址替换老机器的ip地址)即可。在chubby运行过程中,master 服务里会将集群数据库中的地址列表做相应的变更,
	  集群内部的其他副本服务器通过复制的方式就可以获取最新的服务器地址列表了。

	2.目录与文件
		chubby 对外提供了一套与unix文件系统非常相近但是更简单的访问接口。chubby的数据结构可以看做是一个由文件和目录组成的树,其中每一个节点都可以表示为一个使用
	  斜杠分隔的字符串,典型的节点路径表示如下:
	  /ls/foo/wombat/pouch
	    其中,ls 是所有chubby 节点所共有的前缀,代表着锁服务,是 Lock Service 的缩写;foo 则代表了 chubby 集群的名字,从dns可以查询到由一个或者多个服务器组成
	  该chubby集群;剩余部分的路劲是 /whobat/pouch 则是一个真正包含业务含义的节点名字,由chubby服务器内部解析并定位到数据节点。
	    chubby 的命名空间,包括文件和目录,我们称之为节点(nodes)。在同一个chubby 集群数据库中,每一个节点都是全局唯一的。和unix一样,每个目录都可以包含一系列
	  的子文件和子目录列表,而每个文件中则会包含文件内容。当然,chubby并非模拟一个完整的文件系统,因此没有符号链接和硬链接的概念。
	    由于chubby的命名结构组成了一个近似标准文件系统的视图,因此chubby的客户端应用程序也可以通过自定义的文件系统访问接口来访问chubby服务端数据,比如可以使用
	  GFS的文件系统访问接口,这就大大减少了用户使用chubby的成本。
	    chubby上的每个数据节点都分为持久节点和临时节点,其中持久节点需要显式调用接口api来删除,而临时节点则会在其对应的客户端会话失效后被自动删除。也就是说,临时
	  节点的生命周期和客户端会话绑定,如果该临时节点对应的文件没有被任何客户端打开,那么它就会删除。因此,临时节点通常可以用来进行客户端会话有效性的判断依据。
	    另外,chubby 上的每个数据节点都包含了少量的元数据信息,其中包括用于权限控制的访问控制列表(ACL)信息。同时,每个节点的元数据中还包括4个单调递增的64位编号,
	  分别如下:
	  1.实例编号
	  	用于chubby创建该数据节点的顺序,节点的创建顺序不同,其实例编号也不同,因此,通过实例编号,即使针对2个名字相同的数据节点,客户端也能够非常方便的识别出是否
	  是一个数据节点---因为创建时间晚的数据节点,其实力编号必定大于任意先前创建的同名节点。
	  2.文件内容编号(只针对文件)
	  	文件内容编号用于标识文件内容的变化情况,该编号会在文件内容被写入时增加
	  3.锁编号
	  	用于标识节点锁状态变更情况,该编号会在节点所从自由(free)状态转换到被持有(held)状态时增加。
	  4.ACL编号
	  	用于标识节点的ACL信息变更情况,该编号会在节点的ACL配置信息被写入时增加。
	  同时,chubby还会标识一个64位的文件内容校验码,以便客户端能够识别出文件是否变更。
	  
	3.锁与锁序列器
		在分布式系统中,锁是一个非常复杂的问题,由于网络通信的不确定性,导致在分布式系统中锁机制变得非常复杂,消息的延迟或是乱序都有可能引起锁的失效。诸如消息接收
	  顺序紊乱引起的数据不一致,典型的解决方案包括虚拟时间和虚拟同步。
	    在 chubby 中,任意一个数据节点都可以充当一个读写锁来使用:一种是单个客户端以排他(写)模式持有这个锁,另外一种则是任意数目的客户端以共享(读)模式持有这个
	  锁。同时,在chubby的锁机制中需要注意的一点是,chubby舍弃了严格的强制锁,客户端可以在没有获得任何锁的情况下访问 chubby 的文件,也就是说,持有锁F既不是访问
	  文件F的必要条件,也不会阻止其他客户端访问文件F。
	    在chubby 中,主要采用锁延迟和锁序列器两种策略来解决上面我们提到的由于消息延迟和重排序引起的分布式锁问题。其中锁延迟是一种比较简单的策略,使用chubby的应用
	  几乎不需要进行任何的代码修改。具体的,如果一个客户端以正常的方式主动释放了一个锁,那么chubby服务端将会允许其他客户端能够立即获得该锁。而如果一个锁是因为客户端
	  的异常情况(如客户端无响应)而被释放的话,那么chubby服务器会为该锁保留一定的时间,我们称之为'锁延迟'(lock-delay),在这段时间内,其他客户端无法获取这个锁。
	  锁延迟措施能够很好的防止一些客户端由于网络闪断等原因与服务器暂时断开的场景出现。总的来说,该方案尽管不完美,但是锁延迟能够有效的保护在出现消息延迟的情况下发生
	  的数据不一致。
	    chubby 提供的另外一种方式是锁序列器,当然该策略需要chubby的上层应用配合在代码中加入下相应的修改逻辑。任何时候,锁的持有者都可以向chubby请求一个锁序列器,
	  其中包括锁的名字,锁模式,以及锁序列号。当客户端应用程序在进行一些需要锁机制保护的操作时,可以将该锁序列器一并发送给服务端。chubby 服务端接收到这样的请求后,
	  首先会检测该序列器是否有效,以及检查客户端是否处于恰当的锁模式;如果没有通过检查,那么服务端就会拒绝该客户端的请求。

	4.Chubby 中的事件通知机制	
		为了避免大量客户端轮询 chubby 服务端状态所带来的压力,chubby 提供了事件通知机制。chubby 的客户端可以向服务端注册事件通知,当触发这些事件的时候,服务端
	  就会向客户端发送对应的事件通知。在chubby的事件通知机制中,消息通知都是通过异步的方式发送给客户端的。常见的chubby如下:
	  1.文件内容变更
	  	例如,BigTable 集群使用 chubby 锁来确定集群中的哪台 BigTable 机器是master,获得锁的 BigTable master 会将自身信息写入 chubby 上对应的文件中。
	  BigTable 集群中其他客户端可以通过监视这个 chubby 文件的变化来确定新的 BigTable master 机器。

	  2.节点删除
	  	当chubby上指定节点被删除的时候,会产生'节点删除'事件,这通常在临时节点当中比较常见,可以利用该特性来间接判断该临时节点对应的客户端会话是否有效。

	  3.子节点增加,删除
	  	当chubby 上指定节点的子节点新增或者是减少时,会产生'子节点新增,删除'事件。

	  4.Master服务器转移
	  	当chubby 服务器发生 master 转移时,会以事件的形式通知客户端。

	5.Chubby 中的缓存
		为了提高 chubby 的性能,同时也是为了减少客户端和服务端之间频繁的读请求对服务端的压力,chubby 除了提高事件通知机制外,还在客户端中实现了缓存,会在
	  客户端对文件内容和元数据信息进行缓存。使用缓存机制在提高系统的整体性能的同时,也为系统带来了一定的复杂性,其中最主要的问题就是如何保证缓存的一致性。在
	  chubby 中,通过租期机制来保证缓存的一致性。
	    chubby 缓存的生命周期和master租期机制紧密相关,master会维护每个客户端的数据缓存情况下,并通过向客户端发送过期信息的方式来保证客户端数据的一致性。
	  在这种情况下,chubby 就能够保证客户端要么能够从缓存中访问到一致的数据,要么访问出错,而一定不会访问到不一致的数据。具体的,每个客户端的缓存都有一个租期,
	  一旦该租期到期,客户端就需要向服务端续订租期以继续维护缓存的有效性。当文件数据或元数据信息被修改时,chubby 服务端首先会阻塞该修改操作,然后由master 向
	  所有可能缓存了该数据的客户端 发送缓存过期信号,以使其缓存失效,等到master 在接收到所有相关客户端对该过期信号的应答(应答包括2种,一类是客户端明确要求缓存
	  更新,另一类则是客户端允许缓存租期过期)后,再继续进行之前的修改操作。
	    chubby 缓存数据保证了强一致性。尽管要保证严格的数据一致性对于性能的开销和系统的吞吐影响很大,但由于弱一致性模型在实际使用过程中极容易出现问题,因此chubby
	  在设计之初就决定了用强一致性。

	6.会话与会话激活
		chubby 客户端和服务端之间通过创建一个tcp连接来进行所有的网络通信操作,我们将这一连接称为会话(session)。会话是有生命周期的,存在一个超时时间,在超时时间内,
	  chubby 客户端和服务端之间可以通过心跳检测来保持会话的活性,以使会话周期得到延续,我们将这个过程称为 KeepAlive(会话激活)。如果能够成功的通过KeepAlive过程
	  将 chubby 会话一直延续下去,那么客户端创建的句柄,锁和缓存数据等依然有效。

	7.KeepAlive 请求
		chubby master 是如何处理客户端的 keepalive 请求的。master 接收到客户端的 keepalive 请求时,首先会将该请求阻塞,并等到该客户端的档期会话租期即将到期时,
	  才为其续租该客户端的会话租期,之后再向客户端响应这个keepalive请求,并同时将最新的会话租期超时时间反馈给客户端。master 对于会话续租时间的设置,默认是12秒,但
	  这不是一个固定的值,chubby 会根据实际的运行情况,自行调节该周期的长短。举个例子,如果当前master处于高负载运行的状态的话,那么master会适当的延长会话租期的长度,
	  以减少客户端keepalive请求的发送频率。客户端在接收到来自master的续租响应后,会立即发起一个新的keepalive 请求,再由master进行阻塞。因此我们可以看出,在正常运行
	  过程中,每一个 chubby 客户端总是会有一个keepalive 请求阻塞在master服务器上。
	    除了为客户端进行会话续租外,master还将通过keepalive响应来传递chubby事件通知和缓存过期通知给客户端。具体的,如果master发现服务端已经触发了针对该客户端的事件
	  通知或缓存过期通知,那么会提前将keepalive响应反馈给客户端。

	8.会话超时
		谈到绘画租期,chubby 客户端也会维持一个和master端近似相同的会话租期。为什么是近似相同?这是因为客户端必须考虑2方面的因素:一方面,keepalive响应在网络传输过程
	  中会花费一定的时间;另一方面,master服务器和chubby客户端存在时钟不一致现象。因此在chubby会话中,存在master端会话租期和客户端本地会话租期。
	    如果chubby客户端在运行过程中,按照本地会话租期超时时间,检测到其会话租期已经过去却尚未收到master的keepalive响应,那么这个时候,它将无法确定master服务端是否已经
	  中止了当前会话,我们称这个时候客户端处于 '危险状态'。此时,chubby 客户端会清空其本地缓存,并将其标记为不可用。同时,客户端还会等待一个'宽限期'的时间周期,这个
	  宽限期默认是45秒。如果在宽限期到期前,客户端和服务端之间成功进行了keepalive,那么客户端就会再次开启本地缓存,否则,客户端就会认为当前会话已经过期了,从而中止
	  本次会话。
	    当客户端进入上诉的危险状态时,chubby 客户端库会通过一个 'jeopardy' 事件来通知上层应用程序。如果恢复正常,客户端同样通知'safe'事件给应用程序让其正常运行。但如果
	  客户端最终没能从危险状态中恢复过来,那么客户端会以'expired'事件来通知应用程序当前 chubby 会话已经超时。chubby 通过这些不同的事件通知,能够很好地辅助上层应用
	  程序在不明确 chubby 会话的情况下,根据不同的事件类型来做出不同的处理:等待或重启。有了这样的保证机制后,对于那些短时间内chubby 服务不可用的场景下,客户端应用
	  程序可以选择等待,而不是重启。

	9.Chubby Master 故障恢复
		chubby 的master 服务器上运行着会话租期计时器,用来管理会话的生命周期。如果在运行过程中master服务器出现了故障,那么该计时器会停止,直到新的master选举
	  产生后,计时器才会继续计时,也就是说,从旧的master崩溃到新的master选举产生所花费的时间将不计入会话超时计算中,这等价于延长了客户端的会话租期。如果新的master
	  在短时间内就选举产生了,那么客户端就可以在本地会话租期过期前与其创建连接。而如果master的选举花费了较长的时间,就会导致客户端只能清空本地的缓存,并进入
	  宽限期进行等待。从这里我们可以看出,由于宽限期的存在,使得会话能够很好的在服务端master转换的过程中得到维持。
	    客户端进入宽限期后,客户端无法确定master上的会话周期是否过期,因此它不会销毁本地会话,而是将 所有应用程序对它的api调用都阻塞,以避免在这个期间进行的api
	  调用导致数据不一致现象。同时,向上层应用程序发送一个 'jeopardy' 事件。
	    chubby 服务端产生新的master后,当客户端向新的master发送请求,master会检测到该客户端的 Master 周期号过期,因此会在keepalive响应中拒绝这个客户的请求,
	  并将最新的master周期号发送给客户端。
	    不难看出,在master转换的这段时间,只要客户端的宽限期足够长,那么客户端应用程序就可以在没有任何察觉的情况下,实现chubby的故障恢复,但如果客户端的宽限期
	  比较短,那么 chubby 客户端就会丢弃当前会话,并将这个异常通知给上层应用程序。
	    一旦客户端与新的master建立上连接之后,客户端和master之间会通过互相配合来实现对故障的平滑恢复。新的master会设法将上一个master服务器的内存状态构造出来,
	  具体的,由于本地数据库记录了每个客户端的会话信息,以及其持有的锁和临时文件等信息,因此 chubby 会通过读取本地磁盘文件上的数据来恢复一部分状态,总的来说,
	  一个新的 chubby master 服务器选举产生以后,会进行如下几个主要的处理:
	  1.新的master 选举产生之后,首先需要确定 master 周期。master 周期是用来唯一标识一个 chubby 集群的master 统治轮次,以便区分不同的master。一旦新的master
	  确定下来之后,master 就会拒绝所有携带其他master周期编号的客户端请求,同时告知其最新的master周期号,需要注意的一点是,只要发生master重新选举,就一定
	  会产生master周期,即使是选举前后master 都是同一台机器。
	  2.选举产生的新的master能够立即对客户端的master寻址进行相应,但是不会立即开始处理客户端会话相关的请求操作
	  3.master根据本地数据库中存储的会话和锁信息,来构建服务器的内存状态
	  4.到现在为止,master 已经能够处理客户端的keepalive请求了,但依然无法处理其他会话相关的请求
	  5.master 会发送一个 'master 故障切换' 事件给每一个会话,客户端接收到这个事件后,会清空它本地缓存,并警告上层应用程序可能已经丢失了别的事件,之后再
	  向master反馈应答。
	  6.此时,master 会一直等待客户端的应答,直到每一个会话都应答了这个切换事件。
	  7.在master接收到了所有客户端的应答之后,就能够处理所有的请求操作了
	  8.如果客户端使用了一个在故障切换之前创建的句柄,master 会重新为其创建这个句柄的内存对象,并执行调用。而如果该句柄在之前的master 周期中已经被关闭
	  了,那么它就不能在这个master周期内再次被重建了---这一机制保证了即使由于网络原因使得master接收到那些延迟或重发的网络数据包,也不会错误的重建一个
	  已经关闭的句柄。


3.Paxos 协议实现
	chubby 服务端的基本架构大致分为三层:
	1.最底层是容错日志系统(Fault-Tolerant Log),通过 Paxos 算法能够保证集群中所有机器上的日志完全一致,同时具备较好的容错性。
	2.日志层之上是key-value 类型的容错数据库(Fault-Tolerant DB),其通过下层的日志来保证一致性和容错性
	3.存储层之上就是 chubby 对外提供的分布式锁服务和小文件存储服务

	Paxos 算法的作用在于保证集群内各个副本节点的日志能够保持一致。chubby 事务日志中的每一个value对应paxos算法中的一个 Instance,由于chubby
  需要对外提供不间断的服务,因此事务日志会不断增长,于是在整个chubby运行过程中,会存在多个Paxos Instance。同时,chubby 会为每个Paxos Instance
  都按序分配一个全局唯一的Instance 编号,并将其顺序写入到事务日志中去。
    在多 paxos instance 模式下,为了提升算法的性能,就必须选出一个副本节点作为 paxos 算法的主节点(简称为 Master),以避免因为每一个 paxos instance
  都能提出提案而陷入多个 Paxos Round 并存的情况。同时,Paxos 会保证在 master 重启或出现故障的情况下而进行切换的时候,允许出现短暂的多个master共存而
  不影响副本之间的一致性。
    在paxos中,每一个paxos instance 都需要进行一轮或者多伦的 'Prepare - Promise - Propose - Accept' 这样完成的二阶段请求过程来完成对一个提案值的
  选定,而多个 instance 之间是完全独立的,每个 instance 可以自己决定一个 Round 的序号,仅仅只需要保证在 instance 内部不会出现序号重复即可。为了在保证
  正确的前提下尽可能的提高算法的性能,可以让多个instance 共用一套序号分配机制,并将 'Prepare - Promise' 合并为一个阶段,具体做法如下:
  	1.当某个副本节点通过选举称为Master后,就会使用新分配的编号N来广播一个Prepare 信息,该 Prepare 消息会被所有未达成一致的instance和目前还未开始的
  	instance 共用
  	2.当 Acceptor 接收到 Prepare 消息后,必须对多个 instance 同时做出回应,这通常可以通过将反馈消息封装在一个数据包中来实现。假设最多允许k个instance
  	同时进行提案值的选定,那么:	
  		1.当前至多存在K个未达成一致的 instance,将这些未决的 instance 各自最后接收的提案值(若该提案值尚未接受任何值,则使用null来代替)封装进一个数据包,
  		并作为 Promise 消息返回
  		2.同时,判断N是否大于当前 Acceptor 的 highestPromisedNum 值(当前已经接收的最大提案编号值),如果大于该值的话,那么就标记这些未决instance和
  		所有未来的instance的 highestPromisedNum 值为 N --- 这样,这些未决 instance 和所有未来 instance 都不能再接收任何编号小于N的提案。
  	3.然后master就可以对所有未决instance 和所有未来instance 分别执行 'Propose - Accept' 阶段的处理。值得注意的是,如果当前master能够一直稳定的
  	运行的话,那么在接下来的算法运行过程中,就不需要再进行 'Prepare - Promise' 的处理了。但是,一旦 master 发现Acceptor 返回了一个 Reject 消息,
  	说明集群中存在另外一个master,并且试图使用更大的提案编号(比如M,M>N)发送了 Prepare 消息。碰到这种情况,当前master就需要重新分配新的提案编号(必须
  	比M更大)并再次进行'Prepare - Promise' 阶段的逻辑处理。

  	利用上述改进的paxos算法,在master稳定的情况下,只需要使用同一个编号来依次执行每一个 instance 的 'Promise - Accept' 阶段逻辑处理。在每个 instance
  的运行过程中,一旦接受到多数派的Accept反馈后,就可以将对应的提案值写入本地事务日志并广播 COMMIT 消息给集群中的其他副本节点,其他副本节点在接收到这个
  COMMIT 消息之后也会将这个提案值写入到事务日志中去。如果某个副本节点因为宕机或者网络原因没有接收到 commit 消息,可以主动向集群中的其他副本节点进行查询。
  因此,我们可以看到,在chubby的 paxos 算法的实现中,只要维持集群中存在多数派的机器能够正常运行,即使其他机器在任意时刻宕机了,也能够保证已经提交的提案
  的安全性。
    至此,我们已经实现了一套满足一致性的日志副本,在此基础上就可以在上层实现一个一致性的状态机副本。

    最后,为了提高整个集群的性能,还有一个改进之处:得益于paxos算法的容错性,只要任意时刻保证多数派的机器能够正常的运行,那么在宕机的瞬间未能真正写入
  到磁盘上(只有调用flush接口,数据才会被写入到物理磁盘)的那一部分事务日志也可以通过从其他正常运行的副本上复制来进行获取,因此不需要实时的进行事务日志的
  flush操作。


4.Hypertable
	是使用 C++ 语言开发的开源的,高性能,可伸缩的数据库,其以 Google 的 BigTable 相关论文作为基础指导,采用与 HBase 非常相似的分布式模型,其
  目的是构建一个针对分布式海量数据的高并发数据库。

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值