分布式系统

若是如今让你阐述一下什么是“分布式系统”,你脑子里第一下跳出来的是什么?我想,此时能够用苏东坡先生的一句诗,来形象地描述你们对分布式系统的认识:mysql

横当作岭侧成峰,远近高低各不一样。sql

1“分布式系统”等于 SOA、ESB、微服务这些东西吗?数据库

我以为每一个人脑子里一会儿涌现出来的确定是很是具象的东西,就像下面这些:缓存

“分布式系统”等于 SOA、ESB、微服务这些东西吗?架构

若是你一会儿想到的是 XX 中心、XX 服务,意味着你把服务化的模式(SOA、ESB、微服务)和分布式系统错误地划上了等号。并发

那么,什么是“服务化”呢?服务化就像企业当中将相同岗位的人员划分到同一个部门管理,以此来收敛特定的工做入口,再进行二次分配,以提升人员利用率和劳动成果的复用度。服务化的本质是“分治”,而“分治”的前提是先要拆,而后才谈得上如何治。这时,高内聚、低耦合的思想在拆分过程当中起到了一个很是重要的做用,由于这能够尽量地下降拆分后不一样组件间进行协做的复杂度。因此重要的是“怎么拆“,还有如何按部就班地拆,而这个过程当中你到底是采用了何种服务化模式(好比 SOA、ESB、微服务等)并非关键。框架

为何说“怎么拆”最重要呢?我来举个例子,企业的组织架构包括三种模型:职能型、项目型、矩阵型。你能够把这里的企业理解为一个“分布式系统”,把后面的 3 种模型理解为这个分布式系统的 3 种形态。做为这个“系统”的全部人,你须要考虑如何拆分它,才能使得各功能组件相互之间能够更好地协做。假设,你要将一个总计 10000 名员工的企业按“职能型”拆分红 20 个部门,获得的结果是每一个部门 500 人。异步

这时,若是工做是流水线式的上下游关系。一个部门完工了再交给下一个部门。分布式

那么这时候是高内聚、低耦合的。由于一个工种只与另外一个工种产生了关联,而且仅有一次。函数

但若是工做须要频繁的由不一样职能的人员同时进行,会致使同一个部门可能与多个部门产生联系。

那么,这时是低内聚、高耦合的。由于一个工种须要和其余多个工种产生关联而且远不止一次。

能够看到服务化体现了“分治”的效果,这也是分布式系统的核心思想,所以从“分治”这个本质上来看,服务化的确是分布式系统,但分布式系统不只仅停留在那些服务化的模式上。

我相信,你在工做中参与开发的任何软件系统,处处都存在着须要拆分的地方,除非它的功能极简到只须要计算一个 1+1。好比,当咱们在电商平台点击“提交订单”的时候,会涉及生成订单、扣除积分、扣除库存等等动做。电商系统初期全部的功能可能都在一个系统里面,那么这些操做能够写在一个方法体里吗?我想只要代码可以成功运行,大部分人是不会管你怎么写的。可是若是这时须要增长一个红包功能呢?相信你或多或少遇到过在几百上千行代码中去增改功能的事情,其中的痛苦应该深有体会。

要解决这个问题就是要作拆分,经过梳理、归类,将不一样的紧密相关的部分收敛到一个独立的逻辑体中,这个逻辑体能够是函数、类以及命名空间,等等。因此,从这个角度来讲“分治”的问题其实早就存在咱们的工做中,就看咱们是否有去关注它了。所以,这并不仅是咱们在进行服务化时才须要考虑的问题。

那么如何才能作好这个事情,更好的拆分能力正是咱们须要掌握的。若是只是由于看到其余人这么拆,我也这么拆,根据“二八原则”,或许“依样画葫芦”能够达到 80% 的契合度,可是每每那剩下的 20% 会是耗费咱们 80% 精力的“大麻烦”。要知道,只有掌握了核心主旨,才能更快地找到最理想的高内聚、低耦合方案

2“分布式系统”是各类中间件吗?

又或许,听到分布式系统,你想到了某某 MQ 框架、某某 RPC 框架、某某 DAL 框架,把运用中间件和分布式系统错误地划上了等号。

