上一章-RPC
分布式系统是一个硬件或软件组件分布在不同的网络计算机上,彼此之间仅仅通过消息传递进行通 信和协调的系统。
通俗的理解,所谓分布式系统,就是一个业务拆分成多个子业务,分布在不同的服务器节点,共同 构成的系统称为分布式系统,同一个分布式系统中的服务器节点在空间部署上是可以随意分布的,这些 服务器可能放在不同的机柜中,也可能在不同的机房中,甚至分布在不同的城市。
集群: 多个服务器做同一个事情
分布式: 多个服务器做不同的事情
通信异常
网络本身的不可靠性,因此每次网络通信都会伴随着网络不可用的风险(光纤、路由、DNS等硬件 设备或系统的不可用),都会导致最终分布式系统无法顺利进行一次网络通信,另外,即使分布式 系统各节点之间的网络通信能够正常执行,其延时也会大于单机操作,存在巨大的延时差别,也会 影响消息的收发过程,因此消息丢失和消息延迟变的非常普遍。
网络分区
网络之间出现了网络不连通,但各个子网络的内部网络是正常的,从而导致整个系统的网络环境被 切分成了若干个孤立的区域,分布式系统就会出现局部小集群,在极端情况下,这些小集群会独立 完成原本需要整个分布式系统才能完成的功能,包括数据的事务处理,这就对分布式一致性提出非 常大的挑战。
节点故障
节点故障是分布式系统下另一个比较常见的问题,指的是组成分布式系统的服务器节点出现的宕机 或"僵死"现象,根据经验来说,每个节点都有可能出现故障,并且经常发生.
三态
分布式系统每一次请求与响应存在特有的“三态”概念,即成功、失败和超时。
重发
分布式系统在发生调用的时候可能会出现 失败 超时 的情况. 这个时候需要重新发起调用.
幂等
一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。
也就是 说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同
分布式数据一致性,指的是数据在多份副本中存储时,各副本中的数据是一致的。
分布式系统当中,数据往往会有多个副本。多个副本就需要保证数据的一致性。这就带来了同步的 问题,因为网络延迟等因素, 我们几乎没有办法保证可以同时更新所有机器当中的包括备份所有数据. 就 会有数据不一致的情况
总得来说,我们无法找到一种能够满足分布式系统中数据一致性解决方案。因此,如何既保证数据的一 致性,同时又不影响系统运行的性能,是每一个分布式系统都需要重点考虑和权衡的。于是,一致性级 别由此诞生.
因果一致性
CAP定理(CAP theorem),又被称作布鲁尔定理(Brewer’s theorem),它指出对于一个分布式 计算系统来说,不可能同时满足以下三点
1. 一致性(C-Consistency)
这里指的是强一致性
在写操作完成后开始的任何读操作都必须返回该值,或者后续写操作的结果. 也就是说,在一 致性系统中,一旦客户端将值写入任何一台服务器并获得响应,那么之后client从其他任何服务器 读取的都是刚写入的数据
2. 可用性(A-Availability)
系统中非故障节点收到的每个请求都必须有响应. 在可用系统中,如果我们的客户端向服务器 发送请求,并且服务器未崩溃,则服务器必须最终响应客户端,不允许服务器忽略客户的请求
3. 分区容错性(P-Partition tolerance)
允许网络丢失从一个节点发送到另一个节点的任意多条消息,即不同步. 也就是说,G1和G2发送给 对方的任何消息都是可以放弃的,也就是说G1和G2可能因为各种意外情况,导致无法成功进行同 步,分布式系统要能容忍这种情况。
假设确实存在三者能同时满足的系统
三选二利弊如何
如何进行三选二
放弃了一致性,满足分区容错,那么节点之间就有可能失去联系,为了高可用,每个节点只能用本 地数据提供服务,而这样会容易导致全局数据不一致性。对于互联网应用来说,机器数量庞大,节点分 散,网络故障再正常不过了,那么此时就是保障AP,放弃C的场景,而从实际中理解,像网站这种偶尔 没有一致性是能接受的,但不能访问问题就非常大了。
对于银行来说,就是必须保证强一致性,也就是说C必须存在,那么就只用CA和CP两种情况,当保 障强一致性和可用性(CA),那么一旦出现通信故障,系统将完全不可用。另一方面,如果保障了强一 致性和分区容错(CP),那么就具备了部分可用性。实际究竟应该选择什么,是需要通过业务场景进行 权衡的(并不是所有情况都是CP好于CA,只能查看信息但不能更新信息有时候还不如直接拒绝服务)
上面我们讲到CAP 不可能同时满足,而分区容错性是对于分布式系统而言,是必须的。最后,我们 说,如果系统能够同时实现 CAP 是再好不过的了,所以出现了 BASE 理论,
BASE:全称:Basically Available(基本可用),Soft state(软状态),和 Eventually consistent(最终一 致性)三个短语的缩写 ,Base 理论是对 CAP 中一致性和可用性权衡的结果,其来源于对大型互联网分布 式实践的总结,是基于 CAP 定理逐步演化而来的。
其核心思想是:既是无法做到强一致性(Strong consistency,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
两阶段提交协议
两阶段提交协议,简称2PC(2 Prepare Commit),是比较常用的解决分布式事务问题的方式,要么所 有参与进程都提交事务,要么都取消事务,即实现ACID中的原子性(A)的常用手段。
分布式事务: 事务提供一种操作本地数据库的不可分割的一系列操作 “要么什么都不做,要么做全 套(All or Nothing)”的机制,而分布式事务就是为了操作不同数据库的不可分割的一系列操作 “要 么什么都不做,要么做全套(All or Nothing)”的机制
2PC执行流程
三阶段提交协议出现背景:一致性协议中设计出了二阶段提交协议(2PC),但是2PC设计中还存 在缺陷,于是就有了三阶段提交协议,这便是3PC的诞生背景。
3PC,全称 “three phase commit”,是 2PC 的改进版,将 2PC 的 “提交事务请求” 过程一分为二,共 形成了由CanCommit、PreCommit和doCommit三个阶段组成的事务处理协议。
如果出现了任一一种情况,最终都会导致参与者无法收到 doCommit 请求或者 abort 请求,针对 这种情况,参与者都会在等待超时之后,继续进行事务提交
NWR是一种在分布式存储系统中用于控制一致性级别的一种策略。在亚马逊的云存储系统中,就应用 NWR来控制一致性。
N:在分布式存储系统中,有多少份备份数据
W:代表一次成功的更新操作要求至少有w份数据写入成功
R: 代表一次成功的读数据操作要求至少有R份数据成功读取
NWR值的不同组合会产生不同的一致性效果,当W+R>N的时候,整个系统对于客户端来讲能保证强 一致性。
以常见的N=3、W=2、R=2为例:
N=3,表示,任何一个对象都必须有三个副本
W=2表示,对数据的修改操作只需要在3个副本中的2个上面完成就返回
R=2表示,从三个对象中要读取到2个数据对象,才能返回
在分布式系统中,数据的单点是不允许存在的。即线上正常存在的备份数量N设置1的情况是 非常危险的,因为一旦这个备份发生错误,就 可能发生数据的永久性错误。假如我们把N设 置成为2,那么,只要有一个存储节点发生损坏,就会有单点的存在。所以N必须大于2。N越 高,系统的维护和整体 成本就越高。工业界通常把N设置为3。
Gossip 协议也叫 Epidemic 协议 (流行病协议)。原本用于分布式数据库中节点同步数据使用, 后被广泛用于数据库复制、信息扩散、集群成员身份确认、故障探测等。
从 gossip 单词就可以看到,其中文意思是八卦、流言等意思,我们可以想象下绯闻的传播(或者流 行病的传播);gossip 协议的工作原理就类似于这个。gossip 协议利用一种随机的方式将信息传播到整 个网络中,并在一定时间内使得系统内的所有节点数据一致。Gossip 其实是一种去中心化思路的分布式 协议,解决状态在集群中的传播和状态一致性的保证两个问题。
Gossip 协议的消息传播方式有两种:反熵传播 和 谣言传播
Gossip 协议最终目的是将数据分发到网络中的每一个节点。根据不同的具体应用场景,网络中两个节 点之间存在三种通信方式:推送模式、拉取模式、推/拉模式
综上所述,我们可以得出 Gossip 是一种去中心化的分布式协议,数据通过节点像病毒一样逐个传 播。因为是指数级传播,整体传播速度非常快。
Paxos协议其实说的就是Paxos算法, Paxos算法是基于消息传递且具有高度容错特性的一致性算 法,是目前公认的解决分布式一致性问题最有效的算法之一。
Paxos 解决了什么问题
如何解决2PC和3PC的存在的问题呢?
针对basic Paxos是存在一定得问题,首先就是流程复杂,实现及其困难, 其次效率低(达成一致性需要2轮 RPC调用),针对basic Paxos流程进行拆分为选举和复制的过程.
Paxos 是论证了一致性协议的可行性,但是论证的过程据说晦涩难懂,缺少必要的实现细节,而且 工程实现难度比较高, 广为人知实现只有 zk 的实现 zab 协议。
Paxos协议的出现为分布式强一致性提供了很好的理论基础,但是Paxos协议理解起来较为困难, 实现比较复杂。
然后斯坦福大学RamCloud项目中提出了易实现,易理解的分布式一致性复制协议 Raft。Java, C++,Go 等都有其对应的实现之后出现的Raft相对要简洁很多。引入主节点,通过竞选确定主节点。节 点类型:Follower、Candidate 和 Leader
Leader 会周期性的发送心跳包给 Follower。每个 Follower 都设置了一个随机的竞选超时时间,一 般为 150ms~300ms,如果在这个时间内没有收到 Leader 的心跳包,就会变成 Candidate,进入竞选 阶段, 通过竞选阶段的投票多的人成为Leader![在这里插入图片描述](https://img-blog.csdnimg.cn/6773304078184539a23c5065310fe226.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dKT0VE,size_16,color_FFFFFF,t_70)
竞选阶段流程
Leader节点宕机
多个 Candidate 竞选
日志复制
网络分区
网络分区情况日志复制
通过Raft完整版动画演示可以很好的理解
github也提供一个https://raft.github.io/动画演示地址 . 原理都是一样的.
Lease机制,翻译过来即是租约机制,是一种在分布式系统常用的协议,是维护分布式系统数据一致性 的一种常用工具。
Lease机制有以下几个特点:
分布式系统中,如何确认一个节点是否工作正常?如果有5副本1-5。其中1号为主副本。
2. 会在剩下的副节点中选取一当主节点.
主要解决思路有四种:
- 设计能容忍双主的分布式协议
- Raft协议-通过Term版本高的同步低的.
- 用lease机制
- 涉及去中心化-Gossip协议
lease的容错
应用
在分布式环境下,有几个问题是普遍关心的.
在分布式环境中,我们提及过存在非常多的节点(Node)。那么就有一个非常重要的问题,如 何检测一个节点出现了故障乃至无法工作了?
通常解决这一问题是采用心跳检测的手段,如同通过仪器对病人进行一些检测诊断一样。
心跳顾名思义,就是以固定的频率向其他节点汇报当前节点状态的方式。收到心跳,一般可以认 为一个节点和现在的网络是良好的。当然,心跳汇报时,一般也会携带一些附加的状态、元数据信息, 以便管理
3. 集群模式
集群模式是指有多个节点在运行,同时可以通过主控节点分担服务请求。集群模式需要解决主控 节点本身的高可用问题,一般采用主备模式。
容错顾名思义就是IT系统对于错误包容的能力
容错的处理是保障分布式环境下相应系统的高可用或者健壮性,一个典型的案例就是对于缓存穿透 问 题的解决方案。我们来具体看一下这个例子,如图所示
问题描述:
我们在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容,如果不存在 就直接查询数据库然后再缓存查询结果返回。这个时候如果我们查询的某一个数据在缓存中一直不存 在,就会造成每一次请求都查询DB,这样缓存就失去了意义,在流量大时,或者有人恶意攻击
如频繁发起为id为“-1”的条件进行查询,可能DB就挂掉了。
那这种问题有什么好办法解决呢?
负载均衡:其关键在于使用多台集群服务器共同分担计算任务,把网络请求及计算分配到集群可用 的不同服务器节点上,从而达到高可用性及较好的用户操作体验。
如图,不同的用户client1、client2、client3访问应用,通过负载均衡器分配到不同的节点。
和传统的单体架构相比,分布式多了一个远程服务之间的通信,不管是 soa 还是微服务,他们本 质上都是对于业务服务的提炼和复用。那么远程服务之间的调用才是实现分布式的关键因素。
PC全称为remote procedure call,即远程过程调用。借助RPC可以做到像本地调用一样调用远 程服务,是一种进程间的通信方式. 。常见的RPC框架有一下几种.
在分布式系统中, 会有调用其他业务系统,导致出现跨域问题,跨域实质上是浏览器的一种保护处 理。如果产生了跨域,服务器在返回结果时就会被浏览器拦截(注意:此时请求是可以正常发起的,只是 浏览器对其进行了拦截),导致响应的内容不可用. 产生跨域的几种情况有一下:
分布式协调技术主要用来解决分布式环境当中多个进程之间的同步控制,让他们有序的去访问某 种临界资源,防止造成"脏数据"的后果。
分布式锁也就是我们分布式协调技术实现的核心内容。 分布式锁两种实现方式:
主要是还是来自于互联网的业务场景,例如,春节火车票抢购,大量的用户需要同一时间去抢购; 以及大家熟知的阿里双11秒杀, 短时间上亿的用户涌入,瞬间流量巨大(高并发).
削峰从本质上来说就是更多地延缓用户请求,以及层层过滤用户的访问需求,遵从“最后落地到数据 库的请求数要尽量少”的原则。
当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换 种简单的方式处理,从而释放服务器资源以保证核心服务正常运作或高效运作
整个架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保 证重要或基本的服务能正常运行,我们可以将一些 不重要 或 不紧急 的服务或任务进行服务的 延迟使用 或 暂停使用
当触发服务降级后,新的交易再次到达时,我们该如何来处理这些请求呢?从分布式,微服务架构全局的 视角来看,降级处理方案:
结合服务能否降级的优先原则,并根据台风预警(都属于风暴预警)的等级进行参考设计,可将分布 式服务架构的所有服务进行故障风暴等级划分为以下四种
限流并非新鲜事,在生活中亦无处不在,下面例举一二:
以上"限流"例子,可以让服务提供者稳定的服务客户。
限流的目的是通过对并发访问请求进行限速或者一个时间窗口内的的请求数量进行限速来保护系 统,一旦达到限制速率则可以拒绝服务、排队或等待
在请求到达目标服务接口的时候, 可以使用多维度的限流策略,这样就可以让系统平稳度过瞬间来临的并 发
限流算法-计数器(固定窗口)
计数器限制每一分钟或者每一秒钟内请求不能超过一定的次数,在下一秒钟计数器清零重新计算
存在问题:
客户端在第一分钟的59秒请求100次,在第二分钟的第1秒又请求了100次, 2秒内后端会受到200 次请求的压力,形成了流量突刺
限流算法-计数器(滑动窗口)
滑动窗口其实是细分后的计数器,它将每个时间窗口又细分成若干个时间片段,每过一个时间片 段,整个时间窗口就会往右移动一格
时间窗口向右滑动一格,这时这个时间窗口其实已经打满了100次,客户端将被拒绝访问,时间窗口 划分的越细,滑动窗口的滚动就越平滑,限流的效果就会越精确
限流算法-漏桶
漏桶算法类似一个限制出水速度的水桶,通过一个固定大小FIFO队列+定时取队列元素的方式实 现,请求进入队列后会被匀速的取出处理(桶底部开口匀速出水),当队列被占满后后来的请求会 直接拒绝(水倒的太快从桶中溢出来)
优点是可以削峰填谷,不论请求多大多快,都只会匀速发给后端,不会出现突刺现象,保证下游服 务正常运行 , 缺点就是在桶队列中的请求会排队,响应时间拉长
限流算法-令牌桶
令牌桶算法是以一个恒定的速度往桶里放置令牌(如果桶里的令牌满了就废弃),每进来一个请求 去桶里找令牌,有的话就拿走令牌继续处理,没有就拒绝请求
令牌桶的优点是可以应对突发流量,当桶里有令牌时请求可以快速的响应,也不会产生漏桶队列中 的等待时间, 缺点就是相对漏桶一定程度上减小了对下游服务的保护
【熔断】, 熔断这一概念来源于电子工程中的断路器(Circuit Breaker)。在互联网系统中,当下游 服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服 务的调用。这种牺牲局部,保全整体的措施就叫做熔断。
如果不采取熔断措施,我们的系统会怎样呢? 举例说明:
当前系统中有A,B,C三个服务,服务A是上游,服务B是中游,服务C是下游. 它们的调用链如下:
一旦下游服务C因某些原因变得不可用,积压了大量请求,服务B的请求线程也随之阻塞。线程资源 逐渐耗尽,使得服务B也变得不可用。紧接着,服务 A也变为不可用,整个调用链路被拖垮。
像这种调用链路的连锁故障,叫做雪崩。
在这种时候,就需要我们的熔断机制来挽救整个系统。
这里需要解释两点:
分布式微服务架构上通过业务来划分服务的,通过REST调用对外暴露的一个接口,可能需要很多个 服务协同才能完成这个接口功能,如果链路上任何一个服务出现问题或者网络超时,都会形成导致接口 调用失败。随着业务的不断扩张,服务之间互相调用会越来越复杂。
随着服务的越来越多,对调用链的分析会越来越复杂。它们之间的调用关系也许如下:
分布式链路追踪(Distributed Tracing),也叫 分布式链路跟踪,分布式跟踪,分布式追踪 等等. 其 实就是将一次分布式请求还原成调用链路。显示的在后端查看一次分布式请求的调用情况,比如各个节 点上的耗时、请求具体打到了哪台机器上、每个服务节点的请求状态等等。
Trace调用模型,主要有以下概念:
Client && Server:对于跨服务的一次调用,请求发起方为client,服务提供方为Server各术语在一次 分布式调用中,关系如下图所示
链路跟踪系统实现:
大的互联网公司都有自己的分布式跟踪系统,比如Google的Dapper,Twitter的zipkin,淘宝的鹰 眼,新浪的Watchman,京东的Hydra等等.
架构最重要的就是编程思想:
开闭原则:软件实体应当对扩展开放,对修改关闭,这就是开闭原则的经典定义。 这里的软件实体包括以下几个部分:
开闭原则是面向对象程序设计的终极目标,它使软件实体拥有一定的适应性和灵活性的同时具备稳定性 和延续性。具体来说,其作用如下。
单一职责原则又称单一功能原则,这里的职责是指类变化的原因,单一职责原则规定一个类应该有 且仅有一个引起它变化的原因,否则类应该被拆分。
该原则提出对象不应该承担太多职责,如果一个对象承担了太多的职责,至少存在以下两个缺点:
单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。如果遵循单一职责原则将有 以下优点。
单一职责原则是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,再封装到不 同的类或模块中。而发现类的多重职责需要设计人员具有较强的分析设计能力和相关重构经验。下面以 大学学生工作管理程序为例介绍单一职责原则的应用。
大学学生工作管理程序:
接口隔离原则要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客 户感兴趣的方法。
接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两 者是不同的:
接口隔离原则是为了约束接口、降低类对接口的依赖性,遵循接口隔离原则有以下 5 个优点。
在具体应用接口隔离原则时,应该根据以下几个规则来衡量。
接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑。 为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。
了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同深 入了解业务逻辑。
提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。
里氏替换原则主要阐述了有关继承的一些原则。里氏替换原则是继承复用的基础,它反映了基类与 子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。总结:子类可以扩展父类的 功能,但不能改变父类原有的功能
里氏替换原则的主要作用如下。
根据上述理解,对里氏替换原则的定义可以总结如下:
依赖倒置原则的原始定义为:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依 赖细节,细节应该依赖抽象。其核心思想是:要面向接口编程,不要面向实现编程。
依赖倒置原则的主要作用如下。
迪米特法则又叫作最少知识原则, 迪米特法则的定义是:只与你的直接朋友交谈,不跟“陌生人”说 话。其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方 转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
迪米特法则要求限制软件实体之间通信的宽度和深度,正确使用迪米特法则将有以下两个优点。
从迪米特法则的定义和特点可知,它强调以下两点:
合成复用原则(Composite Reuse Principle,CRP)又叫组合/聚合复用原则
(Composition/Aggregate Reuse Principle,CARP)。它要求在软件复用时,要尽量先使用组合或者 聚合等关联关系来实现,其次才考虑使用继承关系来实现。
如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的, 两者都是开闭原则的具体实现规范
通常类的复用分为继承复用和合成复用两种,继承复用虽然有简单和易实现的优点,但它也存在以下缺 点。