仅自己的期末复习笔记,有些零散,但大部分包括了往年例题的考点,来自于书本和其他博客。觉得我写的乱的也可以根据这些考点自己去百度和csdn其他大佬的博客自己填充~
目录
故障 故障检测 故障屏蔽 故障解决方法
心跳检测
Lease租约机制
数据分布方式 副本
数据副本
云服务
cap
异常
远程调用 RMI
RPC
RMI
编辑
进程间通信 Socket
TCP、UDP、Socket、远程过程调用RPC、远程方法调用RMI的理解,并阐述关联
故障处理
Raft共识算法
编辑 互斥
编辑 编辑缓存CDN
解耦
系统框架
编辑
分布式文件系统
向量时钟
Lamport
间接通信
网络分区
总控节点每隔一段时间向work节点发送一个心跳包,如果一切正常,work节点就会回复总控节点的心跳包,同时心跳包中会含有work节点的机器的运行情况(如load值, cpu和IO的使用情况)。否则总控节点尝试一定次数后仍收不到回包则认为work节点出现了故障。
心跳检测机制检测结果并不一定可靠。
比如可能网络问题(网络中断、 网络拥塞造成的所谓“瞬断” )或者work节点繁忙未应答,总控节点认为work节点失效,但其实work节点仍在正常提供服务。在分布式系统中,这个是存在一定业务风险的。他的问题主要在于它是单方面判定节点失效。比如,总控节点认为work节点失效,所以为服务重新选取了新的主服务节点,而判定失效的节点可能正在继续正常工作,导致"双主"问题。
Lease就是由颁发者在某一有效期内的承诺。颁发者一旦发布lease给被颁发者不论状态如何是否收到都会被颁发者是为正常状态,只要lease不过期,颁发者就会严守承诺;接收方在lease的有效期内可以使用承诺,但一旦lease过期,被颁发者一定不能用lease。在授权期限内颁发者默认被颁发者为正常状态,超出这个期限则认为他是异常的。即使期间发生了异常,也要等期限到了再回收lease。
例如:A、B、C节点向Q节点发送“心跳”报告自身状态,Q节点收到后向A、B、C发送lease表示知道A、B、C节点正常,允许该节点在lease有效期内工作;Q会给primary节点一个特殊的lease。
Lease机制牺牲了A,获得了完全的C和很好的P。
确定节点状态
利用Lease机制设计分布式Cache系统,基本原理:
中心节点在向各节点发送数据的同时发送一个Lease,Lease具有一个有效时长,一旦超过该时长,Lease失效。
假设系统全局时钟一致,在发出Lease后的有效时长内,中心节点保证不对相应的数据进行修改。
节点在收到数据与Lease之后,将数据置于本地Cache,当对应Lease超时,删除相应Cache数据。
当中心节点需要修改数据时,先阻塞所有的读请求,并等待之前为该数据发出的Lease超时,然后对数据进行修改。
具体的服务器器与客户端节点⼀一个基本流程如下:
流程 2.3.1:基于 lease 的 cache,客户端节点读取元数据
1. 判断元数据是否已经处于本地 cache 且 lease 处于有效期内
1.1 是:直接返回 cache 中的元数据
1.2 否:向中⼼心服务器器节点请求读取元数据信息 26
1.2.1 服务器器收到读取请求后,返回元数据及⼀一个对应的 lease
1.2.2 客户端是否成功收到服务器器返回的数据
1.2.2.1 失败或超时:退出流程,读取失败,可重试
1.2.2.2 成功:将元数据与该元数据的 lease 记录到内存中,返回元数据
流程 2.3.2:基于 lease 的 cache,客户端节点修改元数据流程
1. 节点向服务器器发起修改元数据请求。
2. 服务器器收到修改请求后,阻塞所有新的读数据请求,即接收读请求,但不不返回数据。
3. 服务器器等待所有与该元数据相关的 lease 超时。
4. 服务器器修改元数据并向客户端节点返回修改成功。
基本流程:
1:primary节点进行协调数据更新
2:外部节点向primary节点发送更新操作
3:primary节点对更新操作进行并发控制(即排列操作的先后顺序)
4:将更新操作发送给secondary节点
5:primary根据secondary节点的完成情况决定更新是否成功,返回外部节点。
数据丢失问题:
1.生产者存放消息的过程中丢失消息
解决方案:
确认机制。每次生产者发送的消息都会分配一个唯一的id,如果写到了消息队列中,则broker会回传一个ack消息,说明消息接收成功。否则采用回调机制,让生产者重发消息。
2.消息队列丢失消息
解决方案:
broker在消息刷盘之后再给生产者响应。假设消息写入缓存中就返回响应,那么机器突然断电这消息就没了,而生产者以为已经发送成功了。
如果broker是集群部署,有多副本机制,则消息不仅要写入当前broker,还需要写入副本机中。配置成至少写入两台机子后再给生产者响应,这样基本就能保证存储的可靠了。
3.消费者丢失消息
解决方案:
消费者处理完消息,主动ack.
消息乱序问题:
生产者向消息队列按照顺序发送了 2 条消息,消息1:增加数据 A,消息2:删除数据 A。
期望结果:数据 A 被删除。
但是如果有两个消费者,消费顺序是:消息2、消息 1。则最后结果是增加了数据 A。
解决方案:
1.全局有序
只能有一个生产者往topic发送消息,并且一个topic内部只能有一个队列。消费者也必须单线程消费这个队列。
2.部分有序
将topic内部拆分,创建多个内存queue,消息1和消息2进入同一个queue。
创建多个消费者,每个消费者对应一个queue。
解决消息积压问题:
消息队列中很多消息来不及消费,场景如下:
消费者都挂了
消费者消费的速度太慢了
解决方案:
修复代码层面消费者的问题。
停掉现有的消费者。
临时建立好原先5倍的Queue数量
临时建立好原先5倍的消费者。
将堆积消息全部转入临时的Queue
解决消息过期失效
解决方案:
准备好批量重导的程序
手动将消息闲时批量重导
解决重复消费问题
插入数据库场景:
每次插入数据时,先检查数据库中是否有这条数据的主键id,如果没有,则进行更新操作。
写redis场景:
redis的set操作天然幂等性
其他场景:
生产者发送每条数据时,增加一个全局唯一id,每次消费时,去redis中检查是否有这个id,如果没有,则进行正常消息处理。若有,则说明之前消费过,避免重复消费。
异步复制数据导致数据丢失
主节点异步同步数据给从节点过程中,主节点宕机了,导致部分数据未同步到从节点,而该从节点又被选举为主节点,这个时候就有部分数据丢失了。引起机器宕机的原因可能是停电、内存错误等。发生机器宕机时,节点无法进入可用状态,机器需要重启,但是内存会被清空。
解决方法:
一些节点需要读取本地储存设备当中的信息或其他节点的信息来恢复内存信息,还有一些“无状态”节点无需读取任何信息即可进入可用状态。
(1) 客户端(client)以本地调用方式(即以接口的方式)调用服务;
(2) 客户端存根(client stub)接收到调用后,负责将方法、参数等组装成能够进行网络传输的消息体(将消息体对象序列化为二进制);
(3) 客户端通过sockets将消息发送到服务端;
(4) 服务端存根( server stub)收到消息后进行解码(将消息对象反序列化);
(5) 服务端存根( server stub)根据解码结果调用本地的服务;
(6) 本地服务执行并将结果返回给服务端存根( server stub);
(7) 服务端存根( server stub)将返回结果打包成消息(将结果消息对象序列化);
(8) 服务端(server)通过sockets将消息发送到客户端;
(9) 客户端存根(client stub)接收到结果消息,并进行解码(将结果消息发序列化);
(10) 客户端(client)得到最终结果。
Socket类别打电话:区号相当于网络地址,一个交换机相当于一个主机;用户的手机相当于一个socket,电话号码相当于socket号;要想拨打电话双方都要有手机和手机号,所以要想进行服务就建立在双方都有socket端口和socket号的前提下;拨打电话相当于发送连接请求;对方处于空闲相当于对方主机开机并可以接受连接请求;接通电话相当于连接成功,挂电话相当于关闭socket并撤销连接。
TCP:面向连接,可靠性好,基于字节流
UDP:非面向连接,可靠性差,基于数据报
Socket:独立于具体协议的网络编程接口,对TCP/IP的封装,编程人员可以通过Socket控制数据在客户端与服务器之间业务的逻辑交互,类似汽车的发动机
RPC:是一种远程调用方法,基于http协议,通过C/S模式,向服务器发送请求并等待返回结果。
RMI:是java独有的,基于不同网路节点上的java虚拟机与Java对象之间的相互调用,使用TCP/IP传输java对象,使用RMI传输需要将对象实例化,因为不同java虚拟机的java对象无法共享,所以采用序列化进行对象间的数据交互。RMI是面向对象方式的javaRPC。
e.g. 服务端挂掉/ 服务端更新而客户端还用的老版本
解决方案:使用 返回值(-1)、exception、signal等代表异常
客户端设置一个timer,超时重发
客户端设置一个timer,超时则重发;对于不幂等的请求,为请求分配序号,服务器区别不同的请求
问题:计算在服务端进行却没有接收计算结果的一端(孤儿),浪费了计算资源,并且可能在计算时还会锁定数据;还可能导致混淆,因为客户端重启后重新调用RPC可能收到上一个RPC的结果。
解决方案:1)extermination:在客户端存根发送RPC前,将操作记录在安全存储的 log entry上,重启后,查看 log entry 然后将 孤儿终止(为每个RPC写 log entry 代价较高,如果网络切分了也无法终止孤儿)2)reincarnation:给时间划分不同的时期,当客户端重启时,开始一个新的时期,并将这个消息广播出去,服务器收到这个消息时,就终止任务的计算。3)expiration: 给rpc执行一个标准的时间段,未完成任务需显式申请附加配额。
负载可以提高整个架构的抗压和流量的负载能力,将用户请求平均分配到应用服务器,有效的解决了单点失效的问题,通过应用服务器要交互的是数据层,也就是我们所说的MySql或者Oracle,一般在大型分布式站点中面对的都是一群数据库服务器,也是为了有效的防止数据库单点失效的问题,或者在大型应用中的高并发问题,以及和数据库交互的缓存服务器,还有各种类型的文件资源,不同的类型的资源放在不同的服务器,从编程的角度来说这是解耦,其实从实际上来说也就是解耦。
上传文件 下载文件流程图
每个事件对应一个Lamport时间戳,初始值为0
网络分区就是其中的一种故障类型。
通常情况下,网络分区指的是在分布式集群中,节点之间由于网络不通,导致集群中节点形成不同的子集,子集中节点间的网络相通,而子集和子集间网络不通。网络分区是子集与子集之间在网络上相互隔离了。
如何判断是否发生了网络分区?
在分布式集群中,不同的集群架构网络分区的形态略有不同。要判断是否发生了网络分区,需要弄清楚不同的分布式集群架构,即集中式架构和非集中式架构中的网络分区形态是什么样的。
集中式架构的网络分区形态
集中式架构中,Master 节点通常以一主多备的形式部署,Slave 节点与 Master 节点相连接,Master 节点的主和备之间会通过心跳相互通信。
以 Master 节点主备部署为例,如下图所示,集中式架构中的网络分区主要是主节点与备节点之间网络不通,且一部分 Slave 节点只能与主 Master 节点连通,另一部分只能与备 Master 节点连通。
非集中式架构中的网络分区形态
如下图所示,非集中式架构中,节点是对称的,因此网络分区的形态是形成不同子集,子集内节点间可互相通信,而子集之间的节点不可通信。比如,子集群 1 中 Node1、Node2 和 Node4 可以相互通信,子集群 2 中 Node3 和 Node5 也可以相互通信,但子集群 1 和 子集群 2 之间网络不通。
从集中式和非集中式这两种分布式集群架构的网络分区形态可以看出,要判断是否形成网络分区,最朴素的方法就是判断节点之间心跳是否超时,然后将心跳可达的节点归属到一个子集中。
解决方案
由于非集中式系统中,每个节点都是对等的、提供的服务相同,所以当多个子集群之间不可达,或部分节点出现故障后,尽管提供的服务质量(SLA)可能会下降,但并不影响这些剩余节点或子集群对外提供服务。所以,重点是集中式系统的网络分区问题。
分布式锁是实现多个进程有序、避免冲突地访问共享资源的一种方式。
基于共享资源处理网络分区的核心,类似于分布式锁的机制。哪个子集群获得共享资源的锁,就保留该子集群。获得锁的集群提供服务,只有当该集群释放锁之后,其他集群才可以获取锁。
这种方式的问题是,如果获取锁的节点发生故障,但未释放锁,会导致其他子集群不可用。 因此,这种方式适用于获取锁的节点可靠性有一定保证的场景。
基于仲裁和共享资源的网络分区处理方法,都是依赖一个三方的节点或组件,借助这个第三方来保证系统中同时只有一个活动分区。所以,这两种处理方法适用于有一个稳定可靠的三方节点或组件的场景。