这里须要搞清楚的是,中间件起到的是标准化的做用。中间件只是承载这些标准化想法的介质、工具,能够起到引导和约束的效果,以此起到大大下降系统复杂度和协做成本的做用。咱们来分别看一下:

  • MQ 框架标准化了不一样应用程序间非实时异步通讯的方式。

  • RPC 框架标准化了不一样应用程序间实时通信的方式。

  • DAL(Data Access Layer,数据访问层)框架标准化了应用程序和数据库之间通信的方式。

因此,虽然分布式系统中会运用中间件,但分布式系统却不只仅停留在用了什么中间件上。你须要清楚每一类中间件背后是对什么进行了标准化,它的目的是什么,带来了哪些反作用,等等。只有如此,你才能真正识别不一样技术框架之间的区别,找到真正适合当前系统的技术框架。

那么标准是拍脑壳决定的吗?确定不是,正如前面所说每一次标准化都是有目的的,须要产生价值。好比,大部分中间件都具有这样一个价值:

为了在软件系统的迭代过程当中,避免将精力过多地花费在某个子功能下众多差别不大的选项中。

在现实中,这点更多时候出如今技术层面的中间件里,好比,数据库访问框架的做用是为了标准化操做不一样数据库的差别,使得上层应用程序不用纠结于该怎么与 mysql 交互或者该怎么与 SQL SERVER 交互。由于与业务相比,技术层面“稳定”多了,因此作标准化更有价值,更能得到长期收益。但“稳定”是相对的,哪怕单纯在业务层面也存在相对稳定的部分。

好比,你能够想象一下“盛饭”的场景,在大多数状况下其中相对稳定的是什么,不稳定的是什么。想完以后看下面的示例。

...
基类:人 
继承基类的子类:男人、女人

基类:碗 
继承基类的子类:大碗、小碗、汤碗

基类:勺子 
继承基类的子类:铁勺、陶瓷勺、塑料勺

function 盛饭(参数 人,参数 碗,参数 勺子){
    do 人拿起碗
    do 人拿起勺子
    do 人用勺子舀起饭
    do 人把勺子放到碗的上方并倒下

} 
...

从这个示例里咱们发现,不稳定的部分都已经成为变量了,那么剩下的这个方法体起到的做用和前面提到的中间件是同样的,它标准化,标准化了盛饭的过程。因此识别相对稳定的部分是什么,如何把它们提炼出来,而且围绕这些点进行标准化,才是咱们须要掌握的能力。而锻炼这个能力和须要这个能力的地方一样并不局限于分布式系统。

列举这些现象只是想说,咱们在认知一个分布式系统的时候,内在胜于表象,掌握一个扎实的理论基本功更为重要。并且,这些训练场无处不在。

3海市蜃楼般的“分布式系统”

我相信,自从进入移动时代以来,各类高大上的系统架构图愈来愈频繁地出现,你的眼前充斥着各类主流、非主流的眼花缭乱的技术框架。你不禁得肃然起敬一番,心中呐喊着:“对,这就是我想去的地方,我想参与甚至实现一个这样牛逼的分布式系统,不再想天天只是增删改查了。”

得不到的事物老是美好的,但每每咱们也会过分地高估它的美好。与此相似,高大上的架构图背后呈现的系统的确也是一个成熟分布式系统的样貌,但咱们要清楚一点:罗马不是一日建成的。

并且,“分布式”这个词只是意味着形态上是散列状的,而“一分为二”和“一分为 N”本质上并无区别。因此,不少小项目或者大型项目的初期所搭配的基础套餐“单程序 + 单数据库”,一样能够理解为分布式系统,其中遇到的问题不少一样也存在于成熟的分布式系统中。

想象一下,下面的场景是否在“单程序 + 单数据库”项目中出现过?

  • log 记录执行成功,可是数据库的数据没发生变化;

  • 进程内的缓存数据更新了,可是数据库更新失败了。

这里咱们停顿 30 秒,思考一下为何会出现这些问题?

这里须要咱们先思考一下“软件”是什么。 软件的本质是一套代码,而代码只是一段文字,除了提供文字所表述的信息以外,自己没法“动”起来。可是,想让它“动”起来,使其可以完成一件咱们指定的事情,前提是须要一个宿主来给予它生命。这个宿主就是计算机,它可让代码变成一连串可执行的“动做”,而后经过数据这个“燃料”的触发,“动”起来。这个持续的活动过程,又被描述为一个运行中的“进程”。

