zk在大型分布式系统中的应用

Hadoop

是一个大型分布式计算框架
定义了一种能开发和运行处理海量数据的软件规范,
来实现一个在大规模集群中对海量数据进行分布式计算的软件平台.

Hadoop的核心是HDFS和MapReduce,
分别提供了对海量数据的存储和计算能力.

在海量数据存储及处理领域,
Hadoop是目前业界公认的最成熟,也是最卓越的开源解决方案.

下面围绕YARN中zk的使用场景来讲解.
- YARN介绍
YARN是Hadoop为提高计算节点Master的扩展性,
同时为支持多计算模型和提高资源的细粒度调度而引入的全新一代分布式调度框架.

- ResourceManager单点问题
为解决ResourceManager单点问题,
YARN设计了一套Active/Standby模式的ResourceManager HA架构
运行期间,
多个ResourceManager并存,
只有一个处于Active状态,
另外的处于Standby状态.
当Active节点无法正常工作,
其余处于Standby状态的节点会通过竞争选举产生新的Active节点

- 准备切换
下面看YARN如何实现多个
ResourceManager之间的主备切换的.
ResourceManager使用基于zk实现的ActiveStandbyElector组件来确定
ResourceManager的状态:Active或Standby
具体如下:
1.创建锁节点
在zk上会有一个类似于/yarn-leader-election/pseudo-yarn-rm-cluster的锁节点,
所有的ResourceManager在启动时候,
会竞争写一个Lock子节点:
/yarn-leader-election/pseudo-yarn-rm-cluster/ActiveStandbyElectorLock
同时,需注意,
该子节点的类型是临时节点,
zk能保证最终只有一个ResourceManaeger能创建成功.
创建成功的那个ResourceManager就切换为Active状态.
没成功的那些ResourceManager则切换为Standby状态.
2.注册Watcher监听
所有Standby状态的ResourceManager会向
/yarn-leader-election/pseudo-yarn-rm-cluster/ActiveStandbyElectorLock
节点注册一个节点变更的Watcher监听,
利用临时节点特性,
能快速感知到Active状态的ResourceManager的运行情况.
3.主备切换
当Active状态的ResourceManager出现诸如重启或挂掉的异常情况时,
其在zk上创建的Lock节点也随之被删除.
其余各个Standby状态的ResourceManager会接收到来自zk服务端的Watcher事件通知,
然后重复步骤1的操作.


- Fencing[隔离]
在分布式环境中,
经常出现诸如单机"假死"情况,
假死是指机器由于网络闪断或其自身由于负载过高
而导致无法正常地对外进行及时响应.

上述主备切换过程中,
假设RM集群由ResourceManager1和ResourceManager2两台机器组成,
且ResourceManager1为Active状态,
ResourceManager2为Standby状态.
某一时刻,
ResourceManager1发生了"假死"
此时,
zk认为ResourceManager1挂了
主备切换,
ResourceManager2会成为Active状态,
但随后
ResourceManager1恢复了正常
其依然认为自己还处于Active状态,
就是常说的分布式"脑裂"现象.
即存在了多个处于Active状态的ResourceManager各司其职.

YARN中引入了Fencing机制,
借助zk数据节点的ACL权限控制机制来实现不同RM间的隔离.
在上文的"主备切换"部分中讲到,
多个RM之间通过竞争创建锁节点来实现主备状态的确定.
创建的根节点需携带zk的ACL信息,
目的是为了独占该根节点,
以防止其他RM对该节点进行更新.

主备切换中,
Fencing机制是如何避免"脑裂"现象出现的.
RM1假死后,
zk会将其创建的锁节点移除掉
此时RM2会创建相应的锁节点,
并切换为Active状态.
RM1恢复之后,
会试图去更新zk的相关数据,
但此时发现其没有权限更新zk的相关节点数据,
也即,RM1发现zk上的相关节点不是自己创建的,
于是,自动切换为Standby状态.
这样,就避免了"脑裂"现象的出现.

