[9]ZooKeeper(HA的前提)

版权声明:Collected by Bro_Rabbit only for study https://blog.csdn.net/weixin_38240095/article/details/83059753

一、概述

  1. Hadoop生态的分布式协调服务组件,基于对Paxos算法的实现,包含一个简单的原语集,分布式应用程序可以基于它实现一致性的同步服务,配置维护和命名服务等
    ZooKeeperOverview
  2. 几点细节说明
    ZooKeeperOverview
    (1) Leader 是一个主节点,所有数据的写操作都是经由Leader实现的。客户端先更新Leader上的数据,Leader通知其他的Follower更新这份数据,且有半数以上的Follower更新成功就认为本次更新成功
    (2) Leader和Follower不是启动之前由配置文件设置的角色,而是通过选举机制进行投票,一旦有一个server当选Leader,那么其他server自动成为Follower
    (3) Obserber也可以接收客户端链接,将写请求转发给Leader,但Observer不参加投票过程,只同步Leader的状态。 Observer的目的是为了扩展系统,提高读取速度。

二、集群搭建

  1. ZooKeeper集群有基数个节点,最少为3个。
  2. 关键修改conf目录下的配置文件
    mv zoo_sample.cfg zoo.cfg
    
    # The number of milliseconds of each tick	(服务器之间或客户端与服务器之间维持心跳的时间间隔,每过一个 tickTime 时间就会发送一个心跳。以毫秒为单位。)
    tickTime=2000
    
    # The number of ticks that the initial synchronization phase can take(集群中的Follower服务器与Leader服务器之间初始连接时能容忍的最大心跳数)
    initLimit=10
    
    # The number of ticks that can pass between sending a request and getting an acknowledgement (集群中的Follower服务器与Leader服务器之间请求和应答之间能容忍的最大心跳数,超过则超时)
    syncLimit=5
    
    # the directory where the snapshot is stored.do not use /tmp for storage, /tmp here is just example sakes.(本地保存数据的目录,默认情况下,ZooKeeper将写数据的日志文件也保存在这个目录里)
    dataDir=/home/hadoopApp/zookeeper-…/data
    
    # the port at which the clients will connect(客户端连接 ZooKeeper 服务器的端口,ZooKeeper 会监听这个端口,接受客户端的访问请求)
    clientPort=2181
    
    # the maximum number of client connections.
    # increase this if you need to handle more clients
    #maxClientCnxns=60
    
    # Be sure to read the maintenance section of the administrator guide before turning on autopurge. 
    # http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
    
    # The number of snapshots to retain in dataDir
    # autopurge.snapRetainCount=3
    # Purge task interval in hours
    # Set to "0" to disable auto purge feature
    # autopurge.purgeInterval=1
    
    #Servers config (ZooKeeper集群服务器.编号:通信端口:选举端口)
    server.<n>=<HostName>:2888:3888
    
  3. 工作目录 下创建一个名为 myid 的文件,其内容为本服务器在配置文件中的标识(server.<n>的<n>)
  4. 进入ZooKeeper-…/bin,执行./zkServer.sh start, 启动后回生成一个QuorumPeerMain进程
  5. 少于集群数量的一半,ZooKeeper不会工作(因为无法得到半数票,选不出Leader)。 可以使用./zkServer status查看状态角色
  6. 使用 ./zkCli.sh 登录只本地ZK服务器测试(通过help可查看zkshell命令)

三、节点

  1. ZooKeeper管理客户端所存放的数据采用的是类似于文件树的结构:从根节点/开始
  2. 每个Znode可以保存信息数据,最好小于1M,可以创建子节点
  3. Znode有两种类型:短暂( ephemeral )和持久( persistent )
    * 短暂Znode在客户端会话结束时,ZooKeeper回将该短暂Znode删除,短暂Znode不可以有子节点
    * 持久Znode不依赖于客户端会话,只有当客户端明确要删除该持久Znode时才会被删除

四、典型应用场景[转载]

情景 概述 详细
数据发布与订阅( 配置中心 ) 发布与订阅模型,即所谓的配置中心,顾名思义就是发布者将数据发布到ZK节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新。例如全局的配置信息,服务式服务框架的服务地址列表等。