那么除了咱们开发的系统是软件,数据库也是软件,前者负责运算,后者负责存储运算后的结果(也可称为“状态”),分工协做。

因此,“单程序 + 单数据库”为何也是分布式系统这个问题就很明白了。由于咱们所编写的程序运行时所在的进程,和程序中使用到的数据库所在的进程,并非同一个。也所以致使了,让这两个进程(系统)完成各自的部分,然后最终完成一件完整的事,变得再也不像由单个个体独自完成这件事那么简单。这就如“两人三足”游戏同样,如何尽量地让外部看起来像是一个总体、天然地前进。

因此,咱们能够这么理解,涉及多个进程协做才能提供一个完整功能的系统就是“分布式系统”。

那么再回到上面举例的两个场景,咱们在思考“单程序 + 单数据库”项目中遇到的这些问题背后的缘由和解决它的过程时,与咱们在一个成熟的分布式系统中的遭遇是同样的,例如数据一致性。固然,这只是分布式系统核心概念的冰山一角。

维基百科对“分布式系统”的宏观定义是这样的:

分布式系统是一种其组件位于不一样的联网计算机上的系统,而后经过互相传递消息来进行通讯和协调。为了达到共同的目标,这些组件会相互做用。

咱们能够再以大小关系来解释它:把须要进行大量计算的工程数据分割成小块,由多台计算机分别计算,而后将结果统一合并得出数据结论的科学。这本质上就是“分治”。而“单程序 + 单数据库”组合的系统也包含了至少两个进程,“麻雀虽小五脏俱全”,这也是“分布式系统”。

4总结

如今,咱们搞清楚了,看待一个“分布式系统”的时候,内在胜于表象。以及,只要涉及多个进程协做才能提供一个完整功能的系统,就是“分布式系统”

我相信还有不少其余景象出现你的脑海中,但这大多数都是分布式系统的本质产生的“化学反应”,进而造成的结果。若是停留在这些表象上,那么咱们最终将没法寻找到“分布式系统”的本质,也就没法获得真正的“道”,更不会真正具有驾驭这些形态万千的“分布式系统”的能力。

因此,但愿你在学习分布式系统的时候,不要因追逐“术”而丢了“道”。没有“道”只有“术”是空壳,最终会走火入魔,学得越多,会越混乱,处处都是矛盾和疑惑。

所以,咱们这个系列除了教给你在具体场景下的最佳实践,还会和你讲解为何这样作,以及该如何去权衡不一样方案。不会过多的讲述具体的技术框架,大部份内容围绕理论展开,欲使每一个人可以掌握好这些分布式中的基础理论和思路,修炼好本身的内功。

我将在后续的文章中,以一个项目的初期到成熟期做为路线图,带领你按部就班地深刻到分布式系统中,层层递进地去剥开它的本质,而且围绕这个本质去思考(是什么问题,有哪些方式能够解决,何时该用何种种方式等等),让你知其然且知其因此然,造成一套完整的知识体系,完成核心“骨架”的塑造。而在此以后,你本身在课外学习时,就能够去填充“血肉”部分,逐渐丰满本身。将来,你们的区别就在于胖一点和瘦一点,但只要能很好地完成工做,胖瘦又有何影响?

 

站在全局角度看,分布式系统的本质是什么?其实说白了,就是两点:“分治”和“冗余”。分治和冗余使得分布式系统具有了核心价值,那么它的价值是什么?

上一篇中,咱们从整体上聊了聊分布式系统:多是讲分布式系统最到位的一篇文章。这一篇中,咱们将聊聊分布式系统的本质。

分布式系统的价值

谈到分布式系统的价值,可能就得从 1953 年提及了。在这一年,埃布·格罗希(Herb Grosch)提出了一个他观察得出的规律——Grosch 定律。维基百科中是这样描述的:

计算机性能随着成本的平方而增长。若是计算机 A 的成本是计算机 B 的两倍,那么计算机 A 的速度应该是计算机 B 的四倍。

这一论断与当时的大型机技术很是吻合,于是使得许多机构都尽其所能购买最大的单个大型机。其实,这也很是符合惯性思惟,简单粗暴。

然而,1965 年高登·摩尔(Gordon Moore)提出了摩尔定律。通过几年的发展,人们发现摩尔定律的预测是符合现实的。这就意味着,集中式系统的运算能力每隔一段时间才能提高一倍。

