导图社区 分布式思维导图
分布式计算是利用互联网上的计算机的 CPU 的闲置处理能力来解决大型计算问题的一种计算科学。下图讲述了基础理论、一致性协议、zookeeper、kafka等内容。
编辑于2021-08-16 23:09:55分布式
基础理论
分布式的概念
分布式系统指硬件或软件分布在不同的网络计算机上,彼此之间仅仅通过消息传递来通信和协调。可以是按照业务垂直分布(微服务),也可以是同一个业务水平分布(多活)
CAP理论(分布式系统不能同时满足服务可用性、一致性和分区容错性)
服务可用性
数据一致性
分区容错性
BASE理论
base理论是对Cap中一致性和可用性权衡的结果。核心思想是即使无法做到强一致性,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。
基本可用
分布式系统在出现故障的时候,允许损失部分可用性,比如响应时间上的损失和功能上的部分损失
软状态
允许系统中的数据存在中间状态,可以简单理解为允许数据在节点之间同步的时候存在延迟
最终一致性
指系统中的数据副本,在经过一段时间的同步之后,最终能够达到一个一致的状态
一致性协议
两阶段提交(应对的是分布式系统中,不同节点协同完成同一个任务的情况)
有一个事务协调者和多个事务参与者
将事务划分为事务准备阶段和事务提交阶段
事务准备阶段
事务协调者收到写请求,先将写请求记录到自己的日志中。然后通知所有事务参与者执行这个事务,等待事务参与者的回应
事务参与者在收到事务协调者的事务请求之后开始执行事务,并将类似于redo和undo的日志记录到事务日志,但是并不提交,而是回复事务协调者自己是否可以提交事务
事务提交阶段
事务协调者收集所有事务参与者回复的消息,如果所有事务参与者都提交可以回复,那么事务协调者做出裁决,可以提交事务,事务协调者先记录日志,然后回复客户端,之后通知事务参与者提交事务。
如果有某个事务参与者回复事务协调者不能提交事务,事务协调者做出裁决,事务要进行回滚,它先记录日志,然后通知客户端,最后通知事务参与者回滚事务
paxos协议
种类
朴素paxos协议
multi-paxos协议
角色(一个进程可能同时承担多个角色)
提议者(proposer)
倡议者可以提出提议并发起投票表决
接受者(Acceptor)
接受者可以对倡议者提出的提议进行投票表决,从众多提议中选出唯一确定的一个
学习者(Learner)
学习者没有提议和投票权,但是可以从接受者那里获知哪个提议最终被选中
过程
raft协议
角色
Leader(领导者)
Follower(跟随者)
Candidate(候选人)
zab协议
zookeeper
认识
zookeeper是一个分布式协调服务,为其它分布式系统提供分布式相关的共性能力,比如master选举,一致性状态等等,基于zk可以实现集群节点管理、发布订阅、注册中心、master选举、分布式任务等功能
zk数据模型
zk以一个类似于linux系统文件树的方式来组织数据,树中每一个节点叫做ZNode,每一个节点都有一个路径,zk的整个树状结构都保存在内存中,所以操作起来效率是非常高的,zk通过日志来持久化操作,为了防止日志过大,还会有另外的线程根据日志来构造快照,当恢复的时候就是根据快照和近期的日志来进行恢复,这和hdfs的namenode是一样的
zk节点类型
临时节点
临时顺序节点
持久节点
持久顺序节点
容器节点
最后一个子节点被删除(意味着一开始添加过子节点,如果从来没添加过子节点也不会删除),容器节点自动删除(有延迟))
ttl节点/ttl顺序节点
指定时间没有操作就会被删除
zk集群的角色
leader领导者
follower跟随者
observer观察者
zab
崩溃恢复
快速领导者选举
zxid
64bit的值,高32位代表***,低32位代表这个***内的事务编号,单调递增,如果有新的***,低32位会重新开始计数
节点状态
looking
Following
leading
observing
选票
选票至少包含两个信息,一是谁发的选票,二是投给谁,它的zxid是多少
原则
能力最大的人当选领导者,对于zk来说谁的zxid大,谁的数据就新,谁的能力就大,zxid相同就看谁的sid大
选举过程
节点进入looking的状态,先构造一张选票,投给自己,然后发给其它的节点,等待其它节点的选票
其它节点的选票和自己一样,那就计数器加1,如果不一样,那么就要比较能力值,如果对方投的节点能力值大,那么自己要改票,改完票还要再发给其它节点,同时更新自己的投票箱
每个节点都是这样一个过程,当发现某一个选票已经超过半数之后,它就可以当选位leader节点,然后进行广播,集群节点退出选举过程,进入到数据同步的过程
数据同步过程
为什么要数据同步
当集群选举出新的leader之后,要保证集群其它节点的状态和leader是一致的,所以要进行同步
同步过程
统一纪元
leader从follower中收集纪元信息,通过过半机制确定旧的纪元,然后根据旧的纪元生成新的纪元,广播给follower,然后正式开始数据同步
同步数据
leader会给每一个follower开一个队列,然后让follower发送它的zxid过来,根据zxid,leader就知道是follower缺失了哪些数据,leader会发送给follower,follower收到之后可以直接commit。如果followe的zxid更大,leader还会让follower回退,这个过程结束之后集群进入到原子广播模式,就可以对外提供服务了。
原子广播
每一个节点都可以接收客户端的请求,但是只有leader节点可以处理写请求,所以followe或者observer接收到写请求需要转发给leader,如果是读请求,则自己处理
写请求
leader接收到请求之后依次放入到队列,有另外的线程从队列中拿出请求进行处理,如果是写请求,那么leader先写日志,然后像两阶段提交那样发给follower进行投票,当收集到超过半数的投票之后,leader可以做出裁决,是提交还是回滚,如果是提交,那么自己先写日志,更改内存中的database,然后响应客户端,再通知follower提交
读请求
保证事务的顺序性
队列
watch机制
watch机制就是一种事件触发的机制,zk客户端会向zk server注册watcher,并且把watcher对象保存在自己的watcher manager中,事件达成后zk server会通知客户端,客户端就会从watcher manager中取出对应的watcher对象来执行回调逻辑
watcher类型
按触发机制分类
一次性的watcher
老版本3.6.0之前都是一次性的watcher,触发完之后需要重新注册
持久性的watcher
3.6.0之后的版本添加了新的持久性的watcher以及持久性递归的watcher
持久性递归watcher
按事件类型分类
dataWatcher
监听某节点的数据变化,比如新增、修改、删除
childWatcher
监听的是某节点的孩子节点的变化,比如新增或者删除,但是不包括孩子节点的数据修改
exitWatcher
只存在于客户端
watcher事件类型
None
特殊事件类型,用来描述客户端和服务器连接状态的变化
NodeCreated
NodeDataChanged
NodeChildrenChanged
子节点变化事件,只包括子节点的新增和删除,不包括子节点数据的变化
NodeDeleted
zk如何应付脑裂
过半机制
zk的缺点
选举期间服务不可用,选举时间长
一次性watcher可能丢失事件处理
数据版本的变化也会触发NodeDataChanged事件,可能造成不必要的网络开销
节点的数据大小受到限制
zk典型应用
集群管理
数据发布订阅(配置众心)
将配置信息写入某个持久节点,集群机器根据节点配置启动,同时监听节点数据变化
负载均衡
客户端要将请求均匀的发送给服务端,可以在一个持久节点下面创建服务器的临时节点,客户端监听持久节点下的子节点事件,客户端在本地实现负载均衡策略,负载均衡的策略包括随机取模,将服务器构造成环轮询,请求次数摸上服务节点数量等
命名服务
分布式全局唯一递增id
顺序节点的名字中有它的序号,这个序号就可以作为分布式的全局唯一递增id
集群节点管理与心跳检测
每个服务器将自己注册为一个临时节点
闪断
延迟一段时间再删除临时节点,或者使用容器节点
master选举
先创建一个持久节点,然后在持久节点中创建同名的临时节点,谁创建成功谁就是master,其它机器监听这个临时节点的删除事件
应对脑裂
脑裂问题是指某个节点本来已经是master了,但是因为GC时间过长,或者网络原因导致与ZK的连接中断,这个时候临时节点会被删除掉,就会有其它节点被选为master,然后当前节点恢复了,它依然认为自己是master,即产生了脑裂。比如Hdfs的namenode是怎么解决脑裂问题的,namenode创建临时节点成功了,这个namenode就变为active的状态,另外一个就会变为standby的状态,active的namenode还会创建一个持久节点,记录自己的信息,如果这个namenode正常结束会去删掉这个持久节点,但是如果是异常退出的,这个持久节点就不会被删掉,但是临时节点会被删掉,这个时候standby的节点就会创建临时节点,准备变为active状态,但是它发现持久化节点还在,就会先尝试通知另外一个namenode,让它进入standby状态,如果失败,默认就会调用一个脚本杀死原来的namenode进程,让它重启,或者如果用户自定义了隔离脚本也可以调用自定义的隔离脚
分布式锁
独占锁
创建临时顺序节点,监听前一个节点的删除事件,监听到之后查看自己是否是序号最小的节点,如果是,那么就获取锁,否则重新监听并继续阻塞,可重入性在本地实现
读写锁
同样创建临时顺序节点,用名字来区分读锁和写锁,如果当前要申请写锁,实现比较容易,监听前一个节点的删除事件,然后看前面是否还有比自己序号小的节点,有就继续阻塞。如果要申请读锁,就会复杂一点,要看前面是否有写锁,没有的话直接获取锁,然后执行,如果写锁,就要监听写锁的删除事件,如果写锁删除了,要接着进行判断。
kafka
认识
kafka是一个消息中间件,分布式的流数据的处理引擎。它具有普通消息系统一样的功能,解耦、流量削峰、缓冲等功能。同时kafka会持久化消息,可以作为一个流数据的稳定来源,和其它流处理引擎比如flink、spark进行配合,kafka本身也可以处理流数据,只是用到的比较少。
典型应用场景
生产者、消费者解耦
业务拆分
流量缓冲
处理生产者生产消息和消费者消费消息能力不一致的问题
流量削峰
应对突然而至的大流量
流数据源
作为稳定的流数据源,配合其它流数据处理引擎
Producer
重要配置参数
acks
0:代表不需要任何确认就返回了,可能丢失数据
1:leader副本确认写成功就可以返回了,不需要follower写入确认
-1:lsr列表中所有副本都写成功,才会返回
n:lsr列表中n个副本写入成功才会返回
模式
发后即忘
producer的send方法会返回一个Future对象,如果忽略这个对象,那就是不关注结果,不关心消息是否到达
同步模式:调用返回future对象的get方法,会一直阻塞到broker返回消息,只有收到broker的回复才会发送下一个消息
异步回调模式:异步发送消息,注册一个回调函数用于处理broker返回的异常信息
Consumer
消费者组
同一条消息组内的客户端只有一个能消费到,但是一条消息可以被不同组消费
通过消费者组,kafka实际上是间接的实现了点对点的模式和发布订阅模式
一个消费者组的多个客户端每个都可以消费同一个topic的多个分区,实际上是一种横向扩展的方式,可以提高消息的处理效率,提高吞吐量,但是客户端数量不应该超过分区的数量,否则会有客户端空跑
偏移量offset
当消费者崩溃或者发生再均衡的时候,需要从原来消费的位置继续消费,所以要保存消费到的位置,这个位点就是offset
kafka server上有一个特殊的主题,叫做__consumer__offset的特殊主题,消费者向这个主题提交偏移量
提交偏移量的方式
自动提交(默认每隔5秒提交一次)
可能丢失消息处理,也可能重复处理
同步提交
commitSync(),该方法会提交poll方法返回的最新偏移量,在broker每给出响应之前会一直阻塞,如果提交失败,还会一直重试
异步提交
commitAsync(),只发送提交的请求,不等待broker的回复,如果提交失败也不会重试
同步、异步提交相结合
一般情况下,偶尔的提交失败并不重要,因为如果是偶发原因导致的失败,那么只需要后续提交成功就可以了,但是一定要保证最后一次提交成功,所以一般做法是平时用异步提交的方式,在finally代码中用同步提交兜底
提交特定的偏移量
kafka控制器
一般kafka集群会有多个broker节点,其中一个broker节点会被选举为控制器节点,也就是kafka controller,它负责管理整个集群中所有分区和副本的状态。比如分区的leader副本故障了,controller就会负责为该分区选举出新的leader副本。
控制器的竞选
在zookeeper中创建/controller临时节点,谁创建成功,谁就会把自己的id记录到这个临时的节点,谁就当选为了控制器
控制器防止脑裂
有另外一个持久节点来记录controller的纪元,每次选举出新的controller之后,纪元都会加1,每个和controller交互的请求都会带上这个纪元信息,如果一个旧的broker依然认为自己是controller的话,它发送给其它节点的消息还是旧的纪元,这个请求就会视为无效。
分区的数据容错
在kafka中每个分区都可以设置多个副本,以防止其中一个分区发生故障消息丢失,副本又分为leader副本和follower副本,只有leader副本对外提供读写服务,follower副本不对外提供读写服务,仅仅用于备份数据,充当leader副本的备选,在leader副本故障之后可能会当选为新的leader副本
为了保证性能,kafka没有采取强一致性的同步方式,而是维护了一个in-sync-replica的列表,这个列表中的副本就是跟上了同步进度的副本,leader副本不需要等待所有的follower副本都同步完成,一般来说只需要ISR列表中的副本同步完成就可以恢复生产者了。
如何判定一个follower是否跟上了同步的进度?
0.9之前kafka提供了一个配置参数replica lag.max.messages来控制follower副本最多落后leader副本的消息数量。但是这种方式有缺陷,当流量突然增大之后,导致很多follower被踢出同步列表,之后的版本,kafka采用了“落后于消费进度的时间长度来判断是否踢出ISR”
kafka蓄水池
kafka的leader副本与follower副本通过一种类似于蓄水池的方式来进行数据同步,有两个重要的指标,高水位线hw和下一个待写入位置的偏移量leo(log end offset)
hw
高水位线之前的数据都是已经提交了的,可以被消费者消费到
leo
高水位线和lwo之间的消息就是需要同步的消息,消费者还不能消费到
hw和leo的跟新
子主题
hive
hdfs