30

我是和耳朵,好久没有写文了,今天和大家聊聊分布式

前段时间一直没有写文是因为忙于面试~,没错😂离职了换了一家公司工作,新公司的系统都是分布式的,入职一个月且写了点东西上线之后我才敢于写写这个分布式主题,不然怕自己没有生产经验误人子弟了。

今天呢,主要是想和大家聊一聊分布式系统的相关概念及其常见分布式组件和设计思想(不涉及计算机科学中分布式系统的技术理论之类的东西),之前为了准备这次的面试我是把市面上的很多分布式组件都看了一遍,我们公司所用的分布式组件基本也没出我了解的那个知识圈(公司用了Apollo我没提前了解,大E了),如果对分布式相关技术栈不太了解,也可以当我这篇征文做你的扫盲贴,不过别忘了点赞👍

单体应用与集群

单体应用、集群和微服务,这个标题一出你们可能就知道我想说啥了,emm,就是架构的演进过程,很多人可能都看过相关知识,不过我为了文章的完整性还是打算简单讲一讲。

首先是单体应用,在一个业务的起步阶段,往往是用户量不大且访问请求少的阶段,这个时候我们一般只需要部署一个Web应用和一台数据库就能满足我们的业务需求,且随着SpringBoot的流行,Web服务器可以内置在应用里面,能够更加方便快捷的部署应用。

同时因为项目规模小,业务流程简单,维护和迭代起来也很方便,所以在当前这个阶段单体应用是非常适合的。

但是随着业务增长,单体应用不可避免的会出现瓶颈,我举个例子:

假如我们现在的单体Tomcat应用只能支撑QPS200,随着用户量的增大并发随之增大,慢慢超出了单台应用能承受的极限,假如现在达到了QPS300,那么多出来的100请求就只能等着前面的请求处理完了之后才能请求进去,这样带来的后果就是响应时间变长,影响用户体验。

或者我们应用中的业务比较复杂,单次请求响应时间比较长,这样的话大量请求挤压也会导致用户的体验很差,他们能明显的感觉到点击某个按钮之后隔了1~2s才有结果返回。

这个时候我们就可以引入集群架构了,我们可以将单体应用同时部署两份,并通过Nginx进行反向代理和负载均衡进行流量分流,这样每个单体应用承受的QPS就是150了,就在可以接受的范围内,而且维护集群应用和维护单机应用区别不大,只是在涉及到锁的时候可能要借助分布式锁来做。

至此,每当访问量增大系统到达瓶颈时我们就可以通过加机器这种方式进行横向扩容,不断的扩大应用的负载能力,但是如果这种方式完美无缺,我们也就不需要微服务的架构了~~~

从单体应用过渡到集群架构,解决的其实是性能的问题,单台机器已经无法满足人民日益增长的访问需求,所以出现了集群架构。

那么从集群过渡到微服务架构的更多原因肯定就不在性能了,这里我说说我自己的看法与感悟:

  1. 首先既然项目已经是集群了证明业务量也不会非常小,这也就代表了代码一定有一些规模了,这时候要面临的第一个问题就是所有开发人员都会在这一个项目里面修改代码,极大情况下是冲突不断。
  2. 其次这么多代码聚合在一块,当你进行一处功能修改的时候,如果需要进行全量回归测试的话那简直太要命了。
  3. 再者说,所有业务块都在一个系统这会导致资源无法最大化利用,比如一个电商系统肯定是商品搜索/推荐系统为最常用的系统,理所当然在资源方面他们应该占有更多的机器资源,但是业务不进行拆分想进行横向扩展只能将所有业务一起扩展。
  4. 最后是有可能会引起雪崩效应,举个例子你的系统中假如有一块非常不重要的业务(比如签到)代码写的有问题在生产环境中引发了OOM,那么它必然会连累到整个系统中的所有业务都变成不可用,因为不同业务之间并没有物理隔离。

通过这几条可以知道过渡到微服务更多的考虑的已经不是性能瓶颈,而是人,是业务,也是应用系统最重要的高可用。