那么,到底要隔多久呢?这个“时间”有不少版本,好比广为流传的 18 个月版本,以及 Gordon Moore 本人坚持的 2 年版本。这里咱们不用太过纠结于实际状况究竟是哪一个“时间”版本,由于这其中隐含的意思更重要,即:若是你的系统需承载的计算量的增加速度大于摩尔定律的预测,那么在将来的某一个时间点,集中式系统将没法承载你所需的计算量

而这只是一个内在因素,真正推进分布式系统发展的催化剂是“经济”因素。

人们发现,用廉价机器的集合组成的分布式系统,除了能够得到超过 CPU 发展速度的性能外,花费更低,具备更好的性价比,而且还能够根据须要增长或者减小所需机器的数量。

因此,咱们获得一个新结论:不管是要以低价格得到普通的性能,仍是要以较高的价格得到极高的性能,分布式系统都可以知足。而且受规模效应的影响,系统越大,性价比带来的收益越高

以后,进入到互联网快速发展的时期,咱们看到了分布式系统相比集中式系统的另外一个更明显的优点:更高的可用性。例如,有 10 个可以承载 10000 流量的相同的节点,若是其中的 2 个挂了,只要实际流量不超过 8000,系统依然可以正常运转。

而这一切的价值,都是创建在分布式系统的“分治”和“冗余”之上的。

分   治

分治,字面意思是“分而治之”,和咱们的大脑在解决问题时的思考方式是同样的。咱们能够将整个过程分为 3 步:分解 -> 治理 -> 归并。而分治思想的表现形式多样,分层、分块都是它的体现。

这么作的好处是:问题越小越容易被解决,而且,只要解决了全部子问题,父问题就均可以被解决了。可是,这么作的时候,须要知足一个最重要的条件:不一样分支上的子问题,不能相互依赖,须要各自独立。由于一旦包含了依赖关系,子问题和父问题之间就失去了能够被“归并”的意义。在软件开发领域,咱们把这个概念称为“耦合度”和“内聚度”,这两个度量概念很是重要。

耦合度,指的是软件模块之间相互依赖的程度。好比,每次调用方法 A 以后都须要同步调用方法 B,那么此时方法 A 和 B 间的耦合度是高的。

内聚度,指的是模块内的元素具备的共同点的类似程度。好比,一个类中的多个方法有不少的共同之处,都是作支付相关的处理,那么这个类的内聚度是高的。

内聚度一般与耦合度造成对比。低耦合一般与高内聚相关,反之亦然。

因此,当你打算进行分治的时候,耦合度和内聚度就是须要考虑的重点。

下面咱们来看个例子,体会一下耦合度和内聚度的含义。(图仅用于表达含义,切勿做其余参考)

假设一个电商平台,为了应对更大的访问量,须要拆分一个同时包含商品、促销的系统。若是垂直拆分,是这样:

而若是水平拆分,则是这样的:

假如咱们面对的场景仅仅是具体的商品详情展现页面,很显然,用水平拆分的效果会更好。由于传统的商品展现必然会同时展现促销,因此,若是用水平拆分,一次请求便可获取全部数据,内聚度很是高,而且此时模块间彻底没有耦合。而若是是垂直拆分的话,就须要同时请求 2 个节点的数据并进行组合,所以耦合度更高、内聚度更差。

可是,这样的假设在真实的电商场景中是不存在的。从全局来看,订单、购物车、商品列表等许多其余场景也须要促销信息。而且这个时候咱们发现引入了一些新的主体,诸如订单、购物车、商品分类等等。这个时候,水平拆分带来的好处愈来愈小,由于这样只解决了多个耦合中的一个,低耦合丧失了。而且随着商品和促销与外界的关联愈来愈多,必然有些场景仅仅涉及到商品和促销的其中一个,可是处理的时候,咱们还须要避免受到另外一个的影响。如此,高内聚也丧失了。

这个时候,反而经过垂直拆分能够得到更优的耦合度和内聚度,以下图。

这个时候,最高的耦合关系从原先的 6 降到了 4,而且商品和促销各自的处理相互不受影响。

因此,你会发现随着业务的变化,耦合度与内聚度也会发生变化。所以,及时地进行梳理和调整,能够避免系统的复杂度快速增加,才能最大程度的发挥“分治”带来的好处。

