7.第七天

7.1 ID生成器

  • 通过在ZooKeepr里创建顺序节点,很容易创建一个全局唯一的名字,即生成全局唯一的ID。

  • 在每一台机子上需要id的时候执行一个创建顺序节点的操作,一定可以获取到一个唯一的id

  • redis 自增长+1也可以实现

  • 以前常使用的:
    例如数据库表格ID,一般用得比较多的有两种ID,一种是自动增长的ID,一种是UUID(9291d71a-0354-4d8e-acd8-64f7393c64ae),两种ID各自都有缺陷,自动增长的ID局限在单库单表中使用,不能在分布式中使用,UUID可以在分布式中使用但是由于ID没有规律难于理解,我们可以借用ZK来生成一个顺序增长的,可以在集群环境下使用的,命名易于理解的ID。

7.2 配置中心

  • 需求:
    在分布式应用系统中,会有很多的类似的配置文件,他们会散落在各个服务中,比如数据库的用户名和密码
    如果后期要修改的话,就需要修改非常多的配置文件,这显然不是好的做法,基于这种现状,就有了配置中心的概念

  • 方案:
    可以把所有的配置都放在一个配置中心,然后各个服务分别去监听配置中心,一旦发现里面的内容发生变化,立即获取变化的内容,然后更新本地配置即可

  • 实现:
    通过Zookeeper的watch机制,我们可以轻松的实现这一需求,
    所有的服务需要监听Zookeeper的配置节点,当配置信息发生变化之后,Zookeeper会将变化信息推送给服务,当然服务也可以主动去拉取节点数据

  • 说明:
    在这里Zookeeper采用了推拉模式相结合的做法:
    push可以保证能够第一时间拿到更新配置,基本可以做到准实时的更新,但push存在问题,即如果有网络抖动,某一次push没有推送成功,将丢失这次配置的更新
    pull可以保证一定可以拉取得到数据,pull一般采用定时拉取的方式,即使某一次出现网络问题,没有拉取得到数据,那在下一次定时器也将可以拉取得到数据

7.3 分布式协调

  • watch机制
    zookeeper的watch机制,一个轻量级的设计。因为它采用了一种推拉结合的模式。
    一旦服务端感知主题变了,那么只会发送一个事件类型和节点信息给关注的客户端,而不会包括具体的变更内容,所以事件本身是轻量级的,这就是所谓的"推"部分。
    然后,收到变更通知的客户端需要自己去拉变更的数据,这就是"拉"部分。
    Zookeeper的事件监听机制有以下特性:
    1.当监听器监听的事件被触发,服务端会发送通知给客户端,但通知信息中不包括事件的具体内容。
    2.Watcher是一次性的。一旦被触发将会失效。如果需要反复进行监听就需要反复进行注册。
  • Curator(是Netflix公司开源的一套zookeeper客户端框架,解决了很多Zookeeper客户端非常底层的细节开发工作,包括连接重连、反复注册Watcher和NodeExistsException异常等等)
    watch机制分为添加数据和监听节点。Curator在这方面做了优化,Curator引入了Cache的概念用来实现对ZooKeeper服务器端进行事件监听。
    Cache是Curator对事件监听的包装,其对事件的监听可以近似看做是一个本地缓存视图和远程ZooKeeper视图的对比过程。
    而且Curator会自动的再次监听,我们就不需要自己手动的重复监听了。
    • Curator中的cache共有三种:
      NodeCache: 用来监听节点的创建和数据变化
      PathChildrenCache: 用来监听指定节点的子节点增删和数据变化
      TreeCache: 像上面两种Cache的结合体,能够监听自身节点的变化、也能够监听子节点的变化

7.4 集群选主

  • 需求:
    在集群中,很多情况下是要区分主从节点的,一般情况下主节点负责数据写入,从节点负责数据读取,那么问题来了,怎么确定哪一个节点是主节点的

  • 实现:
    使用Zookeeper的临时节点可以轻松实现这一需求
    我们把上面描述的这个过程称为集群选主的过程,首先所有的节点都认为是从节点,都有机会称为主节点,然后开始选主,步骤比较简单:

    1. 所有参与选主的主机都去Zookeeper上创建同一个临时节点,那么最终一定只有一个客户端请求能够创建成功。
    2. 成功创建节点的客户端所在的机器就成为了主节点,其他没有成功创建该节点的客户端,成为从节点
    3. 所有的从节点都会在主节点上注册一个子节点变更的Watcher,用于监控当前主节点是否存活,一旦发现当前的主节点挂了,那么其他客户端将会重新进行选主。\