默认前提:数据量很小,但数据更新可能回比较快的场景。
  • 应用中使用的一些配置信息放到ZK集群上进行集中管理。这类场景通常如下:应用在启动时主动来获取一次配置,同时在节点上注册一个Watcher,这样一来,以后每次配置有更新的时候,都会实时通知到订阅的客户端,从而达到获取最新配置信息的目的
  • 分布式搜索服务中,索引的元信息和服务器集群的节点状态存放在ZK的一些指定节点,供各个客户端订阅使用
  • 分布式日志收集系统中,核心工作是收集分布在不同机器的日志。收集器通常是按照应用来分配收集任务单元,因此需要在ZK上创建一个应用名为Path的节点P,并将这个应用的所有机器ip,以子节点的形式注册到节点P上,这样一来就能够实现机器变动的时能够实时通知到收集器调整任务分配。
  • 系统中有些信息需要动态获取,并且还会存在人工手动去修改这个信息的发问。通常是暴露出接口,例如JMX接口,来获取一些运行时的信息。引入ZK之后,就不需要自己实现一套方案,只要将这些信息存放到指定的ZK节点上即可
软负载均衡 在分布式环境中,为了保证高可用性,通常同一个应用或同一个服务的提供方都会部署多分,达到对等服务。而消费者就需要在这些对等的服务器中选择一个来执行相关的业务逻辑,其中比较单行的是消息中间件的生产者与消费者的负载均衡 linkedin开源的KafkaMQ和阿里开源的MetaQ都是通过ZooKeeper来做到生产者和消费者的负载均衡。这里以metaq为例:
  • 生产者负载均衡:MetaQ发送消息的时,生产者在发送消息的时候必须选择一台broker上的一个分区来发送消息,因此metaq在运行过程中,会把所有broker和u对应的分区信息全部注册到ZK指定节点上,默认的策略是一个依次轮询的过程,生产者在通过ZK获取分区列表之后,发送的时候按照从头到尾循环往复地方式选择一个分区来发送消息
  • 消费者负载均衡:在消费过程中,一个消费者会消费一个或多个分区中的消息,但是一个分区只会由一个消费者来消费。MeteQ的消费策略是
    • 每个分区针对同一group只挂载一个消费者
    • 如果同一个group的消费者数目大于分区数目,则多出来的消费者将不参与消费
    • 如果同一个group的消费者数目小于分区数目,则有部分消费者需要额外承担消费任务
    在某个消费者故障或重启等情况下,其他消费者会感知这一变化(通过 ZK watch 消费者列表),然后重新进行负载均衡,保证所有的分区都有消费者进行消费
命名服务 命名服务也是分布式系统中比较常见的一类场景。在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。被命名的实体通常可以是集群中的机器,提供的服务地址,远程对象等等——这些都可以统称为名字Name。其中较为常见的就是一些分布式服务框架中的服务地址列表。通过调用ZK提供的创建节点的API,能够很容易创建一个全局唯一的Path,这个Path即可作为一个名称 阿里开源的分布式服务框架Dubbo中使用ZK来作为其命名服务,维护全局的服务地址列表。在Dubbo实现中:
  • 服务提供者在启动时,想ZK上的指定节点/dubbo/${serviceName}/providers目录下写入自己的URL地址,这个操作就完成了服务的发布
  • 服务消费者启动时,订阅/dubbo/${serviceName}/providers目录下的提供者URL地址,并向/dubbo/${serviceName}/consumers目录下下入自己的URL地址
  • 注意:所有向ZK上注册的地址都是临时节点,这样能够保证服务提供者和消费者能够自动感应资源的变化
    另外,Dubbo还有针对服务力度的监控,方法是订阅/dubbo/${serviceName}目录下所有提供者和消费者的信息
分布式通过/协调 ZK中特有watcher注册与异步通知机制,能够很好地实现分布式环境下不同系统之间地通知与协调,实现对数据变更地实时处理。使用方法通常是不同系统都对ZK上同一个Znode进行注册,监听Znode的变化(包括Znode本身内容及子节点的),其中一个系统Update了Znode,那么另一个系统能够收到通知,并作出相应处理
  • 另一种心跳检测机制:检测系统和被检测系统之间并不直接关联起来,而是通过ZK上某个节点关联,大大减少系统耦合
  • 另一种系统调度模式:某系统有控制台和推送系统两部分组成,控制台负责控制推送系统进行相应的推送的工作。管理人员在控制台额一些 操作,实际上是修改了ZK上某些节点的状态,而ZK就把这些变化通知给它们注册Watcher的客户端,即推送系统
  • 另一种工作汇报模式:类似于任务发布系统。子任务启动后,到ZK来注册一个临时节点,并且定时将自己的进度进行汇报(将进度写回这个临时节点),这样任务管理者就能够实时知道任务进度