综上,分治能够简化解题的难度,经过高内聚、低耦合的协做关系达到更好“性能与经济比”,来承载更大的流量。而“冗余”则带来了系统能够 7*24 小时不间断运做的但愿。

冗   余

这里的冗余并不等同于代码的冗余、无心义的重复劳动,而是咱们有意去作的、人为增长的重复部分。其目的是允许在必定范围内出现故障,而系统不受影响,以下图。

此时,咱们能够将冗余的节点部署在一个独立的环境中。这个独立的环境,多是处于同一个局域网内的不一样主机,也多是在不一样的局域网,还多是在不一样的机房。很显然,它们可以应对的故障范围是逐步递增的。

可是,像这种单纯地为了备用而作的冗余,最大的弊端是,若是没有出现故障,那么冗余的这部分资源就白白浪费了,不能发挥任何做用。因此,咱们才提出了诸如双主多活、读写分离之类的概念,以提升资源利用率。

固然,除了软件层面,硬件层面的冗余也是一样的道理。好比,磁盘阵列能够容忍几块以内磁盘损坏,而不会影响总体。

不过也很显然,当故障影响范围大于你冗余的容量时,系统依然会挂。因此,既然你没法预知故障的发生状况,那么作冗余的时候须要平衡的另外一端就是成本。相比更多的冗余,追求更好的性价比更合理一些。

在咱们生活中的冗余也处处存在。好比,大部分的飞机和直升机的发动机都是偶数的,汽车中的电子控制系统的冗余机制等。就比如替身与真身的关系,冗余的就是替身。它能够和真身同时活动,也能够代替真身活动。

分治和冗余讲究的都是分散化,最终造成一个完整的系统还须要将它们“链接”起来。天下没有免费的午饭,得到分布式系统价值的同时,这个“再链接”的过程就是咱们相比集中式系统要作的额外工做。

再链接

如何将拆分后的各个节点再次链接起来,从模式上来讲,主要是去中心化与中心化之分。

前者彻底消除了中心节点故障带来的全盘出错的风险,却带来了更高的节点间协做成本。后者经过中心节点的集中式管理大大下降了协做成本,可是一旦中心节点故障则全盘出错。

另外,从技术角度来讲,如何选择通讯协议和序列化机制,也是很是重要的。

虽然不少通信协议和序列化机制彻底能够承担任何场景的链接责任,可是不一样的协议和序列化机制在适合的场景下才能发挥它最大的优点。好比,须要更高性能的场景运用 TCP 协议优于 HTTP 协议;须要更高吞吐量的场景运用 UDP 协议优于 TCP 协议,等等。

总   结

无论系统的规模发展到多大,合理地拆分,加上合适的链接方式,那么至少会是一个运转顺畅、协做舒服的系统,至少可以正常发挥分布式系统应有的价值。

现在,咱们发现分布式系统还能够发挥更多的做用。

好比,只要基于一个统一的上层通讯协议,其下层的不一样节点能够运用不一样的技术栈来发挥不一样技术各自的优点,好比用 Go 来应对高并发场景,用 Python 来作数据分析等。

再好比,提升交付的速度,以下图。

经过分配不一样的团队、人员同时进行多个模块的开发,虽然总的耗时增长了,可是总体的交付速度加快了。

事物最本质的东西是恒定的、不变的,能够指引咱们的工做方向。分布式系统的本质也是这样。例如,这样的“分治”方案耦合度和内聚度是否最优,这样作“冗余”带来的收益是否成本可以接受。只要持续带着这些思考,咱们就好像拿着一杆秤,基于它,咱们就能够去衡量各类变量影响,而后做权衡。好比成本、时间、人员、性能、易维护等等。也能够基于它去判断什么样的框架、组件、协议更适合当前的环境。

须要不断的权衡,也意味着分布式系统的设计工做必定不是一步到位,而是按部就班的。由于过度为未知的将来作更多的考量,最终可能都会打水漂。因此,建议以多考虑 1~2 步为宜。假如以你所在的团队中对重大技术升级的频率来做为参考的话,作可供 2 个升级周期的设计,花一个升级周期的时间先实现第一阶段,下个阶段能够选择直接实现剩下部分,也可继续进行 2 个升级周期设计,开启一个循环,持续迭代,而且不断修正方向以更贴近现实的发展,就以下图这样。

在你的工做或者学习中,以为分布式系统还具有哪些价值呢?能够在下方评论区留言。