分布式架构-聊个大概

miloyang
0 评论
/ /
729 阅读
/
12284 字
05 2023-12

单体架构、集群架构、分布式架构、微服务架构,分别是什么?又有什么区别?
今天,我们来聊聊。

各架构介绍

案例说明

以博主前公司业务为案例,前公司产品为软件研发流程工具,包含project、wiki、testcast、pipeline等等应用。

  • project

    项目管理,包含项目新建、需求、任务拆分等等。

  • wiki

    知识库管理,可以和项目管理中的需求进行关联,也可独立存在

  • testcast

    测试管理,bug的新建以及流程的扭转,可以和项目管理关联,也可以和知识库进行关联,也可以独立存在

  • pipeline

    流水线管理,Jenkins制定等等,单独应用,不与任何应用进行关联

单体架构

一个系统,业务量很小的时候,所有的代码都放在一个系统中即可,如以上案例,如果没有pipeline,那么project、wiki、testcast等等功能,全部放在一个项目中管理,部署在一台服务器上面即可。整个项目都有该台服务器提供,这就是单体架构。

danjijiagoutuE

单体架构的优势,就是开发快,部署快,排查问题快,不会有奇奇怪怪的问题出现。
但是,显而易见,缺点也不少。单机处理能力有限,当业务到达一定程度的时候,单机硬件无法满足业务需求,你只能不断的升级硬件去满足,且机器一旦宕机,整个服务全部挂掉。

集群架构

在IT界有各种高大上的解释,各种docker、k8s技术的诞生,使得集群结构推向一个高峰。
简而言之,就是单机处理到达瓶颈的时候,就把单机中的项目复制多份,形成一个集群,集群中每台服务器,就是这个集群的一个节点,每个节点的代码都是一样,都是提供相同的服务,然后由第三方组件进行流量的分流,比如使用NGINX做负载均衡。这样系统的处理能力,就相当于提升了好几倍。

jiqunjiagoujiedian

集群的优势,就是可以通过负载均衡进行分流,性能好的机器可以多干点活,性能差点的机器,可以少干活,如果某一个节点宕机了,也不影响整个服务的正常运行。而且扩展容易,如果比如双十一流量大,就多几台服务器,过了峰值,就减少几台节点。伸缩性强。

但是也是有着一些缺点,比如你的业务增加到一定程度的时候,无论你怎么增加节点,好像整个集群的性能提升了,但是并不明显,比如project应用访问量大,但是pipeline访问小呀,但是每次加节点的时候,都需要整个项目全部复制,这样也带来不少的浪费。

分布式架构

前面讲到的,从单机到集群,你的代码不需要做任何的修改,都是运维干的活,仅仅是多部署几台服务器。但是到了分布式结构的话,之前的代码就需要较大的改动了。所以新系统的时候,就采用分布式结构设计,这样后期维护成本低,嗯,这里强推 DDD 。 当然,如果之前没有涉及,比如案例中的project、wiki、testcase等等,没有拆分分层,后面业务量大了,是重构成分布式还是继续加节点,这就需要你们的架构师进行权衡,分析产出比、收益比,然后做出判断,但是重构是没有任何经济价值的。
好了,闲话少说。

分布式结构就是将一个完整的系统,按照业务划分,拆分成一个个独立的子系统,在分布式中,每个子系统中都是一个服务,这些服务都能够独立的运行在web服务器中,他们之间通过http、rpc等等方式进行通信. 也就是分布式将不同的业务分步在不同的地方。

fenbushijiagou0

可以看出,project、wiki、testcase、pipeline都是分别的项目,单独部署在不同的服务器上,其中project和wiki,project和testcast等等之间的通信,采用的是http进行通信。

这样的好处是,如果project访问大,可以专门针对project进行集群部署,增加节点方式进行扩容,而不需要影响到其他三个应用。更加灵活的进行硬件配置。 但是也会带来一系列的问题,比如分布式事务,project中的一个表,在wiki中也可以访问,如何确保一致性?分布式id如何编写?而且还有一个问题就是project中也有很多的业务,如何继续拆分project呢?

抛开问题不谈,后续会消化,至此,我们好的设计方案,应该是分布式和集群集合使用,先分布式再集群,也就是把project、wiki、testcase、pipeline等等,已经拆分的子业务,然后针对每个子业务做集群。

扩展下,分布式架构,目前常用的有7种,包括,大使模式、断路器模式、CQRS(命令查询责任分离模式)、事件溯源模式、领导选举模式、发布订阅模式、分片技术。 感兴趣的,可以移步 分布式架构七种模式