- ResourceManager状态存储
在ResourceManager中,
RMStateStore能存储一些RM的内部状态信息,
包括Application及它们的Attempts信息,
Delegation Token及Version Information等.
需注意,
RMStateStore中的绝大多数状态信息不需持久化存储,
因为可从上下文信息将其重构出来.
存储的设计方案中,提供了三种可能的实现:
1.基于内存实现,一般用于日常开发测试
2.基于文件系统的实现,如HDFS
3.基于zk的实现
由于这些状态信息的数据流都不是特别大,
因此Hadoop官方建议基于zk来实现状态信息的存储.
在zk上,
ResourceManager的状态信息都被存储在/rmstore这个根节点下面.

数据节点组织实例:
rmstore下
ZKRMStateRoot下
RMAppRoot,RMVersionNode,RMDTSecretManagerRoot
RMAppRoot下
application_xxxx_xxx, application_xxx_xxx, ...
RMDTSecretManagerRoot下
RMDTSequentialNumber,RMDTMasterKeysRoot,RMDelegationTokensRoot

RMDTMasterKeysRoot下
DelegationKey_1, ..., DelegationKey_n

HBase

是一个基于Hadoop文件系统设计的面向海量数据的高可靠性,高性能,面向列,
可伸缩的分布式存储系统
利用HBase技术可在廉价的PC服务器上搭建起大规模结构化的存储集群.

HBase针对数据写入有强一致性的特性,
甚至包括索引列也都实现了强一致性.
海量数据,大规模服务器集群下如何更好地进行分布式状态协调
成为整个HBase系统正常运转的关键.

下面从
系统容错,RootRegion管理,Region状态管理,分布式SplitLog任务管理
和Replication管理五大方面来讲解zk在HBase中的应用场景.
- 系统容错
当HBase启动时,
每个RegionServer服务器会到zk的/hbase/rs节点下创建一个信息节点
如
/hbase/rs/[Hostname]
同时,
HMaster会对这个节点注册监听,
当某个RegionServer挂掉时候,
zk会因为在一段时间内无法接收其心跳信息,
而删除掉该RegionServer服务器对应的rs状态节点.
HMaster会接收到zk的NodeDelete通知,
从而感知到某个节点断开,
并立即开始容错工作
在HBase的实现中,
HMaster会将该RegionServer所处理的数据分片重新路由到其他节点上,
并记录到Meta信息中供客户端查询

- RootRegion管理
对HBase集群,
数据存储的位置信息是记录在元数据分配上的.

每次客户端发起新的请求,
需知道数据的位置,
就去查询RootRegion
RootRegion自身的位置则记录在zk上
[
默认下,记录在zk的/hbase/root-region-server节点中
]
当RootRegion发生变化,
能通过zk来感知到这一变化并做出一系列相应的容灾措施,
保障客户端总是拿到正确的RootRegion信息

- Region状态管理
Region是HBase中数据的物理切片
每个Region记录了全局数据的一小部分
且不同的Region间的数据是相互不重复的.

对分布式系统,
Region会经常变更
一旦Region发生移动,
必然会经历Offline和重新Online的过程

在Offline期间数据不能被访问,
且Region的这个状态变化需让全局知晓,
否则,
可能会出现某些事务性的异常.
对HBase集群,
Region的数量可能达10w级别,或更多.

- 分布式SplitLog任务管理
某台RegionServer服务器挂掉时,
对一部分新写入还没持久化到HFile的数据,
迁移该RegionServer服务时,
一个重要工作是从HLog恢复这部分还在内存中的数据
HMaster需遍历RegionServer的HLog
按Region切分成小块移动到新地址下
进行数据Replay

由于单个RegionServer日志量相对庞大
可将处理HLog的任务分配给多台RegionServer服务器共同处理.

当前HMaster会在zk上创建一个splitlog的节点
存储哪个RegionServer处理哪个Region
由各个RegionServer服务器自行到该节点上去领取任务并在任务执行成功或失败后,
再更新该节点的信息,
以通知HMaster继续后面的步骤.

- Replication管理
Replication是实现HBase中主备集群间的实时同步的重要模块,
借助Replication,
HBase可实现实时的主备同步,
从而拥有容灾和分流等关系型数据库才有的功能

HBase借助zk完成Replication.
在zk上记录一个replication节点,
然后把不同的RegionServer服务器对应的HLog文件名称记录到相应的节点上,
HMaster集群会将新增的数据推送给Slave集群,
同时将推送信息记录到zk上.
再重复以上.
服务器挂掉时,
由于zk已经保存了端点信息,
只要HMaster能根据这些端点信息来协调用来推送HLog数据的主节点服务器,
就可继续复制.