7.5 分布式锁

  • 需求:
    在分布式系统中,很容出现多台主机操作同一资源的情况, 比如两台主机同时往一个文件中追加写入文本,
    如果不去做任何的控制,很有可能出现一个写入操作被另一个写入操作覆盖掉的状况

  • 方案:
    此时我们可以来一把锁,哪个主机获取到了这把锁,就执行写入,另一台主机等待;直到写入操作执行完毕,另一台主机再去获得锁,然后写入
    这把锁就称为分布式锁, 也就是说:分布式锁是控制分布式系统之间同步访问共享资源的一种方式

  • 实现:
    使用Zookeeper的临时有序节点可以轻松实现这一需求

      1. 所有需要执行操作的主机都去Zookeeper上创建一个临时有序节点
      2. 然后获取到Zookeeper上创建出来的这些节点进行一个从小到大的排序
      3. 判断自己创建的节点是不是最小的,如果是,自己就获取到了锁;   如果不是,则对最小的节点注册一个监听
      4. 如果自己获取到了锁,就去执行相应的操作,当执行完毕之后,连接断开,节点消失,锁就被释放了
      5. 如果自己没有获取到锁,就等待,一直监听节点消失,锁释放后,再重新执行抢夺锁的操作(监听优化:第二个监听第一个,第三个监听第二个...)

7.6 集群介绍
Zookeeper在一个系统中一般会充当一个很重要的角色,所以一定要保证它的高可用,这就需要部署Zookeeper的集群。

Zookeeper 有三种运行模式:单机模式、集群模式和伪集群模式。

  • 单机模式:使用一台主机部署一个Zookeeper来对外提供服务,有单点故障问题,仅适合于开发、测试环境
  • 集群模式:使用多台服务器,每台上部署一个Zookeeper一起对外提供服务,适合于生产环境
  • 伪集群模式:在服务器不够多的情况下,也可以考虑在一台服务器上部署多个Zookeeper来对外提供服务

7.7 一致性事务处理

7.7.1 集群角色

  • ZooKeeper集群中的三个角***r> Leader(领导者):负责投票的发起和决议,更新系统状态,是事务请求的唯一处理者,一个ZooKeeper同一时刻只会有一个Leader
    Follower(跟随者):处理客户端请求,参与选主投票
    Observer(观察者):处理客户端请求,不参与选主投票
  • Leader可以提供读写服务,Follower或Observer只提供读服务,但是Observer机器不参与Leader选举,也不参与写操作的『过半写成功』策略

7.7.2 Zookeeper的特性

  1. 顺序性: 从同一个客户端发起的事务请求, 最终会严格按照顺序被应用到zookeeper中
  2. 原子性: 所有的事务请求的处理结果在整个集群中的所有机器上的应用情况是一致的; 也就是说,要么整个集群中的所有机器都成功应用了某一事务、要么全都不应用
  3. 可靠性: 一旦服务器成功应用了某一个事务数据,并且对客户端做了响应,那么这个数据在整个集群中一定是同步并且保留下来的
  4. 实时性: 一旦一个事务被成功应用,客户端就能够立即从服务器端读取到事务变更后的最新数据状态(zookeeper仅仅保证在一定时间内,近实时)

7.7.3 ZAB协议

  • Zookeeper采用ZAB(Zookeeper Atomic Broadcast)协议来保证分布式数据一致性。
  • ZAB并不是一种通用的分布式一致性算法,而是一种专为Zookeeper设计的崩溃可恢复的原子消息广播算法。
  • ZAB协议包括两种基本模式:崩溃恢复模式和消息广播模式:
    消息广播模式主要用来进行事务请求的处理
    崩溃恢复模式主要用来在集群启动过程,或者Leader服务器崩溃退出后进行新的Leader服务器的选举以及数据同步