当然,我们接下来讲的微服务,也是一种不错的设计。

微服务

分布式架构,就已经解决了很多的问题,但是随着业务的不断扩展,比如project负责的是项目新建、需求、任务拆分,如果哪天任务模块的变动,是否是需要把整个project项目进行发布呢?好了,微服务来了。

微服务,是一个种架构风格,一个大型的复杂的软件,应该有一个或者多个微服务组成,每个微服务都可以拆分独立部署,微服务和微服务之间是弱藕合的。每个微服务,仅仅完成一个任务,每一个业务,就是一个小的业务能力,微服务和微服务之间,通过rpc进行通信,现在已经有很多现成的框架,比如gRPC等等。 当然,微服务可以分步在多个服务器上,也可以多个微服务放在一台服务器上。

weifuwujiagoueUEe4

如下,我们可以把project项目进行更为细粒度的拆分,比如task模块,后续有task相关的改动,就只是需要发布上线task项目即可。

当然,微服务的拆分,并不是依靠着某个人的经验,而是有一些列的规范流程,这里又强推DDD思想作为指导了。
恰巧,可以移步到: DDD拆分 了解下吧。

分布式事务

先说事务:事务是并发控制的基本单位,是一个不能被分割的,一句话就是要么全部成功,要么全部失败。具有 原子性、一致性、隔离性、持久性,也就是ACID。

我们今天主要讲解分布式,那么分布式事务是绕不开的一个话题,什么是分布式事务呢?

在我们刚刚说的单体环境下,事务基本是靠数据库自身提供的特性来实现的,我们不用操心,但是一旦到了分布式环境里面,光靠数据库是兜不住的,不仅是跨进程、跨节点,还有一个不靠谱的网络,很有可能导致部分服务成功,而部分服务失败,整个业务流程就会出问题,导致一对的脏数据。
比如一个案例,在分布式架构中,我们的project中的任务业务,其中关联修改了wiki业务,一旦任务业务完成,落库了,但是在调用wiki业务中出问题(网络异常、服务异常等等),那就会造成脏数据,就没有保证数据的一致性。

所以,官方定义为: 分布式事务,是指一个业务流程跨越多个分布式系统或者服务的事务处理,它需要确保在多个参与者之间的数据一致性和原子性

分布式事务解决方案

一般来说,我们有多重解决分布式事务的方案,以下介绍2PC和3PC。

2PC,两阶段提交协议

2PC,就是两阶段提交协议,将整个事务流程分为两个阶段,准备阶段(Prepare phase)和提交阶段(commit phase),2指的是两个阶段,P指的是准备阶段,C指的是提交阶段。

如下案例,为修改testcast中的项目A中的测试用例,并且同步修改项目A中关联的wiki记录。

2pcliuchengtujLk

如上:

  • 当testcase发起事务处理给事务管理器,事务管理器就进入第一阶段,询问project和wiki是否准备好了。
  • 当全部准备好后,都返回ok,事务管理器会根据第一阶段的收到回复,来决定整个事务的命运。事务管理器就会向所有的服务发送一个提交的命令,表示事务已经成功了,project和wiki服务就去做对应的修改。当然,有一个参与者返回了拒绝,那么我们的协调器就向所有的参与者发送回滚命令,所有参与者释放锁定的资源,撤销已经执行的操作,表示事务失败。
  • 所有,在第一阶段,是预先锁定的资源,事务管理器根据参与服务的反馈,来表示是需要提交还是回滚。
  • 通过这种方式,要么全部提交,要么全部失败。

是否有点繁琐?对,在分布式事务中,没有最好的方案,只有最合适的方案。
2PC优点是:可以利用数据库自身的事务特性,进行提交回滚。是根据数据库的事务来的。提交和回滚并不需要我们自己实现。不会侵入我们自己的业务逻辑。

缺点是:

  • 造成我们的同步阻塞,在2PC中,我们所有的参与者需要等到协调器的通知,回滚还是提交,所以会阻塞,整个并发能力下降。
  • 事务管理器这个组件比较关键,如果事务管理器宕机了,那就是整个分架构就完蛋了。
  • 最最麻烦的是事务管理器在发送回滚或者提交的时候,有可能出现网络问题。一旦有故障了,机会造成数据不一致的缺陷
  • 会暂用较高的资源,在2PC会锁定整个参与者的资源,一定要等到全部事务结束后才会释放,一旦事务比较慢,则整个数据都会锁定,系统吞吐能力急剧下降。

故障处理

我们刚刚说了,在2PC中,事务管理器和参与则之间会有一些通信的故障,比喻网络,那么有哪些方式可以解决这些故障呢?
也就是说,一旦出现故障,如何保障一次性和可靠性呢?