- zk部署
HBase的启动脚本可选择是由HBase启动其自带的默认zk
还是用一个已有的外部zk集群.

Kafka

Kafka主要用于实现低延时的发送和收集大量的事件和日志数据
Kafka是一个吞吐量极高的分布式消息系统,
其整体设计是典型的发布与订阅模式系统.

Kafka中所有服务器是对等的.
因此,
可在不做任何配置更改下实现服务器添加和删除,
消息的生产者和消费者也能随意重启和机器的上下线

- 术语介绍
1.消息生产者
生成消息并发送到Kafka服务器
2.消息消费者
负责消费Kafka服务器上的消息
3.主题
由用户定义并配置在Kafka服务端,
用于建立生产者和消费者之间的订阅关系:
生产者发送消息到指定Topic下,
消费者从这个Topic下消费消息
4.消息分区
一个Topic下面会分成多个分区
这个Topic可分为10个分区,
分别由两台服务器提供,
通常可配置为让每台服务器提供5个分区,
设服务器ID分别为0和1,
则所有分区为
0-0, 0-1, 0-2, 0-3, 0-4
和
1-0, 1-1, 1-2, 1-3, 1-4
消息分区机制和分区的数量与消费者的负载均衡机制有很大关系
5.Broker
即Kafka服务器
用于存储消息.
在消息中间件中常被称为Broker
6.消费者分组
用于归组同类消费者
Kafka中,
多个消费者可共同消费一个Topic下的消息,
每个消费者消费其中的部分消息,
这些消费者组成一个分组,
有同一个分组名称,
也称为消费者集群.
7.Offset
消息存储在Kafka的Broker
消费者拉取消息数据需知道消息在文件中的偏移量

- Broker注册
Kafka是一个分布式消息系统
也体现在其Broker,Producer和Consumer的分布式部署上.
虽然Broker是分布式部署且相互间独立运行,
但还需有一个注册系统能将 整个集群中的Broker服务器都管理起来,
在Kafka设计中,
用zk进行所有Broker的管理

在zk上有一个专门用来进行Broker服务器列表记录的节点,
称为 Broker节点,其节点路径为/brokers/ids

每个Broker服务器启动时,
会到zk上进行注册,
即到Broker节点下创建属于自己的节点,
其节点路径为
/broker/ids/[0..N]

在Kafka中,
使用一个全局唯一的数字来指代每一个Broker服务器,
称其为"Broker ID"
不同的Broker需使用不同的Broker ID进行注册,
如
/broker/ids/1和/broker/ids/2分别代表了两个Broker服务器

创建完Broker节点后,
每个Broker会将自己的IP地址和端口等信息写入到该节点中去.

Broker创建的节点是一个临时节点,一旦此Broker服务器宕机或下线,
对应的Broker节点也被删除.

- Topic注册
在Kafka中,
会将同一个Topic的消息分成多个分区并将其分布到多个Broker上,
这些分区信息及与Broker的对应关系也都由zk维护,
由专门的节点记录,
其节点路径为/brokers/topics.
Kafka中每一个Topic,
都以/brokers/topics/[topic]的形式记录在这个节点下,
在Broker服务器启动后,
会到对应Topic节点下注册自己的BrokerID,
并写入针对该Topic的分区总数.

对"login"这个Topic消息,
提供了2个分区进行消息存储.
此分区数节点也是一个临时节点.
- 生产者负载均衡
Kafka会对同一个Topic的消息进行分区并将其分布到不同的Broker服务器上.
生产者需要将消息合理的发送到这些分布式的Broker上.
对生产者的负载均衡,Kafka支持四层负载均衡,也支持使用zk方式.
- 四层负载均衡
一般是根据生产者的IP地址和端口来为其确定一个相关联的Broker.
一个生产者只对应一个Broker.
然后该生产者生成的消息都发给此Broker.

系统运行中,
每个生产者生成的消息量,
每个Broker的消息存储量不一样,
如有的生产者产生的消息多于其他生产者,
会导致不同的Broker收到的消息总数不均匀.
生产者也无法实时感知到Broker的新增与删除.