7.7.4 事务请求的处理流程

  1. 所有的事务请求都交由集群的Leader服务器来处理,Leader服务器会将一个事务请求转换成一个Proposal(提议),并为其生成一个全局递增的唯一ID,
    这个ID就是事务ID,即ZXID,Leader服务器对Proposal是按其ZXID的先后顺序来进行排序和处理的。
  2. Leader服务器会将Proposal放入每个Follower对应的队列中(Leader会为每个Follower分配一个单独的队列),并以FIFO的方式发送给Follower服务器。
  3. Follower服务器接收到事务Proposal后,首先以事务日志的方式写入本地磁盘,并且在成功后返回Leader服务器一个ACK响应。
  4. Leader服务器只要收到过半Follower的ACK响应,就会广播一个Commit消息给Follower以通知其进行Proposal的提交,同时Leader自身也会完成Proposal的提交。
  5. Follower收到commit请求时,从历史队列中将事务请求commit

7.8 集群选举

7.8.1 服务器状态
Zookeeper服务器有四个状态

  • looking: 寻找leader状态。当服务器处于该状态时,它会认为当前集群中没有leader,因此需要进入leader选举状态。
  • leading: 领导者状态。表明当前服务器角色是leader。
  • following:跟随者状态。表明当前服务器角色是follower。
  • observing:观察者状态。表明当前服务器角色是observer。

7.8.2 服务器启动时期的leader选举

  • 在集群初始化阶段,当有一台服务器server1启动时,其单独无法进行和完成leader选举,
  • 当第二台服务器server2启动时,此时两台机器可以相互通信,每台机器都试图找到leader,于是进入leader选举过程。
  • 选举过程如下:
    1. 每个server发出一个投票。
      由于是初始情况,server1和server2都会将自己作为leader服务器来进行投票,每次投票会包含所推举的服务器的myid和zxid,
      使用(myid, zxid)来表示,此时server1的投票为(1, 0),server2的投票为(2, 0),然后各自将这个投票发给集群中其他机器。
    2. 集群中的每台服务器接收来自集群中各个服务器的投票。
    3. 处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行pk,pk规则如下:
      优先检查zxid。zxid比较大的服务器优先作为leader。
      如果zxid相同,那么就比较myid。myid较大的服务器作为leader服务器。
      对于Server1而言,它的投票是(1, 0),接收Server2的投票为(2, 0),首先会比较两者的zxid,均为0,再比较myid,此时server2的myid最大,
      于是更新自己的投票为(2, 0),然后重新投票,对于server2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可。
    4. 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,
      对于server1、server2而言,都统计出集群中已经有两台机器接受了(2, 0)的投票信息,此时便认为已经选出了leader
    5. 改变服务器状态。一旦确定了leader,每个服务器就会更新自己的状态,如果是follower,那么就变更为following,如果是leader,就变更为leading。

7.8.3 服务器运行时期的Leader选举

  • 在zookeeper运行期间,leader与非leader服务器各司其职,即便当有非leader服务器宕机或新加入,此时也不会影响leader,
  • 但是一旦leader服务器挂了,那么整个集群将暂停对外服务,进入新一轮leader选举,其过程和启动时期的Leader选举过程基本一致。
  • 假设正在运行的有server1、server2、server3三台服务器,当前leader是server2,若某一时刻leader挂了,此时便开始Leader选举。
  • 选举过程如下:
    1. 变更状态。leader挂后,余下的服务器都会将自己的服务器状态变更为looking,然后开始进入leader选举过程。
    2. 每个server会发出一个投票。
      在运行期间,每个服务器上的zxid可能不同,此时假定server1的zxid为122,server3的zxid为122,
      在第一轮投票中,server1和server3都会投自己,产生投票(1, 122),(3, 122),然后各自将投票发送给集群中所有机器。
    3. 接收来自各个服务器的投票。与启动时过程相同
    4. 处理投票。与启动时过程相同,此时,server3将会成为leader。
    5. 统计投票。与启动时过程相同。
    6. 改变服务器的状态。与启动时过程相同。

7.8.4 observer角色及其配置

  • observer角色特点:
    1. 不参与集群的leader选举
    2. 不参与集群中写数据时的ack反馈
  • 为了使用observer角色,在任何想变成observer角色的配置文件中加入如下配置:
    peerType=observer
  • 并在所有server的配置文件中,配置成observer模式的server的那行配置追加:observer
    server.3=192.168.60.130:2289:3389:observer