我们先来说方案吧:

  • 超时机制

    在整个2PC协议里面,每一个阶段都设定一个超时时间,比如我们在准备阶段去询问project和wiki,你是否准备好了,如果在设定的时间里面没得到回复,则事务管理器会进行相对应的处理,就可以理解会失败了,通知其他参与者直接回滚。

  • 心跳机制

    事务管理器可以定期向所有参与者发送心跳,如果超时了表示某个服务出现故障,则通知其他参与者回滚。

  • 备份事务管理器

    刚刚也说到了,事务管理器可能出现单点故障,为了防止整个分步架构崩塌,则可以使用备份事务管理器,做到高可用。

  • 消息队列

    事务管理器和参与者之间,通过MQ来进行通信,参与者把消息写入到队列中,事务管理器读取消息,如果在处理消息的时候失败了,就表示有问题,全部回滚。

在实际中,需要根据自身项目的因素,去选择合适的方案,当然,现在都流程3PC了。下面介绍。

3PC,3阶段提交协议

3PC:在2PC的基础上进行了优化的一种分布式事务协议,它通过增加一个额外的阶段来减少同步阻塞和单点故障的风险,在第一阶段和第二阶段中间,增加了一个预提交的机制。

具体有哪些区别呢?

  • 在2PC协议里面,只有协调者才拥有超时管理机制,也就是在一定时间中没有收到参与者的回复,则默认失败。但是在3PC协议中,协调者和参与者都设置了超时机制
  • 3PC在2PC的准备和提交阶段中,增加了preCommit阶段,也就是拥有conCommit和preCommit以及doCommit。添加preCommit目的就是缓冲,也就是保障在doCommit之前,所有参与者的状态都是一样的。

三个阶段如下图:

3pctijiaowtJI

  • 3PC的conCommit,和2PC的准备阶段是非常相似的,都是协调者向参与者发送一条commit请求,如果准备ok就返回yes,否则返回no。
  • 如果所有的参与者的反馈都是yes,进入第二个阶段preCommit,所以第一步是协调者向参与者发送preCommit请求,进入准备阶段,参与者的事务进行一个预提交,也就是把undolog和redolog记录到事务日志里面去,当参与者准备完成后,返回ACK给协调者,当然也可以是commit或者是rollback,如果是rollback那么协调者发送指令,所有的参与者全部回滚。
  • 当协调者收到了参与者的二阶段的状态,就进入最后一个步骤就是doCommit,当然也有两种可能性,要么成功,要么失败。 协调者向参与者发送doCommit命令,则参与者真正提交事务,释放所有的事务资源,最后向协调者发送反馈事务的结果。确认消息。 当然,三阶段中如果没有收到二阶段的ACK,则会发送rollback请求,进行事务的回滚。

所以,如果一旦进入了第三个阶段,说明肯定是接收到了二阶段的请求了,commit或者是rollback。所以在三阶段,发生了网络超时,虽然没有收到反馈事务的响应(3.2) 那么它也有理由相信,我们进行提交它的成功几率会很大,所以进行了提交。

所以3PC避免了状态停滞的问题,因为在2PC中,会有各种各样的原因,比如网络等等,产生停滞状态,但是3PC会让状态继续下去,虽然有可能是一个错误的。一旦错误,则不一致问题又存在了。
所以,无论2PC还是3PC,都存在多多少少的问题。

AT模式-自动补偿型事务模式

AT模式,自动补偿型事务模式,可以被认为是2PC提交协议的一种变种,和其极为相似,也是两阶段进行提交,但AT模式的主要特点是事务过程中引入了补偿操作,解决传统2PC中的某些问题,比如一致性。

分布式系统中的每个参与者(各个子系统)都会在完成本地事务后,生成补偿操作,补偿操作作用于在事务失败后,将已完成的本地事务回滚到之前的状态,通过这种方式,AT模式可以确保分布式事务的一致性,同时避免了传统2PC的阻塞问题,以下是流程。

atmosjiagoutu3uI

如上,也是包含了两个阶段,一阶段是尝试阶段,二阶段是确认阶段。

AT模式,一阶段尝试

在一阶段的时候,协调者向所有参与者发送事务的开始请求,参与者收到请求后执行本地的事务SQL,并将结果返回给协调者,但是不会立即提交事务,而是等待协调者的确认。

  • AT模式协调者会拦截业务SQL,比如sql为update,修改a为2,原本为1。
  • 提取表的元数据,协调者会首先解析SQL语义,找到原本a=1的记录,并且保持原来的快照为before image。
  • 然后再去执行update,a=2的sql。
  • 接着保存新快照,也就是a=2的快照,为after image。
  • 生成行锁,不然别的服务来修改。