- 使用zk进行负载均衡
客户端用基于zk的负载均衡策略来解决生产者的负载均衡问题.
每个Broker启动时,
先完成Broker注册过程,
并注册如"有哪些可订阅的Topic"的元数据信息.
生产者能通过此节点的变化来动态感知到Broker服务器列表的变更.

zk在整个生产者负载均衡过程中扮演了重要角色.
通过zk的Watcher能让生产者动态获取Broker和Topic的变化.

- 消费者负载均衡
Kafka中的消费者同样需负载均衡来实现多个消费者
合理地从对应的Broker服务器上接收消息.
Kafka有消费者分组概念,
每个消费者分组中含若干消费者,
每条消息都只发给分组中的一个消费者,
不同消费者分组消费自己特定Topic下的消息.

- 消费分区与消费者关系
对每个消费者分区,
Kafka会为其分配一个全局唯一的Group ID,
同一个消费者分组内部的所有消费者都共享该ID.
Kafka也会为每个消费者分配一个Consumer ID,
通常用"Hostname:UUID"的形式来表示.
需要在zk上记录下消息分区与消费者间的对应关系.

每个消费者一旦确定了对一个消息分区的消费权利,
则需将其Consumer ID写入到对应消息分区的临时节点上.
如
/consumers/[group_id]/owners/[topic]/[broker_id-partition_id]
broker_id-partition_id是一个消息分区标识.
节点内容是消费该分区上消息的消费者的Consumer ID.

- 消息消费进度Offset记录
在消费者对指定消息分区进行消息消费过程中,
需定时将分区消息的消费进度记录到zk上,
以便在该消费者重启或其他消费者重新接管该消息分区的消息消费后,
能从之前的进度开始继续进行消息消费.
Offset在zk上的记录由一个专门节点负责,
节点路径为
/consumers/[group_id]/offsets/[topic]/[broker_id-partition_id]
其节点内容就是Offset值

- 消费者注册
消费者服务器在初始化启动时加入消费者分组过程
1.注册到消费者分组
每个消费者服务器在启动时,
会到zk的指定节点下创建一个属于自己的消费者节点,
如
/consumers/[group_id]/ids/[consumer_id]
完成节点创建后,
消费者会将自己订阅的Topic信息写入该节点.
2.对消费者分组中消费者的变化注册监听
每个消费者需关注所属消费者分组中消费者服务器的变化情况,
即对/consumers/[group-id]/ids节点注册子节点变化的Watcher监听.
一旦发现消费者新增或减少,
会触发消费者的负载均衡.
3.对Broker服务器的变化注册监听
消费者需对/broker/ids/[0...N]
中的节点进行监听的注册
如发现Broker服务器列表发生变化,
需根据具体情况来决定是否需进行消费者的负载均衡
4.进行消费者负载均衡
为能让同一个Topic下不同分区的消息尽量均衡地被多个消费者消费
而进行的一个消费者与消息分区分配的过程.
对一个消费者分组,
如组内的消费者服务器发生变更或Broker服务器发生变更,
会触发消费者负载均衡.

- 负载均衡
Kafka借助zk上记录的Broker和消费者信息,
采用一套特殊的消费者负载均衡算法.
简单陈述如下:
将一个消费者分组的每个消费者记为C_{1}, ..., C_{G}
则对一个消费者C_{i}
其对应的消息分区分配策略如下:
1.设置P_{T}为指定Topic所有的消息分区
2.设置C_{G}为同一个消费者分组中的所有消费者
3.对P_{T}进行排序,使分布在同一个Broker服务器上的分区尽量靠在一起
4.对C_{G}进行排序
5.设置i为C_{i}在C_{G}中位置的索引值,
同时设置N=size(P_{T})/size(C_{G})
6.将编号为i*N~(i+1)*N-1的消息分区分配给消费者C_{i}
7.重新更新zk上消息分区与消费者C_{i}的关系

- 小结
Kafka从设计初就是一个大规模的分布式消息中间件,
其服务端存在多个Broker,
同时为达到负载均衡,
将每个Topic消息分成多个分区,
并分布在不同的Broker上,
多个生产者和消费者能同时发和接收消息.
Kafka用zk作为其分布式协调框架,
很好将消息生产,消息存储,消息消费过程有机结合.

猜你喜欢

转载自blog.csdn.net/x13262608581/article/details/117171703#comments_16748712
今日推荐