微服务与分布式

微服务是一种面向服务的软件架构模式,自2014年Martin FowlerJames Lewis写了一篇微服务架构的文章之后(微服务这个概念在此之前就有),微服务就被大量的讨论以及实践到生产项目中:Netflix、Amazon这些商业公司都有微服务的成功案例,商业公司最会考虑的一件事就是成本,他们不会因为Martin Fowler是软件工程的名人(巨佬)就对他提出的某个概念迅速披挂上马,一定是经过了很多权衡之后,才对他们的项目使用微服务的架构模式。

上一节中我已经简要说了说集群架构的缺点,微服务则是解决了那些缺点才变得如此受欢迎,微服务的相关定义,这里我引用一段Martin Fowler原文的配图和翻译来说明:

简而言之,微服务架构风格,就像是把一个单独的应用程序开发为一套小服务,每个小服务运行在自己的进程中,并使用轻量级机制通信,通常是 HTTP API。

这些服务围绕业务能力来构建,并通过完全自动化部署机制来独立部署。这些服务使用不同的编程语言书写,以及不同数据存储技术,并保持最低限度的集中式管理。

如果非要我用自己的语言来理解一下就是:将一个大型系统分为一个个的小型服务系统,共同支撑大型系统,每个小服务系统都可以独立开发/测试/迭代/部署/扩容。

将大系统拆分为小服务之后既拥有了小服务的相关优点(利于团队协作/测试/迭代),又能在面对大流量时进行集群扩容,可以说是集两者优点于一身,可能这就是所谓的”天下大势,合久必分,分久必合。”

我新入职的这家公司也是在今年初全面转入微服务架构,我们部门中所有的业务都已经拆成了微服务在跑,微服务拆分这事算是见仁见智把,要根据具体业务具体分析,我们的拆分过程中感觉比较好的一点是:有一些在未来一两年内将要停止运营的业务也单独拆出来放在一个服务里跑。

其实微服务的概念不难理解,但真正动手起来做的话遇到的则一般不是微服务的问题而是分布式问题,有很多人经常把这两个概念搞混淆,认为他俩说的是一个东西,其实是两个东西,我们做出来的东西往往是涵盖了这两个概念。

微服务是指的一种面向服务的软件架构方式,而分布式则是一种为了某个共同目标而协调多台计算机节点进行工作的软件系统。

在上一节的讲解中,有一句话一直出现:随着业务量/访问量增大,这个日益增长的访问量/业务量/数据量是我们使用微服务和分布式系统的主要原因:

  1. 业务的增加我们通过业务拆分来解决其带来的问题,这可以算是微服务的范畴。
  2. 不同的服务部署在不同的机器上,但是对于用户来说却和访问一个系统没什么区别,依靠网络将多个计算机节点组成一个统一的整体,这可以算是分布式系统的范畴。
  3. 由于数据量的不断增加我们可以通过增加机器节点来分摊数据量,这算是分布式系统中分布式存储的范畴。比如我们的Redis数据一台机器已经存不下了,就要考虑使用RedisCluster将数据分散到多台机器上面,其他例如Kafaka、ES都是分布式架构的中间件都可以进行分布式存储。
  4. 如果应用中某些计算量比较大的任务使用一台机器执行会耗时过长,这时我们可以拆分任务给多台节点同时执行,这算是分布式系统中分布式计算的范畴。最近我在接手项目的时候就遇到了类似的问题,原任务单机执行时间过久,让我改为所有在线节点同时执行,共同分摊任务,并且要能动态根据在线节点数量进行任务拆分。

通过上面四点的说明,我想大家对微服务和分布式应该有个简单的区分了把,再次总结一下:

微服务是指的一种面向服务的软件架构方式,而分布式则是一种为了某个共同目标而协调多台计算机节点进行工作的软件系统。

那为什么微服务和分布式系统老放在一块讨论,导致很多人对他俩的定义模糊不清呢,因为往往我们要实现一个微服务架构的应用时,我们就在实现一个分布式系统。