AT模式,二阶段确认

在协调者收到所有参与者的执行结构后,会对这些结果进行汇总和分析,如果所有参与者都成功执行了本地事务,则协调者会向所有参与者发送事务的请求,参与者收到请求后会提交本地事务并向协调者返回提交结果。
当然,如果任何一个参与者在执行本地事务时出现问题,比如错误或者超时,协调者向所有参与者发送回滚事务的请求,参与者收到请求后会撤销本地事务并向协调者返回回滚结果。

  • 如果一阶段所有的参与者都表示ok,则二阶段只需要把一阶段中保持的快照全部删除,因为在一阶段已经提交到了数据库
  • 如果一阶段有问题,则涉及到回滚,回滚的逻辑。
    • 还原之前,要校验,对比数据库中的数据和保存之后的情况,如果一致表示没有脏写。如果不一致表示有脏写,人工处理。
    • 还原数据,就把我们before image 逆向一下,比如insert就是delete,进行数据还原
    • 删除各种镜像以及行锁。

AT模式总结

AT模式一阶段和二阶段、提交和回滚,大部分都是自动生成的,用于只需要关心自己的sql就好了,对于业务侵入性比较小。对比TCC使用起来方便。也提供较好的可靠力和容错能力,即使一个或者多个参与者出现故障,也可以通过回滚事务来保证数据的一致性。

但是也有坑,比如出现脏写了,AT模式是不能自己解决的,需要反馈人工处理。

TCC协议

以上讲解的协议,不太适合高并发的场景,而TCC也是和我们的2/3PC类似,但TCC协议相对比3PC,不会锁定资源,而是通过一个补偿机制,把锁定的资源转为业务的形式,去减少锁的一个粒度。

TCC也是通过三个阶段来实现事务的原子性和一致性, 三个阶段为:

  • 尝试 try:这个阶段,TCC协议会尝试执行所有的事务操作,对资源做一个检测以及预留,但是这些操作不会直接修改库。
  • 确认 confirm:这个阶段会执行真正的业务,不会做业务检查,使用try阶段的预留的资源,要求我们具备幂等性,因为会重试。
  • 取消 cancel:如果有任何一个服务执行出错,则需要执行回滚,释放try阶段的资源,也需要要求幂等性。当然采用的也是逆操作,将数据库中的数据恢复到事务执行之前的状态。 如下图:
    tcccduW4

初步看,优点模糊,我们来个具体的例子把。

TCC协议案例

案例为: testcase修改后,project中项目状态变为待审核,wiki状态变为修改中。这三个服务都是有这自己的数据库,采用分布式方式部署。
那么整个三阶段的流程,如下:

tccxieyiN4

如上图:

  • 我们在try阶段,它的作用就是锁定资源,去设定状态或者冻结数据。比如project和wiki,都是给一个预设置状态。
  • 在第二阶段,就是confirm阶段,它可能会遇到两种情况,一种是所有try成功,那就执行confirm阶段,如果有一个不成功,则执行cancel阶段。
  • 假设成功了,也就是我们会把所有的状态,直接设置成预设置的状态。落库了。
  • 假设失败了,则会把原先try阶段冻结的资源所释放,预状态全部重置,本身的状态还是之前的状态,处于没变的状态。

TCC总结

我们的TCC在正常的流程上来看,也是一个2阶段,但是在出现问题的时候,会具备自我修复能力,任何一个服务出现问题,则会执行逆操作来取消之前的操作。但是作为服务方,所有的补偿逻辑需要我们自己去写。如果开始是上述的协议,那么改成TCC的话,需要修改:

  • try逻辑修改,按照服务调用链路去执行冻结、预修改的操作。
  • confirm逻辑修改,也就是以往的业务,要真正的落地。
  • cancel逻辑修改,出现问题后,需要写补偿逻辑。

所以他的开发成本,针对2PC或者3PC来说,要高很多,因为针对业务侵入性了。但是它可以很好的去保证分布式事务的一致性,可靠性、扩展性。

TCC作为主流分布式事务协议,在微服务,特别是电商网站中,需要高一致性、可靠性的业务场景,用的很广泛,因为库存扣减、支付等等操作,需要高可靠,采用TCC可以保证事务的原子性和一致性。

Saga模式

saga也是一种解决分布式事务问题的模式,它的思想是将一个复杂的事务拆分成多个小事务,从而保证了分布式系统中的数据一致性。目前也慢慢成为很多公司解决分布式事务的首选方案之一。