集群机器管理 通常用于那种对集群中机器状态,机器在线率有较高,需要快速对集群中机器变化做出响应的场景。 过去的做法通常是使用监控系统,通过某种手段( 比如ping )定时检测每个机器,或者每个机器自己定时向监控系统汇报心跳。这种做法可行,但存在两个比较明显的问题:
  • 集群中机器有变动的时候,牵连修改的东西比较多
  • 有一定的延时
利用ZK的两个特性,便可以实施另一种集群机器存活性监控系统:
  • 客户端在节点X上注册一个Watcher,若X的子节点变化,会通知该客户端
  • 创建EPHEMERAL类型的节点,一旦客户端和服务器的会话结束或过期,那么该节点就会消失。例如,监控系统在/clusterServers节点上注册一个Watcher,之后每次动态添加机器,就往/clusterServers下创建一个EPHEMERAL类型的节点:/clusterServers/{hostname}。这样,监控系统就能够实时获取机器的增减情况,置于后续处理就是监控系统的业务
Master选举 Master选举是ZK中最为经典的应用场景。在分布式环境中,相同的业务应用分布在不同的机器上,有些业务逻辑(如一些耗时计算,网络I/O),往往只需要让整个集群中的某一台机器进行执行,其余机器可以共享这个结果,这样可以大大减少重复劳动,提高性能 利用ZK的强一致性,能够保证在分布式高并发情况下节点创建的全局唯一性,即:同时有多个客户端请求创建/clusterMaster节点,最终一定只有一个客户端能够创建成功。利用这个特性,就能很轻易的在分布式环境中进行集群选取。

另外,将该场景进行演化即为动态Master选举。需要用到EPHEMERAL_SEQUENTIAL类型节点的特性。与之前不同,该场景允许所有请求都能够创建成功,但必须按找顺序进行创建,故所有的请求在ZK上创建结果的一种可能情况如下:/currentMaster/{sessionId}-1, /currentMaster/{sessionId}-2, /currentMaster/{sessionId}-3 … 每次选区序列号最小的那个机器作为Master,若该机器挂了,由他创建的节点会马上随之消失,那么之后最小的那个机器就是Master
  • 在搜索系统中,若集群中每个机器都生成一份全量索引,不仅耗时,而且不能保证彼此之间索引数据一致。因此让集群中的Master来进行全量索引的生成,然后同步到集群中其他机器。另外,Master选举的容灾措施是,可以随时进行手动指定Master,即应用在ZK无法获取Master信息时,可以通过如http方式,向一个地方获取Master
分布式锁 分布式锁,主要得益于ZooKeeper保证了数据的强一致性。
锁服务可以分为两类,一个是保持独占,另一个是控制时序
  • 保持独占,即所有试图来获取这个锁的客户端,最终只有一个可以成功获得这把锁。通常的做法是把ZK上的一个Znode看作是一把锁,通过CREATE Znode的方式来实现,所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也就拥有这把锁
  • 控制时序,即所有试图来获取这个锁的客户端,最终都会按照全局时序被安排执行。做法和上面类似,只是这里/distribute_lock已经预先存在,客户端在它下面创建临时有序节点(可以通过节点的属性控制:CreateMode.EPHEMERAL_SEQUENTIAL来指定)。父节点/distribute_lock维持一份sequence,保证子节点创建的时序性,从而也形成了每个客户端的全局时序
分布式队列 简单地将有两种,一种是常规的先进先出,另一种是要等到队列成员聚齐之后才统一按序执行类BATCH
  • 第一种先进先出队列,和分布式锁的时序控制场景原理基本一致,这里不再赘述。
  • 第二种队列其实在FIFO队列的基础上作了一个增强。通常可以在/queue这个Znode下预先创建一个/queue/num节点,并且赋值为n( 或者直接给/queue赋值n )表示队列大小。之后每次有队列成员加入,就判断下是否已经达到队列大小,决定是否可以开始执行。这种用法的典型场景是,分布式环境中一个大任务TaskA需要在很多子任务完成或就绪情况下才能进行。此时,范式其中一个子任务完成或就绪,就去/taskAList下创建自己的临时时序节点( CreateMode.EPHEMERAL_SEQUENTIAL ),当/taskAList发现自己下面的子节点满足指定个数,便可以进行下一步按序处理

猜你喜欢

转载自blog.csdn.net/weixin_38240095/article/details/83059753
今日推荐