如果你不明白我这句话,请好好想想使用微服务架构组成的应用是不是也同时符合分布式的定义。

Tips: 分布式计算/存储也是一门计算机科学中的研究方向,所以它们其实还是可以挺深奥的一堆东西。

分布式与CAP

看了上一节大家应该已经明白了微服务和分布式的关系了吧,我这篇文章的重点其实是分布式,因为微服务只是一个面向服务的软件架构概念,让我理解起来它的主要作用就是提出了服务拆分、独立开发部署这些概念,emm~概念,但是一个分布式系统却有很多各种各样的实际问题需要解决,所以我的重点是在分布式。

先来说说分布式的CAP原则,但凡对分布式有点了解的,都不能不知道这个CAP,先来看看它的定义:

CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。

CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。

分区容错性: 是指在分布式系统中部署在不同地点的机器可能出现网络连接失败的情况,这就像我的推荐服务会请求商品服务里面的数据,但有可能发生了网络波动导致我推荐服务发起的请求网络连接超时。

可用性: 是指用户的请求必须返回结果,要做到这个程度就需要我们在不同机房部署应用避免出现某些地方机房遭遇了事故导致服务宕机的情况。

一致性: 是指数据被修改后,之后读到的数据一定是最新的数据。

上面的定义已经说了,CAP只能最多同时实现两点,因为我们是分布式系统,所以多台服务节点是无法避免的,也就是分区容错性我们必须要保证,所以我们只能保证实现CP或者AP。

为什么不能同时实现CAP呢?原因很简单,因为可用性这个要求需要每个节点的数据都要有几个冗余的副本,用来保证有一个节点挂掉之后副本能顶上去。然而副本节点和主机节点之间因为有网络通信所以往往这个数据的传输是有延迟的,这也就不能保证主机的数据被修改后副本能立即收到修改,而是经过一顿延迟后才能收到主机修改的数据。

为了方便大家理解,我再举一个例子来说明一下,比如说分布式中间件Redis

它在单机的情况下可以保证CP,因为只有一台Redis节点所以数据被修改后之后的请求所访问到的数据都是最新的。

它在集群的情况下可以保证AP,AP是强调可用性,集群架构下如果主节点挂掉之后,副本节点还可以接着响应请求。


既然CAP无法同时保证,那我们就要退而求其次,这里将会引出一个新的理论:BASE

BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)的简写。

BASE是对CAP中一致性和可用性权衡的结果,契合性思想是即使无法做到强一致性,但每个应用都可以根据自身的业务特点,采用适当的方式来使得系统达到最终一致性。

基本可用: 是指某些情况下允许部分可用性的丢失,比如我们的双十一大促,可能会由于下单量激增导致你下不了单,这就属于下单服务不可用了,但是并不会持久太久,而是短暂的。

软状态: 是指允许副本同步过程中出现延迟而导致副本不一致的情况。

最终一致性: 是指系统的所有系统数据副本可以经过一段延迟后最终达到数据的一致性,不需要保证实时的数据一致。

我们既想要可用性也想要一致性,然而二者无法兼得,所以BASE理论就提出了这样一种兼顾的思想,代价是强可用和强一致的损失。

现实中的很多系统中都是BASE理论这种思想,达到系统的一个基本可用和最终一致。

分布式开发与组件

这节我们来聊聊我们分布式系统的常见的各种组件以及相关特性,大家接触过分布式开发的话就会发现分布式开发中有一大箩筐的名词等着你:注册中心、配置中心、网关、熔断、远程调用等等。

初来乍到,不要被这些名词吓住,别人能会的你也能,这一节我会自顶向下的讲解一下分布式开发的基本组成,你可以先忘记这些名词,慢慢看我的讲解,看完这一章后我相信对这些都不会再迷茫。

前面我已经说过,分布式系统是多个节点组成一个整体,那不同节点的IP肯定不同,想要感觉像是一个整体,就得有一个部件在所有节点的最前面承担一个请求分发的作用,这个东西就叫:网关

