Zookeeper_典型应用场景

来源:互联网 发布:python 绝对路径 编辑:程序博客网 时间:2024/05/17 00:12
  1. 数据发布/订阅

    即所谓的配置中心,发布者将数据发布到zookeeper的一个或多个节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。

    zookeeper采用的是推拉结合的方式:客户端向服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务端就会向相应的客户端发送watcher事件通知,客户端接收到这个消息通知之后,需要主动到服务端获取最新的数据。
    ① 配置存储
    在zookeeper上创建一个数据节点(配置节点),将配置信息写入该数据节点
    ② 配置获取
    集群中每台机器在启动初始化阶段,首先从zookeeper数据节点读取数据库信息。同时,客户端需要在该配置节点上注册一个数据变更的Watcher监听,一旦发生节点数据变更,所有订阅的客户端都能够获取到数据变更通知。

  2. 负载均衡
    负载均衡可以分为硬件和软件负载均衡两类。一种基于zookeeper实现的动态DNS方案(Dynamic DNS,DDNS):
    ① 域名配置
    在zookeeper上创建一个数据节点用以进行域名配置,将域名作为节点名,IP地址和端口作为data写入节点
    ② 域名解析
    应用从数据节点获取信息并自行解析,每个应用在域名节点上注册一个数据变更watcher监听,以便及时收到域名变更的通知。
    自动化的DNS服务:

    这里写图片描述
    ·register 负责域名的动态注册
    ·dispatcher负责域名解析
    ·scanner 负责检测以及维护服务状态(探测服务的可用性、屏蔽异常服务节点等)
    ·SDK 提供各种语言的系统接入协议,提供服务注册以及查询接口
    ·monitor 负责收集服务信息以及对DDNS自身状态的监控
    ·controller 是一个后台管理的console,负责授权管理、流量控制、静态配置服务和手动屏蔽服务等功能。系统的运维人员也可以在上面管理register、dispatcher和scanner等
    ·域名注册
    每个服务提供者在启动过程中,都会把自己的域名信息注册到register集群中去。服务提供者通过SDK提供的API接口,将域名、IP地址和端口发送给register集群,register根据域名将信息写入相对应的zookeeper域名节点中。
    ·域名解析
    服务消费者在使用域名的时候,会向dispatcher发出域名解析请求。dispatcher收到请求后,会从zookeeper上指定域名节点读取相应的IP:PORT列表,通过一定的策略选取其中一个返回给前端应用。
    ·域名探测
    域名探测是指DDNS系统需要对域名下所有注册的IP地址和端口的可用性进行检测,俗称“健康度检测”。健康度检测一般有两种方式,第一种是服务端主动发起健康度心跳检测,这种方式一般需要在服务端和客户端之间建立起一个TCP长链接;第二种则是客户端主动向服务端发起健康度心跳检测。DDNS采用第二种模式,即每个服务提供者都会定时向Scanner汇报自己的状态。
    scanner会记录每个服务提供者最近一次的状态汇报时间,一旦超过5秒没有收到状态汇报,那么就认为该IP地址和端口已经不可用。

  3. 命名服务
    zookeeper提供的命名服务功能与JNDI技术有相似的地方,都能够帮助应用系统通过一个资源引用的方式来实现对资源的定位与使用。必须寻求一种能够在分布式环境下生成全局唯一ID的方法,UUID通用唯一识别码,GUID全局唯一标识符。一个标准的UUID是一个包含32位字符和4个短线的字符串。

    UUID的缺陷:长度过长、含义不明

    利用zookeeper生成全局唯一ID:

    这里写图片描述
    ①所有客户端都会根据自己的任务类型,在指定类型的任务下面通过调用create()接口创建一个顺序节点,例如创建“job-”节点
    ②节点创建完毕后,create()接口会返回一个完整的节点名,例如“job-000000000003”
    ③客户端拿到这个返回值后,拼接上type类型,例如“type2-job-000000000003”,这就可以作为一个全局唯一的ID了。

  4. 分布式协调/通知
    zookeeper中特有的watcher注册与异步通知机制,能够很好地实现分布式环境下不同机器,甚至是不同系统之间的协调与通知,从而实现对数据变更的实时处理。不同的客户端都对zookeeper上同一个数据节点进行watcher注册,监听数据节点的变化(包括数据节点本身及其子节点),如果数据节点发生变化,那么所有订阅的客户端都能够接收到相应的watcher通知,并做出相应的处理。

    MySQL数据复制总线:Mysql_Replicator

    这里写图片描述

    在该系统中,zookeeper主要负责进行一系列的分布式协调工作,数据复制组件分为三个核心子模块:Core、Server和Monitor,每个模块分别为一个单独的进程,通过zookeeper进行数据交换:
    ① Core 实现了数据复制的核心逻辑,其将数据复制封装成管道,并抽象出生产者和消费者两个概念,其中生产者通常是MySQL数据库的Binlog日志
    ② Server负责启动和停止复制任务
    ③ Monitor负责监控任务的运行状态,如果数据复制期间发生异常或出现故障会进行警告

    这里写图片描述

    每个模块作为独立的进程运行在服务端,运行时的数据和配置信息均保存在zookeeper上,web控制台通过zookeeper上的数据获取到后台进程的数据,同时发布控制信息。
    ① 任务注册
    Core进程在启动的时候,首先会在zookeeper相应的数据节点注册任务,如果该任务名已存在,说明其他task机器注册了该任务,因此自己不需要再创建该节点了。
    ② 任务热备份
    为了应对复制任务故障,会将同一个复制任务部署在不同的主机上,称这样的机器为“任务机器”,主、备任务机通过zookeeper互相检测运行健康状况。
    为了实现上述热备方案,每台任务机器都需要在任务节点下将自己的主机名注册上去,这时创建的是一个临时顺序节点。通过判断自己是否是所有子节点中序号最小的,决定是否将自己的状态设置为RUNNING,而其余机器将自己设置为STANDBY——这样的策略称为“小序号优先”策略。

    这里写图片描述
    ③ 热备切换
    所有机器都在instances节点上注册一个“子节点列表变更”的watcher监听,用来订阅所有任务执行机器的变化情况——一旦RUNNING机器宕机与zookeeper断开连接后,对应的节点就会消失,于是其他机器也就接收到了这个变更通知,从而开始新一轮的RUNNING选举。
    ④ 记录执行状态
    既然使用了热备份,那么RUNNING任务机器就需要将运行时的上下文状态保留给STANDBY任务机器。这个场景中,最主要的上下文状态就是数据复制过程中的一些进度信息,例如Binlog日志的消费位点,因此需要将这些信息保存在zookeeper上的lastCommit节点。
    ⑤ 控制台协调
    Server会将每个复制任务对应生产者的元数据,即库名、表名、用户名与密码等数据库信息以及消费者的相关信息以配置的形式写入任务节点,以便该任务的所有任务机器都能够共享该复制任务的配置。
    ⑥ 冷备切换
    与热备不同的是,它对所有任务进行了分组。和热备份中的“小序号优先”策略一样,顺序小的Core进程将自己标记为RUNNING,其他Core进程自动将自己创建的节点删除,然后遍历下一个task节点。这样保证每个时刻每个task只有一个core进程。
    ⑦ 冷热备份对比:
    热备份中,针对一个任务使用了两台机器进行热备份,借助zookeeper的watcher通知机制和临时顺序节点的特性,能够非常实时的进行相互协调,缺陷是机器资源消耗大;冷备份中,采用扫描机制,虽然降低了任务协调的实时性,但是节省了机器资源。

  5. 一种通用的分布式系统机器间通信方式
    在绝大数分布系统中,系统机器间的通信无外乎心跳检测、工作进度汇报、系统调度三种类型。
    ① 心跳检测
    通常方法是通过主机之间是否可以相互ping通来判断;复杂一点的会通过在机器之间建立长连接,通过TCP连接固有的心跳检测机制来实现上层机器的心跳检测。
    基于zookeeper的心跳检测,可以让不同的机器都在zookeeper的一个指定节点下创建临时子节点,不同的机器之间根据这个临时节点来判断对应的客户端机器是否存活。这种方式下,检测系统和被检测系统之间并不需要直接相关联,而是通过zookeeper上的某个节点进行关联,大大减少了系统耦合。
    ② 工作进度汇报
    在zookeeper上选择一个节点,每个任务客户端都在这个节点下创建临时子节点,这样便可以实现:心跳检测,以及各个任务机器会实时地将自己的任务执行进度写到这个临时节点上去,以便中心系统能够实时地获取到任务的执行进度。
    ③ 系统调度
    后台管理人员修改zookeeper上某些节点的数据,而zookeeper进一步把这些数据变更以事件通知的形式发送给对应的订阅客户端

  6. Master选举
    广告投放系统后台场景
    整个系统大体上可以分成客户端集群、分布式缓存系统、海量数据处理总线和zookeeper四部分。

    这里写图片描述
    客户端集群每天定时往zookeeper上创建一个临时节点,如/master_election/2017-08-16/binding,只有一个客户端能够成功创建这个节点,那么这个客户端所在的机器就成为master了。同时,其他没有在zookeeper上成功创建节点的客户端都会在/master_election/2017-08-16上注册一个子节点变更的watcher,用于监控当前的master机器是否存活,一旦发现当前的master挂了,那么其余的客户端将会重新进行选举。
    如果仅仅想实现master选举的话,只需要有一个能够保证数据唯一性的组件即可,例如关系型数据库的主键模型。但是,如果希望能够快速地进行集群master动态选举,那么基于zookeeper来实现是一个不错的选择

  7. 分布式锁
    ① 排他锁
    又称为写锁和独占锁。在zookeeper上注册一个临时数据节点以表示一个锁。在获取排他锁

    这里写图片描述
    时,所有客户端都试图调用create()接口创建lock节点,zookeeper将保证只有一个客户端能够创建成功,那么就可以认为该客户端获取了锁。其他没有获得锁的客户端就需要在exclusive_lock上注册一个子节点变更的watcher监听,以便实时监听到lock节点的变更情况
    当前获取锁的客户端机器发生宕机,那么zookeeper上的这个临时节点就会被移除;正常执行完业务逻辑后,客户端就会主档将自己创建的临时节点删除。
    ② 共享锁,又称读锁:
    1). 在需要获取共享锁时,所有客户端调用create()方法都到特定的数据节点如/shared_lock下创建一个临时顺序节点;
    2). 所有客户端调用getChildren()接口来获取所有已经创建的子节点列表,注意,这里不注册任何watcher
    3). 如果无法获取共享锁(即自己的序号不是最小),那么就调用exist()来对比自己小的那个节点注册watcher。对于读请求,向比自己序号小的最后一个写请求注册watcher监听,对于写请求,向比自己序号小的最后一个节点注册watcher监听。
    4). 等待watcher通知,继续进入步骤②

  8. 分布式队列
    分布式队列分为两大类:常规的先入先出队列,要等到队列元素集聚之后才同意安排执行的Barrier模型。
    ① FIFO先入先出模型
    所有客户端都到特定数据节点如/queue_fifo节点下创建一个临时顺序节点,接下来:
    1). 通过调用getChildren()接口来获取/queue_fifo节点下所有子节点,即获取队列中所有元素
    2). 确定自己的节点序号在所有子节点中的顺序
    3). 如果自己不是序号最小的子节点,那么就需要进入等待,同时向比自己序号小的最后一个节点注册watcher监听
    4). 接收到watcher通知后,重复步骤 1)
    ② Barrier分布式屏障
    Barrier原意指障碍物,而在分布式系统中,特指系统之间的一个协调条件,规定了一个队列的元素必须都集聚后才能统一进行安排,否则一直等待。
    开始时创建/queue_barrier节点,并将其data赋值为一个数字n来代表barrier值,只有当/queue_barrier节点下的子节点个数达到10后,才会打开barrier。
    创建完节点后,按以下步骤执行:
    1). 通过调用getData()接口获取/queue_barrier节点的数据内容:n
    2). 通过调用getChildren()接口获取/queue_barrier节点下的所有子节点,即获取队列中的所有元素,同时注册对子节点列表变更的watcher监听
    3). 统计子节点的个数
    4). 如果子节点的个数还不足10个,那么就需要进入等待
    5). 接收到watcher通知后,重复步骤 2)

  9. zookeeper在Hadoop中的应用
    ResourceManager HA:
    ①创建锁节点
    在zookeeper上有一个类似于/yarn-leader-election/yrc/ActiveStandbyElectorLock的锁节点,所有的ResourceManager在启动的时候,都会去竞争写一个Lock子节点。写成功的那个ResourceManager就切换为Active状态,没有成功的那个ResourceManager则切换为Standby状态
    ②注册watcher监听
    所有Standby状态的ResourceManager都会向/yarn-leader-election/yrc/ActiveStandbyElectorLock节点注册一个节点变更的watcher监听,利用临时节点的特性,能够快速感知Active状态的ResourceManager的运行状态
    ③主备切换
    当Active状态的ResourceManager出现诸如重启或挂掉的异常情况时,其在zookeeper上创建的lock节点也会随之被删除。此时,其余各Standby状态的ResourceManager都会接收到来自zookeeper服务端的watcher事件通知,然后重复进行步骤①的操作
    Fencing(隔离):
    “假死”:机器由于网络闪断或是其自身由于负载过高(常见为GC占用时间过长或CPU负载过高等)而导致无法正常对外响应。
    “脑裂”:当处于active状态的ResourceManager出现假死,根据主备切换逻辑,这时会产生一个新的active状态的ResourceManager,假死的ResourceManager恢复正常后会认为自己仍处于active状态,这时集群将出现两个active状态的ResourceManager。即为脑裂。
    “隔离”:通过zookeeper数据节点的ACL权限控制机制来实现不同RM之间的隔离。RM1假死后,zookeeper将其创建的锁节点移除,此时RM2创建相应锁节点,并切换为active状态。RM1恢复之后,会试图去更新zookeeper的相关数据,但会发现其没有权限更新zookeeper的相关数据节点。这就避免了脑裂。
    ResourceManager状态存储:
    在zookeeper上,ResourceManager的状态信息都被存储在/rmstore这个根节点下

  10. zookeeper与HBase
    ① 系统容错:
    当hbase启动时,每个regionserver服务器都会到zookeeper的/hbase/rs节点下创建一个临时信息节点(rs状态节点),同时hmaster会对这个节点注册监听。当某个region server挂掉,zookeeper通知hmaster。hmaster便将该regionserver所处理的数据分片重新路由到其他节点上,并记录到meta信息中供客户端查询。
    ② RootRegion管理:
    hbase数据存储的位置信息记录在元数据分片,也就是RootRegion上。RootRegion自身的位置信息记录在zookeeper上的/hbase/meta-region-server节点。
    ③ Region状态管理:
    分布式splitlog任务管理:
    当某台regionserver服务器挂掉时,由于总有一部分新写入的数据还没有持久化到HFile中,因此在迁移该regionserver的服务时,一个重要的工作就是从HLog中恢复这部分还在内存中的数据,而这部分工作最关键的一步就是splitlog,即HMaster需要遍历regionserver服务器的HLog,并按region切分成小块移动到新的地址下,并进行数据的replay。可行方案是将HLog任务分配给多台regionserver服务器共同处理,而这有需要一个持久化组件来辅助HMaster完成任务的分配。当前的做法是,HMaster会在zookeeper上创建一个splitlog的节点,将“哪个regionserver处理哪个region”这样的信息以列表的形式存放到该节点上,然后由各个regionserver服务器自行到该节点领取任务并执行。
    ④ Replication管理:
    在zookeeper上记录一个Replication节点,然后把不同的regionserver服务器对应的HLog文件名称记录到对应的节点上,hmaster集群会将新增的数据推送给slave集群,并同时将推送信息记录到zookeeper上。当服务器挂掉时,由于zookeeper上保存了相关推送信息,因此hmaster能够根据这些信息来协调用来推送HLog数据的主节点服务器。
    zookeeper部署,如果一个zookeeper集群需要被几个HBase复用的话,务必为每一个HBase集群指明对应的zookeeper根节点配置(zookeeper.znode.parent),以确保各个HBase集群间互不干扰。HBase中几乎所有的元数据存储都是放在zookeeper上的。

  11. zookeeper与Kafka
    生产者负载均衡:
    生产者需要将消息合理地发送到那些分布式的Broker上。Kafka支持传统的四层负载均衡,也支持使用zookeeper方式来实现负载均衡。
    ① 四层负载均衡:
    根据生产者的IP地址和端口来为其确定一个相关联的Broker。通常一个生产者只会对应单个Broker.缺点:这种方法无法做到真正的负载均衡,因为每个生产者产生的消息总量可能差别很大.
    ② 使用zookeeper进行负载均衡:
    每当一个Broker启动时,会首先会完成Broker注册过程,并注册一些诸如“有哪些可订阅的Topic”的元数据信息。生产者就可以通过这个节点的变化动态地感知Broker服务器列表的变更。这种模式下还能够允许开发人员控制生产者根据一定的规则(如消费者的消费行为)来进行数据分区。

    消费者负载均衡:
    kafka有消费者分组的概念,每个消费者分组中都包含了若干各消费者,每一条消息都智慧发送给分组中的一个消费者。不同的消费者分组消费自己特定的Topic下面的消息。因此消费者的负载均衡也可以看作是同一个消费者分组内部的消息消费策略。
    ① 消息分区与消费者关系:
    kafka规定了每个消费分区有且只能同时有一个消费者进行消息的消费,因此需要在zookeeper中记录下消息分区与消费者之间的对应关系。每个消费者一旦确定了对一个消息分区的消费权利,那么需要将其Consumer ID写入到对应消息分区的临时节点上。
    /brokers/topics/saweaewqew/partitions/0节点内容就是该分区上的消费者Consumer ID
    ② 消息消费进度Offset记录:
    在消费者对指定消息分区进行消息消费的过程中,需要定时地将分区消息的消费进度,即Offset记录到zookeeper上去,以便该消费者进行重启或是其他消费者重新接管该消息分区的消息消费后,能够从之前的进度开始继续进行消息的消费。offset记录在/consumers/test1/offsets/12_simhash/1节点
    消费者注册:
    ③ 注册到消费者分组:
    每个消费者服务器启动的时候,都会到zookeeper的指定节点下创建一个属于自己的临时消费节点,例如/consumers/test1/ids/[consumer_id],完成节点创建后,消费者就将自己订阅的Topic信息写入该节点。
    ④ 对消费者分组中消费者的变化注册监听
    每个消费者都需要关注所属消费者分组中消费者服务器的变化情况,即对/consumers/test1/ids节点注册子节点变化的watcher监听。一旦发现消费者新增或减少,就会触发消费者的负载均衡。
    ⑤ 对Broker服务器的变化注册监听
    消费者需要对/brokers/ids注册监听,如果发现Broker服务器列表发生变化,那么就根据具体情况来决定是否需要进行消费者的负载均衡
    ⑥ 进行消费者负载均衡
    所谓的消费者负载均衡,是指为了能够让同一个Topic下不同分区的消息尽量均衡地被多个消费者消费而进行的一个消费者与消费分区分配的过程。如果组内消费者服务器发生变更或者Broker服务器发生变更,会触发消费者负载均衡。
    合理地设置分区数目至关重要。如果分区数目太小,则有部分消费者可能闲置;如果分区数目太大,则对服务器的性能有影响。

原创粉丝点击