编程是一门艺术,它的魅力在于创造。
65 哥已经工做两年了,一直作着简单重复的编程工做,活活熬成了一个只会 CRUD 的打工 boy。nginx
65 哥:老是听大佬讲分布式分布式,什么才是分布式系统呢?
分布式系统是一个硬件或软件系统分布在不一样的网络计算机上,彼此之间仅仅经过消息传递进行通讯和协调的系统。在一个分布式系统中,一组独立的计算机展示给用户的是一个统一的总体,就好像是一个系统似的。系统拥有多种通用的物理和逻辑资源,能够动态的分配任务,分散的物理和逻辑资源经过计算机网络实现信息交换。程序员
65 哥:巴拉巴拉,能不能讲点人话。俺听不懂。
那好,下面咱们从日常最熟悉的事物开始理解分布式系统如何出现,发展的,并通过实践总结通用的理论,这些理论成为指导咱们如何设计更完善的分布式系统的基础。web
今后篇文章,你将学习到如下知识:算法
为何会出现分布式应用?sql
65 哥:这个我也不清楚啊,之前我写的 web 应用都是直接扔进 Tomcat 中,启动 Tomcat 就能够访问了,这确定不是分布式应用。
嗯,咱们就从你们最熟悉 web 后台应用讲起,之前咱们的系统访问量小,业务也不复杂,一台服务器一个应用就能够处理全部的业务请求了,后来咱们公司发达了,访问量上去了,业务也拓展了,虽然老板依旧没有给咱们加工资,确老是埋怨咱们系统不稳定,扛不住大并发,是可忍孰不可也,加钱,咱们要升级。数据库
若是咱们的服务器能够无限添加配置,那么一切性能问题都不是问题。
为提升系统处理能力,咱们首先想到的扩展方式就是升级系统配置,8 核 cpu 升级为 32 核,64 核,内存 64G 升级为 128G,256G,带宽上万兆,十万兆,这就叫作垂直扩展
。但这样的扩展终将没法持续下去,缘由以下。编程
没有什么能够拦住咱们编程打工人的步伐。安全
俗话说,系统撑不住了,就加服务器,一台不行就加两台。
当垂直扩展
到达技术瓶颈或投入产出比超过预期,咱们能够考虑经过增长服务器数量来提升并发能力,这种方式就是水平扩展
。服务器
65 哥:哦,这就是分布式系统了?这么简单的么。
我勒个呵呵,哪有那么简单,在水平扩展
中,咱们增长了服务器数量,可是如何让这些服务器像一个总体同样对外提供稳定有效的服务才是关键。既然已经有了多台服务器,咱们就要考虑如何将系统部署到到不一样的节点上去。网络
65 哥:这还不简单,我将个人 SpringBoot 项目部署到多台服务器上,前面加个 nginx 就能够了,如今咱们的系统都是这样的,稳定高效 perfect。给你画个架构图(小声,这个我在学校时就会了。)
哪有什么岁月静好,只不过是有人在为你负重前行
。上面你所认为的简单,其实有两个缘由:
系统拆分也有两种方式,垂直拆分
和水平拆分
,注意,这里和上面提到的垂直扩展
和水平扩展
不是处理同一个问题的。(65 哥:哈哈,我知道,世间万物不外乎纵横二字)。
系统的垂直拆分
,就是将相同的系统部署多套,全部的节点并无任何不一样,角色和功能都同样,它们各自分担一部分功能请求,这样整个系统的处理能力的上升了。
从处理 web 请求上来看,垂直拆分
的每一个节点都处理一个完整的请求,每一个节点都承担一部分请求量;
从数据存储的角度看,每一个数据节点都存储相同的业务数据,每一个节点存储一部分数据。
系统的水平拆分
,就是将系统按不一样模块或角色拆分,不一样的模块处理不一样的事情。
从 web 请求上来看,须要多个相互依赖的系统配合完成一个请求,每一个节点处理的需求不一致;
从数据存储角度上来看,每一个数据节点都存储着各自业务模块相关的数据,它们的数据都不同。
上面垂直拆分
以后各个节点组成的就是一个集群
,而水平拆分
各个节点就是分布式
。这就是集群
和分布式
的区别。集群
除了上面提到的能够提升并发处理能力外,还能够保证系统的高可用,当一部分节点失效后,整个系统依旧能够提供完整的服务。分布式
也同样,除了提升并发能力,解耦系统,使系统边界更清晰,系统功能更内聚也是其一大好处,因此在实际的系统中咱们每每这两种方式同时都在使用,并且咱们经常说起的分布式系统
实际上是包含着集群
的概念在里面的。
概括和演绎是人类理性的基石,学习和思考就是不断的概括过去的经验,从而获得广泛的规律,而后将得之的规律演绎于其余事物,用于指导更好的实践过程。
上面咱们讲解了分布式系统的由来,如今咱们回顾和总结一下这个过程。咱们引入分布式系统必然是基于现实的需求和目标而来的。
65 哥:那分布式的目标什么呢?
分布式就是为了知足如下目标而设计的:
Transparency: 透明性,即用户是不关心系统背后的分布式的,不管系统是分布式的仍是单机的,对用户来讲都应该是透明的,用户只须要关心系统可用的能力。这里的透明性就包括如下方面:
分布式的挑战来源于不肯定性。想想,分布式系统相对于单体应用,多了那些东西?
65 哥:有了更多的服务节点,还有就是服务之间的网络通讯。
是的,看来 65 哥同窗已经懂得思考和分析系统了。分布式系统的全部挑战就来源于这二者的不肯定性。
节点数量越多,出故障的几率就变高了。分布式系统须要保证故障发生的时候,系统仍然是可用的,这就须要系统可以感知全部节点的服务状态,在节点发生故障的状况下将该节点负责的计算、存储任务转移到其余节点。
节点间经过网络通讯,咱们都知道网络是不可靠的。可能的网络问题包括:网络分割、延时、丢包、乱序。相比单机过程调用,网络通讯最让人头疼的是超时已经双向通行的不肯定性。出现超时状态时,网络通讯发起方是没法肯定当前请求是否被成功处理的。
在不可靠的网络和节点中,分布式系统依然要保证其可用,稳定,高效,这是一个系统最基本的要求。所以分布式系统的设计和架构充满了挑战。
分布式系统就是充分利用更多的资源进行并行运算和存储来提高系统的性能,这就是分而治之
的原理。
65 哥:哦,懂了懂了,那 MapReduce 的 map,Elasticsearch 的 sharding,Kafka 的 partition 是否是都是分布式的分而治之原理。
能够啊,65 哥同窗不只可以概括,还可以触类旁通了。不错,不管是 map,sharding 仍是 partition,甚至 请求路由负载均衡
都是在将计算或数据拆分,再分布到不一样的节点计算和存储,从而提升系统的并发性。
一样是分
,在不一样领域的,甚至不一样实现的系统中一般会有不一样的说法。sharding 一般是在数据存储系统中将不一样数据分布到不一样节点的方式,中文一般翻译为数据分片
。
好比在 MongoDB 中,当 MongoDB 存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。这时,咱们就能够经过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据。
好比在 Elasticsearch 中,每一个索引有一个或多个分片,索引的数据被分配到各个分片上,至关于一桶水用了 N 个杯子装。分片有助于横向扩展,N 个分片会被尽量平均地(rebalance)分配在不一样的节点上。
partition
的概念常常在 Kafka 中能够看到,在 kafka 中 topic 是一个逻辑概念,从分布式队列的角度看,topic 对使用者来讲就是一个队列,topic 在 kafka 的具体实现中,由分布在不一样节点上的 partition 组成,每一个 partition 就是根据分区算法拆分的多个分区,在 kafka 中,同一个分区不能被同一个 group 下的多个 consumer 消费,因此一个 topic 有多少 partition 在必定意义上就表示这个 topic 具备多少并发处理能力。
在 Amazing 的分布式数据库DynamoDB
中,一张表在底层实现中也被分区为不一样的 partition。
负载均衡是高可用网络基础架构的关键组件,一般用于将工做负载分布到多个服务器来提升网站、应用、数据库或其余服务的性能和可靠性。
好比 nginx 的负载均衡,经过不一样的负载均衡分配策略,将 http 请求分发到 web 应用的不一样节点之上,从而提升应用的并发处理能力。
好比 dubbo 的客户端负载能力,能够将 dubbo 请求路由到具体的 producer 提供节点上,负载均衡是一个完善的 RPC 所应该具备的能力。
在 Spring Cloud 的体系中 Robbin 组件能够经过 Spring Cloud 的各微服务之间通讯的负载均衡分配问题,依旧是将请求分发到集群中的不一样节点上去。
不管是分区仍是分片,仍是分区路由,其实都有一些通用的分区算法,如下的概念可能不少同窗都在不一样的领域看到过,如上面看到的反向代理服务器 nginx 中,如分布式消息队列 kafka 中,如 RPC 框架 Dubbo 中,这有时候会让不少同窗感到懵。
其实不管在什么领域中,你只要抓住它在完成的核心功能上就能够理解,它们就是在考虑如何分
的问题,把处理请求(即计算)如何均匀
地分到不一样的机器上,把数据如何分配到不一样的节点上。
从大的方向看分
有两种策略,一种可复刻
,一种不可复刻
。
可复刻
,这种策略根据必定算法分配计算和数据,在相同的条件下,不管什么时间点得出的结果相同,所以对于相同条件的请求和数据来讲是可复刻
的,在不一样时间点相同的请求和数据始终都在统一节点上。这种策略通常用于有数据状态在状况。
不可复刻
,这种策略使用全随机方式,即便在相同的条件下,不一样时间点得出的结果也不一致,所以也是不可还原
的,若是只是为了可还原
,若是经过元数据记录已经分配好的数据,以后须要还原
时经过元数据就能够准确的得知数据所在位置了。
65 哥:这么神奇么?我想看看不一样系统都有什么策略。
Dubbo 是阿里开源的分布式服务框架。其实现了多种负载均衡策略。
随机,能够按权重设置随机几率。在一个截面上碰撞的几率高,但调用量越大分布越均匀,并且按几率使用权重后也比较均匀,有利于动态调整提供者权重。
轮询,按公约后的权重设置轮询比率。存在慢的提供者累积请求的问题,好比:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,长此以往,全部请求都卡在调到第二台上。
最少活跃调用数,相同活跃数的随机,活跃数指调用先后计数差。使慢的提供者收到更少请求,由于越慢的提供者的调用先后计数差会越大。
一致性 Hash,相同参数的请求老是发到同一提供者。 当某一台提供者挂时,本来发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引发剧烈变更。
Kafka 中提供了多重分区分配算法(PartitionAssignor)的实现:
RangeAssignor 策略的原理是按照消费者总数和分区总数进行整除运算来得到一个跨度,而后将分区按照跨度进行平均分配,以保证分区尽量均匀地分配给全部的消费者。对于每个 Topic,RangeAssignor 策略会将消费组内全部订阅这个 Topic 的消费者按照名称的字典序排序,而后为每一个消费者划分固定的分区范围,若是不够平均分配,那么字典序靠前的消费者会被多分配一个分区。
RoundRobinAssignor 的分配策略是将消费组内订阅的全部 Topic 的分区及全部消费者进行排序后尽可能均衡的分配(RangeAssignor 是针对单个 Topic 的分区进行排序分配的)。
从字面意义上看,Sticky 是“粘性的”,能够理解为分配结果是带“粘性的”——每一次分配变动相对上一次分配作最少的变更(上一次的结果是有粘性的),其主要是为了实现如下两个目标:
65 哥:哇,看来优秀的系统都是相通的。
副本是解决分布式集群高可用问题的。在集群系统中,每一个服务器节点都是不可靠的,每一个系统都有宕机的风险,如何在系统中少许节点失效的状况下保证整个系统的可用性是分布式系统的挑战之一。副本就是解决这类问题的方案。副本一样也能够提升并发处理能力,好比数据在不一样的节点上能够读写分离,能够并行读等。
在这里其实也有不少说法,如 Master-Salve、Leader-Follower、Primary-Shard、Leader-Replica 等等。
目前,大部分的主流关系型数据库都提供了主从热备功能,经过配置两台(或多台)数据库的主从关系,能够将一台数据库服务器的数据更新同步到另外一台服务器上。这既能够实现数据库的读写分离,从而改善数据库的负载压力,也能够提升数据高可用,多份数据备份下降了数据丢失的风险。
在 ES 中有主分片和副本分片的概念。副本分片的主要目的就是为了故障转移,若是持有主分片的节点挂掉了,一个副本分片就会晋升为主分片的角色从而对外提供查询服务。
在理论计算机科学中,CAP 定理(CAP theorem),又被称做布鲁尔定理(Brewer's theorem),它指出对于一个分布式计算系统来讲,不可能同时知足分布式系统一致性、可用性和分区容错(即 CAP 中的"C","A"和"P"):
65 哥:什么是一致性、可用性和分区容错性能?
一致性意味着全部客户端同时看到相同的数据,不管它们链接到哪一个节点。要发生这种状况,每当将数据写入一个节点时,必须当即将数据转发或复制到系统中的全部其余节点,而后才能将写入视为"成功"。
任何客户端的请求都能获得响应数据,不会出现响应错误。换句话说,可用性是站在分布式系统的角度,对访问本系统的客户的另外一种承诺:我必定会给您返回数据,不会给你返回错误,但不保证数据最新,强调的是不出错。
分区即分布式系统中的通讯中断,两个节点之间的丢失或暂时延迟的链接。分区容错意味着群集必须继续工做,尽管系统中的节点之间存在的通讯故障。
这三种性质进行俩俩组合,能够获得下面三种状况:
CA 和 CP 系统设计遵循的都是强一致性理论。不一样的是 CA 系统不能容忍节点发生故障。CP 系统可以容忍 2f+1 个节点中有 f 个节点发生失败。
CAP 理论代表,对于一个分布式系统而言,它是没法同时知足 Consistency(强一致性)、Availability(可用性) 和 Partition tolerance(分区容忍性) 这三个条件的,最多只能知足其中两个。
在分布式环境中,咱们会发现必须选择 P(分区容忍)要素,由于网络自己没法作到 100% 可靠,有可能出故障,因此分区是一个必然的现象。也就是说分区容错性是分布式系统的一个最基本要求。
CAP 定理限制了咱们三者没法同时知足,但咱们能够尽可能让 C、A、P 都知足,这就是 BASE 定理。
BASE 理论是 Basically Available(基本可用),Soft State(软状态)和 Eventually Consistent(最终一致性)三个短语的缩写。既是没法作到强一致性(Strong consistency),但每一个应用均可以根据自身的业务特色,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
基本可用是指分布式系统在出现故障的时候,容许损失部分可用性,即保证核心可用。
电商大促时,为了应对访问量激增,部分用户可能会被引导到降级页面,服务层也可能只提供降级服务,这就是损失部分可用性的体现。
什么是软状态呢?相对于原子性而言,要求多个节点的数据副本都是一致的,这是一种“硬状态”。
软状态指的是:容许系统中的数据存在中间状态,并认为该状态不影响系统的总体可用性,即容许系统在多个不一样节点的数据副本存在数据延时。
最终一致性是指系统中的全部数据副本通过必定时间后,最终可以达到一致的状态。
弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊状况。
BASE 理论面向的是大型高可用、可扩展的分布式系统。与传统 ACID 特性相反,不一样于 ACID 的强一致性模型,BASE 提出经过牺牲强一致性来得到可用性,并容许数据段时间内的不一致,可是最终达到一致状态。
分布式是系统扩展的必然方向,分布式系统所遇到的问题是广泛,随着大量优秀的项目在分布式的道路上披荆斩棘,前人已经总结了大量丰富的理论。而且不一样领域的分布式系统也层出不穷,咱们既应该学习好这些好的理论知识,也应该去多看看不一样分布式系统的实现,总结它们的共性,发现它们在不一样领域独特的亮点权衡,更重要的,咱们应该将所学用于平常项目的实践当中,也应该在实践中总结出更多的规律理论。
感谢读者看完本文,码哥
将为读者持续输出高质量的文章,下期咱们继续深刻讲讲分布式一致性的问题和解决方案,敬请关注。