一般的网关有两层,第一层是服务器的Nginx+LVS这种,第二层是分布式应用的网关,我这里说的是分布式应用的网关。

这个网关的作用有点像Nginx,可以帮你做反向代理帮你做负载均衡,但是比它更强大,分布式应用的所有请求第一站就是应用的网关层,在这里可以校验请求是否合法,阻挡网络攻击,也可以进行动态的请求分发。

请求被网关转发到对应的服务之后,就由对应的服务来处理了,这个时候问题又来了,我们同一个服务可能部署了三四台节点,这个时候我该往哪个服务器转发呢?

我们要想转发首先要知道各个节点的IP和端口号,这个信息我们不能写死,写死的话不利于动态加机器,所以这个时候我们需要用到一个中间价:注册中心

我们所有的服务都会注册到注册中心上面去,当然这个注册中心也要是一个集群,这样可以保证高可用。

举个例子:我有一个商品服务给它起了个名字叫做goods-service,我给他起了三台服务器,这三台服务器都会注册到注册中心上去,然后我们就可以在注册中心上看到有一个叫goods-service的东西下面又三台节点,每台节点的IP端口号都会在注册中心上面保存。

这时候我们网关来转发的时候是通过goods-service这个名称去注册中心拿到三台节点的地址,然后根据不同的策略最终决定我们是要将请求发到哪一台节点。

请求发送到服某某服务的这个过程,我们可以称作为服务通信,这个通信方式一般有两种:HTTP和RPC,这个其实我觉得没啥好说的,主要就是通信的协议不一样,协议的不同也造成了效率的不同。

上面说了三个比较重要的组件,还有两个比较重要的听我娓娓道来。

第一个是配置中心,这个其实是属于用不用都行的东西,但是用起来还是更方便的,它的主要作用是将分布式应用的配置都放在一个地方管理,我们需要更改的时候就可以让所有引用这些配置的应用感知到并在运行的过程中动态加载到这些被修改的配置。

比如我们有六个微服务应用,我们可以统一的把配置都放在配置中心中间件上,这个数据一般是放在配置中心所连的数据库里,所以等于把我们所有项目的配置抽出来都放在数据库了,一旦线上运行有什么需要动态修改的配置,我们都可以直接在中间件上修改然后所有应用都会收到修改的通知,就会对修改的配置进行重新加载。

它对于某些公共的配置也很方便,比如六个微服务用的是一个Redis集群,我们只需要配置一份就够了,而且集群配置有变化我们也只需要修改一次,六个微服务应用都能感知到。

第二个是熔断器,所谓熔断器,就是为了保护应用而设计的中间件,比如淘宝双十一下单服务器顶不住的时候就会给你提示一个哎呀,服务器太火爆了,请稍后再试呢” 这种提示,这个东西就是为了保护淘宝服务不被冲垮而使用的一种限流措施,这种措施叫做服务降级。

使用熔断器还有一些场景,比如下单经历了商品服务和下单服务和库存服务,走到最后一步时库存服务崩掉了,这个时候下单服务这里会报错,进而引起羊群效应,所有和崩掉的节点有通信的节点都会发生错误,这个时候就需要熔断器出场了,发现那个服务不会响应之后会给一个提示,防止羊群效应的产生。

这两个概念表现方式很像,但是不要被表象迷惑,他俩很主要的一个区别是引起的内因不一样,大家有机会写一次相关的代码就能明白了。

以上介绍的这些都是分布式开发中比较基础的中间件,还有其他可选的中间件链路追踪我会在下一节提一下,在这就不多说了。

SpringCloud与Alibaba

上一节大概讲了五个分布式相关的组件,一般也就这五种:微服务网关、注册中心、配置中心、服务通信组件、熔断组件。

这一节要给大家说说相关的实现,给大家认认脸熟,因为我们搞Java应用开发的一般都是用Spring全家桶的,在微服务开发这块Spring推出了它的SpringCloud全家桶用来做微服务分布式开发。