整体流程如下:

sagashiwu

如上,我们有很多微服务信息,有一个主程序开始。

  • 主程序向事务管理器提交一个saga事务。然后主程序会把自己本地的事务给提交。
  • 然后通过事件的方式,通知微服务1,微服务1也把事务提交给事务管理器,得到反馈后,继续通过事件方式,通知微服务2,微服务2也是把事务提交给事务管理器,得到反馈后,以此类推。

假设出现异常,比如在微服务2上面出现了异常,那么则往后回滚到C2、C1。

当然saga的视线方式有几种,以下介绍两种最流行的:

  • 基于事件的方式,这种方式没有协调中心,整个模式的工作方式就像舞蹈一样,各个舞蹈演员按照预先编排的动作和走位各自表演,最终形成一只舞蹈。
  • 基于命令的方式,这种方式就像一只乐队,由一个指挥家(调度中心)来协调大家的工作,协调中心告诉saga的参与方应该执行哪一个本地事务。

CAP定理

CAP定理是分布式系统的基础理论,它描述一个分布式系统在以下三个特征中,只能满足两个理论。

  • 一致性(consistency):分布式系统完成写操作后的任何读操作,都应该读取到该写操作写入后的值。
  • 可用性(avaiablity):分布式系统可以在合理的时间内返回合理的结果,合理的时间意味着请求不能被无限阻塞,合理的响应指的是系统应该明确返回结果且结果是正确的。
  • 分区容忍性(parition tolerance):某个节点或者节点之间的通信故障的时候,认为是出现了网络分区,系统依然可以提供正常服务,意味着为了实现分区容忍性,一个数据块要在多个节点进行存储,这也就带来了一致性和可用性的矛盾。

所以,这三个特性,不能全部满足,最多只有三种组合:

  • CA(一致性、可用性),放弃分区容忍性,意味着系统没有分布式的概念,无法部署子节点,违背了分布式系统的初衷,只有单机系统满足这个组合,比如传统的mysql、redis等等。
  • CP(一致性、分区容忍性),放弃了可用性,意味着分区数据的同步事件可能无限延长,只有所有数据同步才能严格保证一致性,一旦发生网络波动和故障,就需要牺牲用户体验了,所有数据一致后才允许访问系统,所以CP主要应用在金融货币等数据一致性要求高的系统中。
  • AP(可用性、分区容忍性),放弃一致性,意味着当节点数据同步出现故障的时候,会出现全局数据的不一致性,例如redis为高可用存储,AP主要应用于各种互联网应用系统,也是比较常用的一个祖册。

caplilundd

但是,关于一些误解:

  • CAP定理必须放弃 一种一个吗?

    并不是哈,因为P(分区容忍性)出现的概率极低,所以正常情况下一个分布式系统还是需要有完美的一致性和可用性的。

  • 一致性和可用性是针对整个分布式系统的?

    当分区发生的时候对一致性和可用性的抉择是局部的,并非针对整个系统。例如电商中,入账的业务来说,就需要强一致性,但是获取用户信息等等边缘业务,就只需要高可用即可,具体取决于业务。

BASE理论

BASE是 Basiclly Avalibale(基本可用)、Soft satae(软状态)、Eventually consisiten(最终一致性)三个短语的缩写,是对CAP中的一致性和可用性权衡的结果,基于CAP定理逐步演化而来的,核心思想就是即使无法做到强一致性,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性

基本可用 Basiclly Available

基本可用指的是分布式系统在出现不可预知的故障的时候,允许损失部分的可用性,但绝不是等价于系统不可用,例如:

  • 在响应时间上面的消耗,正常情况下一个请求100ms可以响应用户,但是因为故障,响应时间变为2s一下。
  • 功能上的损失,比如正常情况下用户可以进行完整的功能体验,但是流量高峰下,部分用户可能被降级到一个兜底页面,或者部分功能被隐藏掉。

软状态 Soft satae

指的是系统重的数据存在中间状态,并认为该终极爱你状态不会影响系统的整体可用性,既允许在不同节点的数据副本存在数据延迟。

比如用户获取活动奖励的时候,在玩法里的收益流水和钱包里面的收益流水会出现短暂的不一致的情况。

最终一致性 Eventually consistent

最终一致强调的是系统所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态,因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而非强一致性。

例如上述例子,用户的流水最终会出现一致性即可。

人未眠
工作数十年
脚步未曾歇,学习未曾停
乍回首
路程虽丰富,知识未记录
   借此博客,与之共进步