注册中心是 CP 还是 AP 系统?
一个就是 CP 系统,追求数据的强一致性。还有一个是 AP 系统,追求高可用与最终一致。
可以参考图书《从Paxos到Zookeeper:分布式一致性原理与实践》
对于本地事务处理或者是集中式的事务处理系统,很显然我们可以采用已经被实践证明很成熟的ACID模型来保证数据的严格一致性
原子性(A):
所有的系统都受惠于原子性操作。当我们考虑可用性的时候,没有理由去改变分区两侧操作的原子性。而且满足ACID定义的、高抽象层次的原子操作,实际上会简化分区恢复。
一致性(C):
ACID的C指的是事务不能破坏任何数据库规则,如键的唯一性。与之相比,CAP的C仅指单一副本这个意义上的一致性,因此只是ACID一致性约束的一个严格的子集。ACID一致性不可能在分区过程中保持,因此分区恢复时需要重建ACID一致性。推而广之,分区期间也许不可能维持某些不变性约束,所以有必要仔细考虑哪些操作应该禁止,分区后又如何恢复这些不变性约束。
隔离性(I):
隔离是CAP理论的核心:如果系统要求ACID隔离性,那么它在分区期间最多可以在分区一侧维持操作。事务的可串行性(serializability)要求全局的通信,因此在分区的情况下不能成立。只要在分区恢复时进行补偿,在分区前后保持一个较弱的正确性定义是可行的。
持久性(D):
牺牲持久性没有意义,理由和原子性一样,虽然开发者有理由(持久性成本太高)选择BASE风格的软状态来避免实现持久性。这里有一个细节,分区恢复可能因为回退持久性操作,而无意中破坏某项不变性约束。但只要恢复时给定分区两侧的持久性操作历史记录,破坏不变性约束的操作还是可以被检测出来并修正的。通常来讲,让分区两侧的事务都满足ACID特性会使得后续的分区恢复变得更容易,并且为分区恢复时事务的补偿工作奠定了基本的条件。
CAP理论告诉我们,一个分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)和分区容错性(P:Partition tolerance)这三个基本需求,最多只能同时满足其中的两项。
一致性
在分布式环境中,一致性是指数据在多个副本之间是否能够保持一致的特性。在一致性的需求下,当一个系统在数据一致的状态下执行更新操作后,应该保证系统的数据仍然处于一致的状态。
对于一个将数据副本分布在不同分布式节点上的系统来说,如果对第一个节点的数据进行了更新操作并且更新成功后,却没有使得第二个节点上的数据得到相应的更新,于是在对第二个节点的数据进行读取操作时,获取的依然是老数据(或称为脏数据),这就是典型的分布式数据不一致情况。在分布式系统中,如果能够做到针对一个数据项的更新操作执行成功后,所有的用户都可以读取到其最新的值,那么这样的系统就被认为具有强一致性(或严格的一致性)。
简单理解:分布式系统如何保证多系统处理数据如同单台机器符合ACID一样。
可用性:
可用性是指系统提供的服务必须一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。这里我们重点看下“有限的时间内”和“返回结果”。
比如我们需要关注的接口指标TP99、TP999的响应时间。
“返回结果”是可用性的另一个非常重要的指标,它要求系统在完成对用户请求的处理后,返回一个正常的响应结果。正常的响应结果通常能够明确地反映出对请求的处理结果,即成功或失败,而不是一个让用户感到困惑的返回结果。
分区容错性:
分区容错性约束了一个分布式系统需要具有如下特性:分布式系统在遇到任何网络分区故障的时候,仍然需要能够保证对外提供满足一致性和可用性的服务,除非是整个网络环境都发生了故障。
从CAP定理中我们可以看出,一个分布式系统不可能同时满足一致性、可用性和分区容错性这三个需求。另一方面,需要明确的一点是,对于一个分布式系统而言,分区容错性可以说是一个最基本的要求。为什么这样说,其实很简单,因为既然是一个分布式系统,那么分布式系统中的组件必然需要被部署到不同的节点,否则也就无所谓分布式系统了,因此必然出现子网络。而对于分布式系统而言,网络问题又是一个必定会出现的异常情况,因此分区容错性也就成为了一个分布式系统必然需要面对和解决的问题。因此系统架构设计师往往需要把精力花在如何根据业务特点在C(一致性)和A(可用性)之间寻求平衡。
BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写。
BASE是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结,是基于CAP定理逐步演化而来的,其核心思想是即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
基本可用(Basically Available):
基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性——但请注意,这绝不等价于系统不可用。以下两个就是“基本可用”的典型例子。
响应时间上的损失:正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果,但由于出现故障(比如系统部分机房发生断电或断网故障),查询结果的响应时间增加到了1~2秒。
功能上的损失:正常情况下,在一个电子商务网站上进行购物,消费者几乎能够顺利地完成每一笔订单,但是在一些节日大促购物高峰的时候,由于消费者的购物行为激增,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面。
弱状态(Soft State):
弱状态也称为软状态,和硬状态相对,是指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。
最终一致性(Eventually consistent):
最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
最终一致性的5种变种:
因果一致性(Causal consistency)
因果一致性是指,如果进程A在更新完某个数据项后通知了进程B,那么进程B之后对该数据项的访问都应该能够获取到进程A更新后的最新值,并且如果进程B要对该数据项进行更新操作的话,务必基于进程A更新后的最新值,即不能发生丢失更新情况。与此同时,与进程A无因果关系的进程C的数据访问则没有这样的限制。
读己之所写(Read your writes)
读己之所写是指,进程A更新一个数据项之后,它自己总是能够访问到更新过的最新值,而不会看到旧值。也就是说,对于单个数据获取者来说,其读取到的数据,一定不会比自己上次写入的值旧。因此,读己之所写也可以看作是一种特殊的因果一致性。
会话一致性(Session consistency)
会话一致性将对系统数据的访问过程框定在了一个会话当中:系统能保证在同一个有效的会话中实现“读己之所写”的一致性,也就是说,执行更能操作之后,客户端能够在同一个会话中始终读取到该数据项的最新值。
单调读一致性(Monotonic read consistency)
单调读一致性是指如果一个进程从系统中读取出一个数据项的某个值后,那么系统对于该进程后续的任何数据访问都不应该返回更旧的值。
单调写一致性(Monotonic write consistency)
单调写一致性是指,一个系统需要能够保证来自同一个进程的写操作被顺序地执行。
实现一个注册服务中心需要满足的功能:
注册中心一般包含如下几个功能:
服务发现:
服务注册/反注册:保存服务提供者和服务调用者的信息
服务订阅/取消订阅:服务调用者订阅服务提供者的信息,最好有实时推送的功能
服务路由(可选):具有筛选整合服务提供者的能力。
服务配置(不包括其它无关配置):
配置订阅:服务提供者和服务调用者订阅微服务相关的配置
配置下发(可选):主动将配置推送给服务提供者和服务调用者
服务健康检测:
检测服务提供者的健康情况
初识Zookeeper:
Zookeeper是Hadoop的子项目,是一个开源的分布式协调服务框架,chubby是google的,完全实现paxos算法,不开源。zookeeper是chubby的开源实现,使用zab协议,paxos算法的变种。
zookeeper的应用场景:
分布式通知/协调、负载均衡、配置中心、命名服务、数据发布/订阅、集群管理、Master选举、分布式锁、分布式队列等。
ZooKeeper可以保证如下分布式一致性特性。
- 顺序一致性
从同一个客户端发起的事务请求,最终将会严格按照其发起顺序被应用到ZooKeeper中。- 原子性
所有事务请求的结果在集群中所有机器上的应用情况是一致的,也就是说要么整个集群所有集群都成功应用了某一个事务,要么都没有应用,一定不会出现集群中部分机器应用了该事务,而另外一部分没有应用的情况。- 单一视图
无论客户端连接的是哪个ZooKeeper服务器,其看到的服务端数据模型都是一致的。- 可靠性
一旦服务端成功地应用了一个事务,并完成对客户端的响应,那么该事务所引起的服务端状态变更将会被一直保留下来,除非有另一个事务又对其进行了变更。- 实时性
通常人们看到实时性的第一反应是,一旦一个事务被成功应用,那么客户端能够立即从服务端上读取到这个事务变更后的最新数据状态。这里需要注意的是,ZooKeeper仅仅保证在一定的时间段内,客户端最终一定能够从服务端上读取到最新的数据状态。
Zookeeper的设计目标
目标一:简单的数据模型
共享的、树型结构名字空间(指Zookeeper服务器内存中的一个数据模型,由一系列ZNode的数据节点组成,就像文件系统目录一样,不过与传统磁盘文件系统不同的是,将全量数据存储在内存中,来实现提高服务器吞吐和减少延迟的目的)相互协调
目标二:可以构建集群
组成zookeeper集群的每台机器都会在内存中维护当前的服务器状态,并且每台机器之间都互相保持通信。假设集群中存在超过一半的机器能够正常工作,那么整个集群就能正常对外服务。
目标三:顺序访问
全局唯一的递增编号,编号反映了所有事务操作的先后顺序。
目标四:高性能
3台zookeeper服务器,100%读请求的场景下压测结果12~13w的QPS
基本概念
- 集群角色:leader、foller、observer。
- 会话session:默认端口2181,客户端启动建立服务器之间的TCP连接,会话开始通过心跳检测与服务器保持有效的会话,同时可以接收服务器的Watch事件。如果由于外来压力或者网络故障,客户端问题,则在sessionTimeout结束之前重新连接依然有效。
- 数据节点
节点分为两类:一类构成集群的机器,机器节点
另一类存储数据,数据节点(持久节点,临时节点),每个节点添加一个特殊的属性SEQUENTIAL为了在每个节点后面加一个整型数字- 版本
zookeeper会维护一个叫做stat队形,state记录了ZNode的是那个数据版本,分别是version(当前版本)aversion(当前ZNode的ACL版本),cversion(Zode子节点版本)- watcher(事件监听器)
是ZooKeeper中一个很重要的特性。ZooKeeper允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发的时候,ZooKeeper服务端会将事件通知到感兴趣的客户端上去。该机制是ZooKeeper实现分布式协调服务的重要特性。- ZooKeeper采用ACL(Access Control Lists)策略来进行权限控制。ZooKeeper定义了如下5种权限。
CREATE: 创建子节点的权限。
READ: 获取节点数据和子节点列表的权限。
WRITE:更新节点数据的权限。
DELETE: 删除子节点的权限。
ADMIN: 设置节点ACL的权限。
注意:CREATE 和 DELETE 都是针对子节点的权限控制
ZAB协议(zookeeper Atomic Broadcast原子消息广播协议,是一种特别为zookeeper设计的奔溃可恢复的原子消息广播算法)。
基于ZAB协议,ZooKeeper实现了一种主备模式(Leader、Follower)的系统架构来保持集群中各副本之间数据的一致性。
具体的,ZooKeeper使用了一个单一的主进程(Leader)来接收并处理客户端的所有事务请求,并采用ZAB的原子广播协议,将服务器数据的状态变更以事务Proposal的形式广播到所有的副本进程上去(Follower)。ZAB协议的这个主备模型架构保证了同一时刻集群中只能有一个主进程来广播服务器的状态变更,因此能够很好地处理客户端大量的并发请求。另一方面,考虑到分布式环境中,顺序执行的一些状态变更其前后会存在一定的依赖关系,有些状态变更必须依赖于比它早生成的那些状态变更,例如变更C需要依赖变更A和变更B。这样的依赖关系也对ZAB协议提出了一个要求:ZAB协议必须能够保证一个全局的变更序列被顺序应用。也就是说,ZAB协议需要保证如果一个状态变更已经被处理了,那么所有依赖的状态变更都应该已经被提前处理掉了。最后,考虑到主进程在任何时候都有可能出现崩溃退出或重启现象,因此,ZAB协议还需要做到在当前主进程出现上述异常情况的时候,依然能够正常工作。
ZAB协议的核心是定义了对应那些会改变ZooKeeper服务器数据状态的事务请求的处理方式,即:
所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为Leader服务器,而剩下的其他服务器则成为Follower服务器。Leader服务器负责将一个客户端事务请求转换成一个事务Proposal(提案)并将该Proposal分发给集群中所有的Follower服务器。之后Leader服务器需要等待所有Follower服务器的反馈,一旦超过半数的Follower服务器进行了正确的反馈后,Leader就会再次向所有的Follower服务器分发Commit消息,要求对刚才的Proposal进行提交
ZAB协议包括两种基本的模式,分别是崩溃恢复和消息广播。
在整个ZooKeeper集群启动过程中,
或是当Leader服务器出现网络中断、崩溃退出与重启等异常情况时,ZAB协议就会进入恢复模式并选举产生新的Leader服务器。
当选举产生了新的Leader服务器,同时集群中有过半的机器与该Leader服务器完成了状态同步之后,ZAB协议就会退出恢复模式。其中,状态同步是指数据同步,用来保证集群中存在过半的机器能够和Leader服务器的数据状态保持一致。
崩溃恢复模式包括两个阶段:Leader选举和数据同步。
当集群中有过半的Follower服务器完成了和Leader服务器的状态同步,那么整个集群就可以进入消息广播模式了。
当一台同样遵守ZAB协议的服务器启动后加入到集群中时候,如果此时已经存在一个leader服务器在负责进行消息广播,那么新加入的服务器就会自觉的进入数据恢复模式:找到leader所在的服务器,并与其进行数据同步,然后一起参与道消息广播流程中去。
总结:
- 奔溃恢复—》主要就是leader选举过程
- 数据同步—》Leader服务器与其他服务器进行数据同步
- 消息广播—》Leader服务器将数据发送给其他服务器
集群角色下的选举机制
在ZooKeeper中,有三种角色:
Leader
Follower
Observer
一个ZooKeeper集群同一时刻只会有一个Leader,其他都是Follower或Observer。
ZooKeeper配置很简单,每个节点的配置文件(zoo.cfg)都是一样的,只有myid文件不一样。myid的值必须是zoo.cfg中server.{数值}的{数值}部分。
zoo.cfg文件内容示例:
maxClientCnxns=0
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
dataDir=/var/lib/zookeeper/data
# the port at which the clients will connect
clientPort=2181
# the directory where the transaction logs are stored.
dataLogDir=/var/lib/zookeeper/logs
server.1=192.168.20.101:2888:3888
server.2=192.168.20.102:2888:3888
server.3=192.168.20.103:2888:3888
server.4=192.168.20.104:2888:3888
server.5=192.168.20.105:2888:3888
minSessionTimeout=4000
maxSessionTimeout=100000
在装有ZooKeeper的机器的终端执行 zookeeper-server status 可以看当前节点的ZooKeeper是什么角色(Leader or Follower)。
ZooKeeper默认只有Leader和Follower两种角色,没有Observer角色。
为了使用Observer模式,在任何想变成Observer的节点的配置文件中加入:peerType=observer
并在所有server的配置文件中,配置成observer模式的server的那行配置追加:observer,例如:
server.1:localhost:2888:3888:observer
ZooKeeper集群的所有机器通过一个Leader选举过程来选定一台被称为『Leader』的机器,Leader服务器为客户端提供读和写服务。
Follower和Observer都能提供读服务,不能提供写服务。两者唯一的区别在于,Observer机器不参与Leader选举过程,也不参与写操作的『过半写成功』策略,因此Observer可以在不影响写性能的情况下提升集群的读性能。
在ZooKeeper中,能改变ZooKeeper服务器状态的操作称为事务操作。一般包括数据节点创建与删除、数据内容更新和客户端会话创建与失效等操作。对应每一个事务请求,ZooKeeper都会为其分配一个全局唯一的事务ID,用ZXID表示,通常是一个64位的数字。每一个ZXID对应一次更新操作,从这些ZXID中可以间接地识别出ZooKeeper处理这些事务操作请求的全局顺序。
zookeeper的奔溃恢复模式和数据同步
一、什么情况下zab协议会进入崩溃恢复模式?
- 1、当服务器启动时
- 2、当leader 服务器出现网络中断,崩溃或者重启的情况
- 3、当集群中已经不存在过半的服务器与Leader服务器保持正常通信。
二、zab协议进入崩溃恢复模式会做什么?
- 1、当leader出现问题,zab协议进入崩溃恢复模式,并且选举出新的leader。当新的leader选举出来以后,如果集群中已经有过半机器完成了leader服务器的状态同(数据同步),退出崩溃恢复,进入消息广播模式。
- 2、当新的机器加入到集群中的时候,如果已经存在leader服务器,那么新加入的服务器就会自觉进入崩溃恢复模式,找到leader进行数据同步。
三、特殊情况下需要解决的两个问题:
- 1、已经被处理的事务请求(proposal)不能丢(commit的)
- 2、没被处理的事务请求(proposal)不能再次出现
什么时候会出现事务请求被丢失呢?
当 leader 收到合法数量 follower 的 ACKs 后,就向各个 follower 广播 COMMIT 命令,同时也会在本地执行 COMMIT 并向连接的客户端返回「成功」。但是如果在各个 follower 在收到 COMMIT 命令前 leader 就挂了,导致剩下的服务器并没有执行都这条消息。
如何解决 已经被处理的事务请求(proposal)不能丢(commit的)呢?
- 1、选举拥有 proposal 最大值(即 zxid 最大) 的节点作为新的 leader:由于所有提案被 COMMIT 之前必须有合法数量的 follower ACK,即必须有合法数量的服务器的事务日志上有该提案的 proposal,因此,zxid最大也就是数据最新的节点保存了所有被 COMMIT 消息的 proposal 状态。
- 2、新的 leader 将自己事务日志中 proposal 但未 COMMIT 的消息处理。
- 3、新的 leader 与 follower 建立先进先出的队列, 先将自身有而 follower 没有的 proposal 发送给 follower,再将这些 proposal 的 COMMIT 命令发送给 follower,以保证所有的 follower 都保存了所有的 proposal、所有的 follower 都处理了所有的消息。通过以上策略,能保证已经被处理的消息不会丢。
问题二出现的场景是:
当 leader 接收到消息请求生成 proposal 后就挂了,其他 follower 并没有收到此 proposal,因此经过恢复模式重新选了 leader 后,这条消息是被跳过的。 此时,之前挂了的 leader 重新启动并注册成了 follower,他保留了被跳过消息的 proposal 状态,与整个系统的状态是不一致的,需要将其删除。
解决方案是:
Zab 通过巧妙的设计 zxid 来实现这一目的。一个 zxid 是64位,高 32 是纪元(epoch)编号,每经过一次 leader 选举产生一个新的 leader,新 leader 会将 epoch 号 +1。==低 32 位是消息计数器,每接收到一条消息这个值 +1,新 leader 选举后这个值重置为 0。==这样设计的好处是旧的 leader 挂了后重启,它不会被选举为 leader,因为此时它的 zxid 肯定小于当前的新 leader。当旧的 leader 作为 follower 接入新的 leader 后,新的 leader 会让它将所有的拥有旧的 epoch 号的未被 COMMIT 的 proposal 清除。
深入ZAB协议,等面试以后深入学习
ZAB与Paxos算法的联系与区别
- 两者都存在一个类似于leader进程的角色,由其负责协调多个Follower进程的运行。
- Leader进程都会等待超过半数的Follower做出正确的反馈后,才会将一个提案进行提交。
- 在ZAB协议中,每个Proposal中都包含一个epoch值,用来代表当前的leader周期,在Paxos算法中,同样存在这样的一个标识,只是名字变成了Ballot。
- ZAB协议额外添加了一个同步阶段,在同步阶段之前,ZAB也存在一个和Paxos算法中的读阶段非常类似的过程,发现(discovery)阶段。
总的来说:ZAB协议和Paxos算法的本质区别在于,两者的设计目标不太一样。ZAB协议主要用于构建一个高可用的分布式数据主备系统,例如zookeeper,而paxos算法则是构建一个分布式的一致性的状态机系统。
Zookeeper的使用:
部署模式:单机模式、伪集群模式、集群模式。
集群规则为2N+1台,N>0,即3台。
集群需要一半以上的机器可用,所以,3台挂掉1台还能工作,2台依然能够执行,因为包括leader自己。
集群搭建步骤:
- 1、准备java环境
- 2、下载zookeeper安装包
- 3、配置文件zoo.cfg
需要将安装包下zoo_sample.cfg文件重命名。
配置server.1=IP1:2888:3888
配置server.2=IP2:2888:3888
配置server.3=IP3:2888:3888
格式为server.id = host :port:port,id称为Server ID,用来标识该机器在集群中的机器序号。每台zookeeper目录下创建myid文件来指定数字对应id值。id的范围1~255.- 4、 创建svn或者git管理该zoo.cfg文件
- 5、 启动服务器 zkServer.sh start
- 6、验证服务器 telnet 127.0.0.1 2181和stat命令
注:单机模式的话会显示Mode为standalone,如果集群的模式会显示leader或者follower
常用命令
常用命令:ls get set create delete等。
ls path,查看指定path路径下的文件
get path ,获取节点的数据内容和属性信息
set path data,更新指定路径节点的内容,内容的dataversion也会变化增加
delete path ,删除指定节点
create path number,创建路径上的指定节点
java客户端:zk自带的zkclient及Apache开源的Curator(Netflix公司开发的)。
面试完成以后待补充。
数据发布/订阅
数据发布/订阅(Publish/Subscribe)系统,即所谓的配置中心,顾明思义就是发布者将数据发布到zookeeper的一个或一系列的节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。
zookeeper采用推拉结合的方式来实现发布订阅系统:客户端向服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务端就会向相应的客户端发送Watcher事件通知,客户端接收到这个消息通知之后,需要主动到服务端获取最新的数据。
程序总是需要配置的,如果程序分散部署在多台机器上,要这个改变配置就变得困难。好吧,现在把这些配置全部放到zookeeper上去,保存在zookeeper的某个目录节点中,然后所有相关应用程序对这个目录节点进行监控,一旦配置信息发生变化,每个应用程序就会收到zookeeper的通知,然后从zookeeper中获取新的配置信息应用到系统中就好
2、负载均衡
每台服务端在启动时都会去zookeeper的servers节点下注册临时节点(注册临时节点是因为,当服务不可用时,这个临时节点会消失,客户端也就不会请求这个服务端),每台客户端在启动时都会去servers节点下取得所有可用的工作服务器列表,并通过一定的负载均衡算法计算得出应该将请求发到哪个服务器上
3、生成分布式唯一ID
在过去的单库单表型系统中,通常可以使用数据库字段自带的auto_increment属性来自动为每条记录生成一个唯一的ID。但是分库分表后,就无法在依靠数据库的auto_increment属性来唯一标识一条记录了。此时我们就可以用zookeeper在分布式环境下生成全局唯一ID。做法如下:每次要生成一个新Id时,创建一个持久顺序节点,创建操作返回的节点序号,即为新Id,然后把比自己节点小的删除即可。
4、Master选举
Master选举是一个在分布式系统中非常常见的应用场景。在分布式系统中,Master往往用来协调系统中的其他系统单元,具有对分布式系统状态变更的决定权。例如,在一些读写分离的应用场景用,客户端的写请求往往是由Master来处理的,而在另一些场景中, Master则常常负负责处理一下复杂的逻辑,并将处理结果同步给集群中其他系统单元。Master选举可以说是zookeeper最典型的应用场景了
利用zookeeper的强一致性,能够很好地保证在分布式高并发情况下节点的创建一定能保证全局唯一性,即zookeeper将会保证客户端无法重复创建一个已经存在的数据节点。也就是说,如果同时有多个客户端请求创建同一个节点,那么最终一定只有一个客户端能够创建成功。利用这个特性,就很容易在分布式环境中进行Master选举
客户端集群往zookeeper上创建一个/master临时节点。在这个过程中,只有一个客户端能够成功创建这个节点,那么这个客户端就成了master。同时其他没有在zookeeper上成功创建节点的客户端,都会在节点/master上注册一个变更的watcher,用于监控当前的master机器是否存活,一旦发现当前的master挂了,那么其余的客户端将会重新进行master选举
5、分布式锁
在同一个JVM中,为了保证对一个资源的有序访问,如往文件中写数据,可以用synchronized或者ReentrantLock来实现对资源的互斥访问,如果2个程序在不同的JVM中,并且都要往同一个文件中写数据,如何保证互斥访问呢?这时就需要用到分布式锁了
目前分布式锁的主流实现方式有两种
利用redis setnex(key value) key不存在返回0,key存在返回1
zookeeper实现排他锁,共享锁(读锁)
这里只简单介绍一下排他锁的实现方式
实现原理和master选举类似,所有客户端在/exclusive_lock节点下创建临时子节点/exclusive_lock/lock,zookeeper会保证在所有的客户端中,最终只有一个客户端能够创建成功,那么就认为该客户端获取了锁,其他没有获取到锁的客户端就需要到/exclusive_lock节点看上注册一个子节点变更的watcher监听,以便实时监听到lock节点的变更情况
释放锁的情况有如下两种
当前获取锁的客户端发生宕机,那么zookeeper上的这个临时节点就会被删除
正常执行完业务逻辑后,客户端会主动将自己创建的临时节点删除
整个排他锁的获取和释放流程可以用如下图表示
6、分布式队列
如下图,创建/queue作为一个队列,然后每创建一个顺序节点,视为一条消息(节点存储的数据即为消息内容),生产者每次创建一个新节点,做为消息发送,消费者监听queue的子节点变化(或定时轮询),每次取最小节点当做消费消息,处理完后,删除该节点。相当于实现了一个FIFO(先进先出)的队列。注:zookeeper强调的是CP(一致性),而非专为高并发、高性能场景设计的,如果在高并发,qps很高的情况下,分布式队列需酌情考虑。
https://yq.aliyun.com/articles/227260
在粗粒度分布式锁,分布式选主,主备高可用切换等不需要高TPS 支持的场景下有不可替代的作用,而这些需求往往多集中在大数据、离线任务等相关的业务领域,因为大数据领域,讲究分割数据集,并且大部分时间分任务多进程/线程并行处理这些数据集,但是总是有一些点上需要将这些任务和进程统一协调,这时候就是 ZooKeeper 发挥巨大作用的用武之地。
但是在交易场景交易链路上,在主业务数据存取,大规模服务发现、大规模健康监测等方面有天然的短板,应该竭力避免在这些场景下引入 ZooKeeper,在阿里巴巴的生产实践中,应用对ZooKeeper申请使用的时候要进行严格的场景、容量、SLA需求的评估。
所以可以使用 ZooKeeper,但是大数据请向左,而交易则向右,分布式协调向左,服务发现向右。
参考链接:
https://www.jianshu.com/p/84ad63127cd1
http://jm.taobao.org/2018/06/13/做服务发现?/