经过这几年的发展其实大致可以分为两套开发套件:SpringCloud开发套件和SpringCloudAlibaba开发套件,这个只是我个人的分法,还有一些其他家的我也会提一下。

我们先来看看SpringCloud这一套,这一套相关的组件一开始基本都是Netflix公司开发的,这是一个做流媒体应用的,可以理解为国外的爱奇艺。

这张导图右边的是三个注册中心中间件,Eureka就是Netflix公司开发的,Zookeeper这个功能比较强不是简简单单只做注册中心,它还可以做注册中心和分布式锁,Consul则是一个Go语言开发的注册中心中间件(也可以用作配置中心),这几个里面Eureka是这两年用的比较多的,更早之前国内可能Zookeeper用的更多。

左边第一个SpringCloudGateway则是网关中间件,由Spring开发,之前Netflix还有一个Zuul,也是用来做网关中间件的。

Feign/OpenFeign/Rabbon这三个我放在一块说,Feign和OpenFeign其实是一个东西,OpenFeign是在Feign停更之后在其基础上开发的,都是用来做服务间通信的,他们使用HTTP做通信协议,其实就是Spring中的RestTemplate,而Rabbon是一个做服务转发策略的中间件,前文我们提到网关进行服务转发时可以根据一定的策略比如:轮询、随机、权重这些策略,这块其实就是Rabbon在起效,我们用Eureka的时候现在都是自带Rabbon不需要额外去引入相关JAR包了,毕竟都是Netflix一家的东西。

SpringCloudConfig是Spring开发的配置中心中间件,功能比起其他的后起之秀比如携程的Apollo,阿里的Nacos稍弱,所以不推荐用。

Hystrix也是Netflix公司开发的中间件,它的作用就是熔断器,是一款非常经典的熔断器中间件,这些中间件里面它是相对来说最复杂的。

Sleuth是一款链路追踪中间件,可以记录你的请求会经过哪些链路,Zipkin则可以以可视化的形式将其表现出来。

上面这一套东西在我现在的公司里面除了配置中心用的Apollo之外其他都用到了,算是比较流行的套件了。


不同于上面的基本都是由Netflix开发的套件,下面我要介绍的套件是SpringCloudAlibaba套件,因为是由Alibaba开发的所以一般都这样称呼。

上面这幅图相比上一张就要简单的多了。

Nacos是一个多功能中间件,同时可以做注册中心和配置中心,这个组件我自己也用过感觉还是蛮好用的,而且自带一套比较美观的可视化界面。

Duubo这个可能大家比较熟悉,原来是由阿里开发后来捐给Apache,现在是Apache的顶级项目之一,它是一个服务通信的组件,使用RPC通信方式。

Sentinel阿里开发的熔断器,名字翻译过来就是哨兵可以说是很贴切了,并且拥有链路追踪的功能,还配有配套的可视化界面,可以说是一个人干了Hystrix+Sleuth+Zipkin的活。


像阿里这套中间件我是比较推荐的,因为第一套Netflix公司基本都已经宣布停止更新了,比如Feign不更新之后Spring又接手进行二次开发搞了一个OpenFeign。

这里在提一下Apollo,携程开发的配置中心中间件,现在也有很多公司在用,我们公司也在用个人用起来感觉还是可以的,很方便上手也简单。

其实别看这里罗列了这么多的中间件,每中学一种,剩下的看看文档也就上手了,大同小异吧,开源界的东西就是喜欢弄的简单易上手,这对大家来说也是减少学习成本。

框架学的越多用的越多,你越会发现其实大部分都是大同小异,越能明白学好Java基础的作用,越能知道设计模式、算法才是一个软件工程师必备的技能。

下个月我应该会把这些中间件也逐一写文章写个入门够用的程度吧,大家一起加油🎉。

我是和耳朵,欢迎关注我的公众号,加入后端相关交流群:


和耳朵
763 声望5.4k 粉丝