集群任务html
主从架构java
在分布式系统设计中一个获得普遍应用的架构:一个主-从(master-worker)架构,该系统中遵循这个架构的一个重要例子是HBase——一个Google的数据存储系统(BigTable)模型的实现,在最高层,主节点服务器(HMaster)负责跟踪区域服务器(HRegionServer)是否可用,并分派区域到服务器。node
master-worker模式面临的问题算法
若是主节点发送错误并失效,系统将没法分配新的任务或从新分配已失败的任务。这就须要重选备份主节点接管主要主节点的角色,进行故障转移,数据恢复等等,更糟的是,若是一些从节点没法与主要主节点通讯,如因为网络分区(network partition)错误致使,这些从节点可能会中止与主要主节点的通讯,而与第二个主要主节点创建主-从关系。针对这个场景中致使的问题,咱们通常称之为脑裂(split-brain):系统中两个或者多个部分开始独立工做,致使总体行为不一致性。咱们须要找出一种方法来处理主节点失效的状况,关键是咱们须要避免发生脑裂的状况。数据库
若是从节点崩溃,已分配的任务将没法完成。若是从节点崩溃了,全部已派发给这个从节点且还没有完成的任务须要从新派发。其中首要需求是让主节点具备检测从节点的崩溃的能力。主节点必须可以检测到从节点的崩溃,并肯定哪些从节点是否有效以便派发崩溃节点的任务。一个从节点崩溃时,从节点也许执行了部分任务,也许所有执行完,但没有报告结果。若是整个运算过程产生了其余做用,咱们还有必要执行某些恢复过程来清除以前的状态。安全
若是主节点和从节点之间没法进行信息交换,从节点将没法得知新任务分配给它。若是一个从节点与主节点的网络链接断开,好比网络分区(network partition)致使,从新分配一个任务可能会致使两个从节点执行相同的任务。若是一个任务容许屡次执行,咱们在进行任务再分配时能够不用验证第一个从节点是否完成了该任务。若是一个任务不容许,那么咱们的应用须要适应多个从节点执行相同任务的可能性。bash
主从模式总结服务器
这是关键的一步,使得主节点能够给从节点分配任务。网络
主节点必须具备检测从节点崩溃或失去链接的能力。session
主节点必须具备知道哪个从节点能够执行任务的能力。
主节点和从节点必须具备经过某种可靠的方式来保存分配状态和执行状态的能力。
指望
理想的方式是,以上每个任务都须要经过原语(内核或微核提供核外调用的过程或函数称为原语(primitive))的方式暴露给应用,对开发者彻底隐藏实现细节。ZooKeeper提供了实现这些原语的关键机制,所以,开发者能够经过这些实现一个最适合他们需求、更加关注应用逻辑的分布式应用。
什么是zookeeper
来源
Zookeeper 最先起源于雅虎研究院的一个研究小组。在当时,研究人员发现,在雅虎内部不少大型系统基本都须要依赖一个相似的系统来进行分布式协调,可是这些系统每每都存在分布式单点问题。因此,雅虎的开发人员就试图开发一个通用的无单点问题的分布式协调框架,以便让开发人员将精力集中在处理业务逻辑上。
zookeeper是什么
ZooKeeper是一种用于分布式应用程序的高性能协调服务.
ZooKeeper is a high-performance coordination service for distributed applications. It exposes common services - such as naming, configuration management, synchronization, and group services - in a simple interface so you don't have to write them from scratch. You can use it off-the-shelf to implement consensus, group management, leader election, and presence protocols. And you can build on it for your own, specific needs.
ZooKeeper是一个典型的分布式数据一致性解决方案,其设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。分布式应用程序能够基于 ZooKeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。
初识
zk架构
角色
Leader
Leader做为整个ZooKeeper集群的主节点,负责响应全部对ZooKeeper状态变动的请求。它会将每一个状态更新请求进行排序和编号,以便保证整个集群内部消息处理的FIFO。
Follower
Follower主要是响应本服务器上的读请求外,另外follower还要处理leader的提议,并在leader提交该提议时在本地也进行提交。另外须要注意的是,leader和follower构成ZooKeeper集群的法定人数,也就是说,只有他们才参与新leader的选举、响应leader的提议。
Observe
为客户端提供读服务器,若是是写服务则转发给Leader。不参与选举过程当中的投票,也不参与“过半写成功”策略。在不影响写性能的状况下提高集群的读性能。
client
链接zookeeper服务器的使用着,请求的发起者。独立于zookeeper服务器集群以外的角色。
数据模型znode
ZAB协议
特色
运用场景
Standalone模式演示开始,本地启动
配置
ZooKeeper 中使用的基本时间单元, 以毫秒为单位, 默认值是 2000。它用来调节心跳和超时。
默认值是 10, 即 tickTime 属性值的 10 倍。它用于配置容许 followers 链接并同步到 leader 的最大时间。若是 ZooKeeper 管理的数据量很大的话能够增长这个值。
默认值是 5, 即 tickTime 属性值的 5 倍。它用于配置leader 和 followers 间进行心跳检测的最大延迟时间。若是在设置的时间内 followers 没法与 leader 进行通讯, 那么 followers 将会被丢弃。
ZooKeeper 用来存储内存数据库快照的目录, 而且除非指定其它目录, 不然数据库更新的事务日志也将会存储在该目录下。
服务器监听客户端链接的端口, 也即客户端尝试链接的端口, 默认值是 2181。
/bin/命令
监控命令
在客户端能够经过 telnet 或 nc 向 ZooKeeper 提交相应的服务信息查询命令。使用方式
echo mntr | nc localhost 2181
.
复制模式配置
zookeeper集群模式下还要配置一个myid文件,这个文件须要放在dataDir目录下,文件中写入一个id便可。
server.1= 192.168.1.9:2888:3888
server.2= 192.168.1.124:2888:3888
server.3= 192.168.1.231:2888:3888
复制代码
其中,id 被称为 Server ID,用来标识该机器在集群中的机器序号(在每台机器的 dataDir 目录下建立 myid 文件,文件内容即为该机器对应的 Server ID 数字)。host 为机器 IP,port1 用于指定 Follower 服务器与 Leader 服务器进行通讯和数据同步的端口,port2 用于进行 Leader 选举过程当中的投票通讯。
核心概念
数据模型znode
存储
Zookeeper的数据模型是树结构,在内存数据库中,存储了整棵树的内容,包括全部的节点路径、节点数据、ACL信息,Zookeeper会定时将这个数据存储到磁盘上。
DataTree是内存数据存储的核心,是一个树结构,表明了内存中一份完整的数据。DataTree不包含任何与网络、客户端链接及请求处理相关的业务逻辑,是一个独立的组件。
DataNode是数据存储的最小单元,其内部除了保存告终点的数据内容、ACL列表、节点状态以外,还记录了父节点的引用和子节点列表两个属性,其也提供了对子节点列表进行操做的接口。
Zookeeper的内存数据库,管理Zookeeper的全部会话、DataTree存储和事务日志。ZKDatabase会定时向磁盘dump快照数据,同时在Zookeeper启动时,会经过磁盘的事务日志和快照文件恢复成一个完整的内存数据库。
事务日志指zookeeper系统在正常运行过程当中,针对全部的更新操做,在返回客户端“更新成功”的响应前,zookeeper会保证已经将本次更新操做的事务日志已经写到磁盘上,只有这样,整个更新操做才会生效。
临时(Ephemeral)znode
持久(PERSISTENT)znode
顺序(SEQUENTIAL)znode
zxid
zookeeper znode stat 结构
ZooKeeper Sessions
ZooKeeper的每一个客户端都维护一组服务端信息,在建立链接时由应用指定,客户端随机选择一个服务端进行链接,链接成功后,服务端为每一个链接分配一个惟一标识。客户端在建立链接时能够指定溢出时间,客户端会周期性的向服务端发送PING请求来保持链接,当客户端检测到与服务端断开链接后,客户端将自动选择服务端列表中的另外一个服务端进行重连。
建立会话
ZooKeeper zk = new ZooKeeper(serverList, sessionTimeout, watcher);
zk.create("/test", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
复制代码
建立客户端session时,应用必须传入一组以逗号分隔的host:port列表,每一个都对应一个ZooKeeper服务端,ZooKeeper客户端将选择任意一个服务端并尝试与其链接(这组serverlist会在初始化的时候打乱),若是链接失败,或者因为某些缘由致使客户端与服务端链接断开,客户端将自动的选择列表中的另外一个服务端进行链接,直到成功。当session建立成功后,ZooKeeper服务端为session分配一个惟一标识。
client进行tcp创建链接
当tcp链接成功以后,client发送一个ConnectRequest包,将ZooKeeper构造函数传入的sessionTimeout数值发给Server。zookeeper server会验证客户端发来的sessionTimeout值;zookeeper server中有连个配置项.
(tickTime也是一个配置项。是Server内部控制时间逻辑的最小时间单位)
若是客户端发来的sessionTimeout超过min-max这个范围,server会自动截取为min或max.
server等表决经过后,会为这个session生成一个password,连同sessionId,sessionTimeOut一块儿返回给客户端(ConnectResponse)。客户端若是须要重连Server,能够新建一个ZooKeeper对象,将上一个成功链接的ZooKeeper 对象的sessionId和password传给Server ZooKeeper zk = new ZooKeeper(serverList, sessionTimeout, watcher, sessionId,passwd);ZKServer会根据sessionId和password为同一个client恢复session,若是尚未过时的话。
会话状态
Zookeeper会话在整个运行期间的生命周期中,会在不一样的会话状态中之间进行切换,这些状态能够分为CONNECTING, ASSOCIATING, CONNECTED, CLOSED, AUTH_FAILED。
一旦客户端开始建立Zookeeper对象,那么客户端状态就会变成CONNECTING状态,同时客户端开始尝试链接服务端,链接成功后,客户端状态变为CONNECTED,一般状况下,因为断网或其余缘由,客户端与服务端之间会出现断开状况,一旦碰到这种状况,Zookeeper客户端会自动进行重连服务,同时客户端状态再次变成CONNCTING,直到从新连上服务端后,状态又变为CONNECTED,在一般状况下,客户端的状态老是介于CONNECTING和CONNECTED之间。可是,若是出现诸如会话超时、权限检查或是客户端主动退出程序等状况,客户端的状态就会直接变动为CLOSE状态。
session激活
在ZooKeeper中,服务器和客户端之间维持的是一个长链接,在 SESSION_TIMEOUT 时间内,服务器会肯定客户端是否正常链接(客户端会定时向服务器发送heart_beat),服务器重置下次SESSION_TIMEOUT时间。;同时在Zookeeper的实际设计中,只要客户端有请求发送到服务端,那么就会触发一次会话激活,总结下来两种状况都会触发会话激活。
会话清理
leader server的SessionTracker管理线程会管理者session,执行session的过时检查,若是会话过时就执行清理操做.
会话重连
客户端链接指定根路径
在ZooKeeper 3.2.0增长了可选的“chroot”后缀,能够改变当前客户端的根路径。例如,若是使用”localhost:2181/app/a”,客户端将使用”/app/a”做为其根路径,全部的路径都会相对于该路径。好比操做路径”/foo/bar”将真正对应到”/app/a/foo/bar”。这个特征在多租户环境下是很是有用的,能够简化客户端的应用逻辑。
ZooKeeper Watches
在ZooKeeper中,全部的读操做(getData,getChildren和exists)均可以设置监听,一个Watch事件是一个一次性的触发器,当被设置了Watch的数据发生了改变的时候,则服务器将这个改变发送给设置了Watch的客户端,以便通知它们。
zookeeper机制的特色
当数据改变的时候,那么一个Watch事件会产生而且被发送到客户端中。可是客户端只会收到一次这样的通知,若是之后这个数据再次发生改变的时候,以前设置Watch的客户端将不会再次收到改变的通知,由于Watch机制规定了它是一个一次性的触发器。
这个代表了Watch的通知事件是从服务器发送给客户端的,是异步的,这就代表不一样的客户端收到的Watch的时间可能不一样,可是ZooKeeper有保证:当一个客户端在看到Watch事件以前是不会看到结点数据的变化的。例如:A=3,此时在上面设置了一次Watch,若是A忽然变成4了,那么客户端会先收到Watch事件的通知,而后才会看到A=4。
znode 节点自己具备不一样的改变方式,setData() 会触发设置在某一节点上所设置的数据监视(假定数据设置成功),而一次成功的 create() 操做则会出发当前节点上所设置的数据监视以及父节点的子节点监视。一次成功的 delete() 操做将会触发当前节点的数据监视和子节点监视事件,同时也会触发该节点父节点的child watch。WatchEvent是最小的通讯单元,结构上只包含通知状态、事件类型和节点路径。ZooKeeper服务端只会通知客户端发生了什么,并不会告诉具体内容。
监听事件类型
ACL 权限控制
zk作为分布式架构中的重要中间件,一般会在上面以节点的方式存储一些关键信息,默认状况下,全部应用均可以读写任何节点,在复杂的应用中,这不太安全,ZK经过ACL机制来解决访问权限问题.
回顾zookeeper架构
ZAB协议
ZAB协议(Zookeeper Atomic Broadcast Protocol)是Zookeeper系统专门设计的一种支持崩溃恢复的原子广播协议。Zookeeper使用该协议来实现分布数据一致性并实现了一种主备模式的系统架构来保持各集群中各个副本之间的数据一致性。采用zab协议的最大目标就是创建一个高可用可扩展的分布式数据主备系统。即在任什么时候刻只要leader发生宕机,都能保证分布式系统数据的可靠性和最终一致性。
特色
ZAB协议工做原理
ZAB协议要求每一个leader都要经历三个阶段,即发现,同步,广播。
* 发现:即要求zookeeper集群必须选择出一个leader进程,同时leader会维护一个follower可用列表。未来客户端能够与这个follower中的节点进行通讯。
* 同步:leader要负责将自己的数据与follower完成同步,作到多副本存储。这样也是体现了CAP中高可用和分区容错。follower将队列中未处理完的请求消费完成后,写入本地事物日志中。
* 广播:leader能够接受客户端新的proposal请求,将新的proposal请求广播给全部的follower。
复制代码
ZAB两种模式
当服务初次启动,或者 leader 节点挂了,系统就会进入恢复模式,直到选出了有合法数量 follower 的新 leader,而后新 leader 负责将整个系统同步到最新状态。
Zab 协议中,全部的写请求都由 leader 来处理。正常工做状态下,leader 接收请求并经过广播协议来处理。
选举
问题: 如何选举leader
某个服务能够配置为多个实例共同构成一个集群对外提供服务。其每个实例本地都存有冗余数据,每个实例均可以直接对外提供读写服务。在这个集群中为了保证数据的一致性,须要有一个Leader来协调一些事务。那么问题来了:如何肯定哪个实例是Leader呢?
分布式选举算法
zookeeper选主
搞清楚几个问题
在ZooKeeper集群中,Server的信息都在zoo.conf配置文件中,根据配置文件的信息就能够知道其它Server的信息。
Leader要具备最高的zxid;集群中大多数的机器(至少n/2+1)获得响应并follow选出的Leader。
ZooKeeper中每个Server都有一个ID,这个ID是不重复的,若是遇到这样的状况时,ZooKeeper就推荐ID最大的哪一个Server做为Leader。
Leader定时向Fllower发ping消息,Fllower定时向Leader发ping消息,当发现Leader没法ping通时,就改变本身的状态(LOOKING),发起新的一轮选举。
leader选主时机
核心概念
ZooKeeper服务器状态
myid
每一个Zookeeper服务器,都须要在数据文件夹下建立一个名为myid的文件,该文件包含整个Zookeeper集群惟一的ID(整数)。例如某Zookeeper集群包含三台服务器,hostname分别为zoo一、zoo2和zoo3,其myid分别为一、2和3,则在配置文件中其ID与hostname必须一一对应,以下所示。在该配置文件中,server.后面的数据即为myid.
server.1=zoo1:2888:3888
server.2=zoo2:2888:3888
server.3=zoo3:2888:3888
复制代码
zxid
每次对Zookeeper的状态的改变都会产生一个zxid(ZooKeeper Transaction Id),zxid是全局有序的,若是zxid1小于zxid2,则zxid1在zxid2以前发生。为了保证顺序性,该zkid必须单调递增。所以Zookeeper使用一个64位的数来表示,高32位是Leader的epoch,从1开始,每次选出新的Leader,epoch加一。低32位为该epoch内的序号,每次epoch变化,都将低32位的序号重置。这样保证了zkid的全局递增性。
logicalclock
每一个服务器会维护一个自增的整数,名为logicalclock,它表示这是该服务器发起的第多少轮投票。
选主步骤
服务器启动的时候每一个server的状态时Looking,若是是leader挂掉后进入选举,那么余下的非Observer的Server就会将本身的服务器状态变动为Looking,而后开始进入Leader的选举状态;
Zookeeper规定全部有效的投票都必须在同一轮次中。每一个服务器在开始新一轮投票时,会先对本身维护的logicalclock进行自增操做。
每一个服务器在广播本身的选票前,会将本身的投票箱清空。该投票箱记录了所收到的选票。例:服务器2投票给服务器3,服务器3投票给服务器1,则服务器1的投票箱为(2, 3), (3, 1), (1, 1)。票箱中只会记录每一投票者的最后一票,如投票者更新本身的选票,则其它服务器收到该新选票后会在本身票箱中更新该服务器的选票。
每一个server会产生一个(sid,zxid)的投票,系统初始化的时候zxid都是0,若是是运行期间,每一个server的zxid可能都不一样,这取决于最后一次更新的数据。将投票发送给集群中的全部机器;
服务器会尝试从其它服务器获取投票,并记入本身的投票箱内。若是没法获取任何外部投票,则会确认本身是否与集群中其它服务器保持着有效链接。若是是,则再次发送本身的投票;若是否,则立刻与之创建链接。
收到外部投票后,首先会根据投票信息中所包含的logicalclock来进行不一样处理. * 外部投票的logicalclock大于本身的logicalclock。说明该服务器的选举轮次落后于其它服务器的选举轮次,当即清空本身的投票箱并将本身的logicalclock更新为收到的logicalclock,而后再对比本身以前的投票与收到的投票以肯定是否须要变动本身的投票,最终再次将本身的投票广播出去。 * 外部投票的logicalclock小于本身的logicalclock。当前服务器直接忽略该投票,继续处理下一个投票。 * 外部投票的logickClock与本身的相等。当时进行选票PK。
对本身的投票和接收到的投票进行PK: 1. 先检查zxid,较大的优先为leader; 2. 若是zxid同样,sid较大的为leader; 3. 根据PK结果更新本身的投票,在次发送本身的投票;
每次投票后,服务器统计投票信息,若是有过半机器接收到相同的投票,那么leader产生,若是否,那么进行下一轮投票;
一旦肯定了Leader,server会更新本身的状态为Following或者是Leading。选举结束。
几种leader选举场景
举例
集群启动选举
Follower重启选举
Leader重启选举
数据同步
在完成leader选举阶段后,准Leader能够获取集群中最新的提议历史。准Leader在该阶段会把最新的提议历史同步到集群中的全部节点。当同步完成时(过半),准Leader才会真正成为Leader,执行Leader的工做。
原子广播
分布式一致问题
分布式中有这么一个疑难问题,客户端向一个分布式集群的服务端发出一系列更新数据的消息,因为分布式集群中的各个服务端节点是互为同步数据的,因此运行完客户端这系列消息指令后各服务端节点的数据应该是一致的,但因为网络或其余缘由,各个服务端节点接收到消息的序列可能不一致,最后致使各节点的数据不一致。
分布式一致性
CAP
分布式系统的最大难点,就是各个节点的状态如何同步。CAP 定理是这方面的基本定理,也是理解分布式系统的起点。
写操做以后的读操做,必须返回该值。
意思是只要收到用户的请求,服务器就必须给出回应。每次请求都能获取到非错的响应——可是不保证获取的数据为最新数据。
区间通讯可能失败。
这三个基本需求,最多只能同时知足其中的两项,一致性和可用性不可能同时成立,由于可能通讯失败(即出现分区容错)。
拜占庭问题
11位拜占庭将军去打仗, 他们各自有权力观测敌情并做出判断, 进攻或撤退, 那么怎么让他们只用传令兵达成一致呢?一种很符合直觉的方法就是投票,每位将军做出决定后都将结果"广播"给其他全部将军, 这样全部将军都能得到一样的11份(包括本身)结果, 取多数, 便可获得全军都赞成的行为.但若是这11位将军中有间谍呢? 假设有9位忠诚的将军, 5位判断进攻, 4位判断撤退, 还有2个间谍恶意判断撤退, 虽然结果是错误的撤退, 但这种状况彻底是容许的. 由于这11位将军依然保持着状态一致性。
一致性解决方案
2PC和3PC
2PC
第一阶段:准备阶段(投票阶段)和第二阶段:提交阶段(执行阶段)。
3PC
在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段以前各参与节点的状态是一致的。引入超时机制,同时在协调者和参与者中都引入超时机制。
区别
相对于2PC,3PC主要解决的单点故障问题,并减小阻塞,由于一旦参与者没法及时收到来自协调者的信息以后,他会默认执行commit。而不会一直持有事务资源并处于阻塞状态。可是这种机制也会致使数据一致性问题,由于,因为网络缘由,协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时以后执行了commit操做。这样就和其余接到abort命令并执行回滚的参与者之间存在数据不一致的状况。
总结
不管是二阶段提交仍是三阶段提交都没法完全解决分布式的一致性问题。那么世上只有一种一致性算法,那就是Paxos,全部其余一致性算法都是Paxos算法的不完整版。
Paxos
ZAB原子广播(数据一致原理)
paxos理论到实际是个艰难的过程。好比怎样在分布式环境下维持一个全局惟一递增的序列,若是是靠数据库的自增主键,那么整个系统的稳定和性能的瓶颈全都集中于这个单点。paxos算法也没有限制Proposer的个数,Proposer个数越多,那么达成一致所形成的碰撞将越多,甚至产生活锁,若是限制Proposer的个数为一个,那么就要考虑惟一的Proposer崩溃要怎么处理。
工做步骤
扩展
Curator是Netflix公司开源的一套Zookeeper客户端框架。了解过Zookeeper原生API都会清楚其复杂度。Curator帮助咱们在其基础上进行封装、实现一些开发细节,包括接连重连、反复注册Watcher和NodeExistsException等。
总体回顾
思考问题