分布式系统

什么是分布式系统

分布式系统首先要相对集中式系统,集中式就是把一个系统的所有服务都放在一个机器上,而分布式是把一个系统拆分成多个服务模块,部署在不同的机器机器上,服务模块之间通过远程调用(RPC)进行通信,所以分布式系统的一个主要问题就是网络通信问题。

CAP理论

一个分布式系统只能满足CAP理论的两个,其中P又是必须的,所以就是在C和A之间的权衡。

  • C(Consistency)一致性:

    每次读取都会收到最新的写入数据或错误信息。(强一致性模型)

  • A(Availability)可用性:

    每个请求都会收到(非错误的)响应,但不能保证响应包含最新的写入数据。

    根据停机时间判断。

  • P(Partition tolerance)分区容错性:

    尽管网络节点之间会丢弃(或延迟)任意数量的消息,系统仍然能够继续运行。

BASE理论

BASE理论是对CAP理论的延伸,核心思想是即使无法做到强一致性(Strong Consistency, CAP的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性(Eventual Consitency)

BASE是指基本可用(Basically Available)、软状态( Soft State) 、最终一致性(Eventual Consistency) )。

两阶段提交和三阶段提交

所谓的两个阶段是指:第一阶段:准备阶段(投票阶段)和第二阶段:提交阶段(执行阶段)。

但是2PC本身存在着同步阻塞问题、单点故障问题、数据不一致问题等。

值得注意的是,二阶段提交协议的第一阶段准备阶段不仅仅是回答YES or NO,还是要执行事务操作的,只是执行完事务操作,并没有进行commit还是rollback。一旦事务执行之后,在没有执行commit或者rollback之前,资源是被锁定的。这会造成阻塞。

2PC的问题

  • 同步阻塞

    • 在第一阶段的时候,需要占有公共资源,其他节点需要访问只能同步阻塞。
  • 单点故障

    • 如果协调者出现故障,参与者占有的资源就会一直不释放,在选取新的协调者之前一直都会阻塞。
  • 数据不一致

    • 在2PC的过程中有可能出现参与者或协调者宕机的情况,无论哪一个宕机,都有可能带来一致性问题,有三种情况:

      • 参与者宕机,协调者正常

        • 参与者宕机,没再恢复,不需要处理,不会产生一致性问题;
        • 宕机之后又恢复,如果它有未完成的事务,直接取消,然后询问协调者,协调者根据自己的事务记录以及参与者的事务记录进行判断。
      • 参与者正常,协调者宕机

        只需要找一个新的协调者,再查看他们最后的决定就可以了。

      • 两个都宕机

        需要分三种情况判断:

        • 是在阶段一宕机的

          由于这个阶段还没有执行commit,协调者只需要再询问参与者决定是commit还是rollback就行。

        • 第二阶段宕机,但是在参与者宕机之前并没有收到协调者的指令,或者收到指令后没来得及执行

          和上一种情况差不多,因为宕机的参与者还是没有执行操作,所以在协调者回复之后,还是询问参与者,如果有机器返回了rollback就都进行rollback,如果没有人rollback但是有人commit,就都执行commit,当宕机的参与者回复之后只需要执行协调者的指令就行了,因为都是一样的。

        • 第二阶段宕机,但是参与者在宕机之前执行了操作,但是没有人知道他执行的什么

          这样就会出现数据不一致的情况。

3PC

3PC最关键要解决的就是协调者和参与者同时挂掉的问题,所以3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有CanCommit、PreCommit、DoCommit三个阶段。

在第一阶段,只是询问所有参与者是否可以执行事务操作,并不在本阶段执行事务操作。当协调者收到所有的参与者都返回YES时,在第二阶段才执行事务操作,然后在第三阶段在执行commit或者rollback。

3PC是怎么解决2PC的问题的呢?

还是假设参与者和协调者都在第二阶段宕机,参与者在宕机前执行了操作,通过3PC协调者可以通过判断没有宕机的参与者来判断宕机的参与者执行的什么操作,因为如果宕机参与者执行的commit操作,那么其他的参与者一定也是precommit或者commit,如果执行的rollback操作,其他参与者一定也是执行的rollback操作,所以就可以通过判断其他的参与者执行的什么操作,来判断宕机的参与者执行的什么操作。

3PC的问题

在doCommit阶段,如果参与者无法及时接收到来自协调者的doCommit或者reborti请求时,会在等待超时之后,会继续进行事务的提交。

所以,由于网络原因,协调者发送的bot响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到aborti命令并执行回滚的参与者之间存在数据不一致的情况。

分布式锁实现方式

主要有以下三种:

  • 数据库:数据库表记录
  • Redis:setnx命令、Redission、redlock
  • zk:zookeeper的临时顺序节点

分布式事务方案

  • 强一致性
    • 两阶段提交
    • 三阶段提交
    • 支持两阶段提交的第三方框架:Seata
    • TCC
  • 最终一致性
    • 本地消息表
    • 可靠消息,支持事务的消息中间件,RocketMQ
    • 最大努力通知
    • Seata

如何选择分布式事务

  • 考虑成本
    • 引入中间件需要考虑维护,TCC成本太高
  • 一致性程度考虑
  • 可用性要求
    • 根据实际业务场景考虑是需要最终一致性还是强一致性
  • 数据规模
    • 如果数据量过大,不适合使用消息中间件,有可能出现消息堆积,消息丢失等问题,可以选择Seata

TCC

TCC是Ty-Confirm–Cancel的缩写,它是一种分布式事务解决方案,采用了基于业务逻辑的补偿机制,将整个分布式事务分解为若干个子事务,每个子事务都有一个try、confirm和cancel3三个操作,通过这些操作来实现分布式事务的执行和回滚。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • Try:
    • 在try阶段,参与者尝试执行本地事务,并对全局事务预留资源。如果try阶段执行成功,参与者会返回一个成功标识,否则会返回一个失败标识。
  • confirm
    • 如果所有参与者的try阶段都执行成功,则协调者通知所有参与者提交事务,那么就要执行confirm 阶段,这时候参与者将在本地提交事务,并释放全局事务的资源。
  • cancel
    • 如果任何一个参与者在try阶段执行失败,则协调者通知所有参与者回滚事务。那么就要执行cancel 阶段,还有就是,如果某个参与者在try阶段执行成功,但是在confirml阶段执行失败,则需要执行cancel操作,将之前预留的资源释放掉。

空回滚和悬挂问题:

  • 空回滚问题:TCC中的Try过程中,有的参与者成功了,有的参与者失败了,这时候就需要所有参与者都执行Cancel,这时候,对于那些没有Ty成功的参与者来说,本次回滚就是一次空回滚。需要在业务中做好对空回滚的识别和处理,否则就会出现异常报错的情况,甚至可能导致Cancel-一直失败,最终导致整个分布式事务失败。

    (Try过程中有的成功有的失败就需要Cancel,但是对于失败的参与者来说他们都没有执行,回滚也没事干。)

  • 悬挂事务问题:TCC的实现方式存在悬挂事务的问题,在调用TCC服务的一阶段Try操作时,可能会出现因网络拥堵而导致的超时,此时事务协调器会触发二阶段回滚,调用TCC服务的Cancel操作;在此之后,拥堵在网络上的一阶段Try数据包被TCC服务收到,出现了二阶段Cancel请求比一阶段Try请求先执行的情况。

    举一个比较常见的具体场景:一次分布式事务,先发生了Try,但是因为有的节点失败,又发生了Cancel,而下游的某个节点因为网络延迟导致先接到了Cancel,在空回滚完成后,又接到了Try的请求,然后执行了,这就会导致这个节点的Try占用的资源无法释放,也没人会再来处理了,就会导致了事务悬挂。

    (网络延迟导致Cancel比Try先执行,Try的资源无法释放)

解决方案:

引入分布式事务记录表,有了这张表,每一个参与者,都可以在本地事务执行的过程中,同时记录一次分布式事务的操作记录。

这张表中有两个关键的字段,一个是tx_id用于保存本次处理的事务ID,还有一个就是state,用于记录本次事务的执行状态。至于其他的字段,比如一些业务数据,执行时间、业务场景啥的,就自己想记录上就记录啥。

  • 空回滚解决:当一个参与者接到一次Cancel请求的时候,先去distribute_transaction表中根据tx_id查询是否有try的记录,如果没有,则进行一次空回滚即可。并在distribute_transaction中创建一条记录,状态标记为cancel。
  • 事务悬挂解决:当一个参与者接到一次Try请求的时候,先去distribute_transaction表中根据tx_id查询是否有记录,如果当前存在,并且记录的状态是cancel,则拒绝本次try请求。

分布式ID生成方案

  • UUID
  • 数据库自增
  • 号段模式(数据库基础上,取出一批放在缓存)
  • Redis
  • 雪花算法
  • 第三方(美团Leaf)

分布式Session

用户在A服务器登录,下次在B服务器登录需要一个分布式Session来告诉B服务器用户登录过了。

  • 客户端存储
    • 将Session保存在客户端,下次用户再登录的时候,通过Cookie携带客户端保存的Session(需要将Session暴露给客户端)
  • 基于分布式存储
    • 使用分布式存储,不同的服务共用一个分布式存储(需要第三方的存储服务Redis、数据库)
  • 粘性Session
    • 用户在哪个服务器登录的,下次还让他访问这个服务器(单点故障)
  • Session复制
    • 用户在登录之后生成一个Session,Session在服务之间进行复制(存在延迟)

负载均衡

负载均衡(Load Balance),意思是将负载(工作任务,访问请求)进行平衡、分摊到多个操作单元(服务器,组件)上进行执行。是解决高性能,单点故障(高可用),扩展性(水平伸缩)的终极解决方案。

负载均衡算法可以分为两类:静态负载均衡算法和动态负载均衡算法。

静态负载均衡算法包括:轮询,比率,优先权

动态负载均衡算法包括:最少连接数,最快响应速度,观察方法,预测法,动态性能分配,动态服务器补充,服务质量,服务类型,规则模式。

OSI七层网络模型如下图:

image-20231019180401726

负载均衡大多在2/3/4/7层做文章,常用的是4/7

  • 四层负载均衡
    • 四层负载均衡工作在OS模型的传输层,由于在传输层,只有TCP/UDP协议,这两种协议中除了包含源IP目标IP以外,还包含源端口号目的端口号。四层负载均衡服务器在接受到客户端请求后,以后通过修改数据包的地址信息**(IP+端口号)**将流量转发到应用服务器。
  • 七层负载均衡
    • 七层负载均衡工作在OS模型的应用层,应用层协议较多,常用http、radius、dns等。七层负载就可以基于这些协议来负载。这些应用层协议中会包含很多有意义的内容。比如同一个Wb服务器的负载均衡,除了根据P加端口进行负载外,还可根据七层的URL浏览器类别语言来决定是否要进行负载均衡。

负载均衡工具:

Nginx、LVS、HAProxy

常见负载均衡算法:

  • 静态
    • 轮询(Round Robir):顺序循环将请求一次顺序循环地连接每个服务器。当其中某个服务器发生第2到第7层的故障,BG-IP就把其从顺序循环队列中拿出,不参加下一次的轮询,直到其恢复正常。
    • 比率(Rtio):给每个服务器分配一个加权值为比例,根椐这个比例,把用户的请求分配到每个服务器。当其中某个服务器发生第二到第7层的故障,BG-P就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常。
    • 优先权(Priority):给所有服务器分组,给每个组定义优先权,BIG-IP用户的请求,分配给优先级最高的服务器组(在同一组内,采用轮询或比率算法,分配用户的请求);当最高优先级中所有服务器出现故障,BG-IP才将请求送给次优先级的服务器组。这种方式,实际为用户提供一种热备份的方式。
  • 动态
    • 最少的连接方式(Least Connection):传递新的连接给那些进行最少连接处理的服务器。当其中某个服务器发生第二到第7层的故障,BG-P就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常。
    • 最快模式(Fastest):传递连接给那些响应最快的服务器。当其中某个服务器发生第2到第7层的故障,BG-IP就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常。
    • 动态性能分配(Dynamic Ratio-APM):BIG-IP收集到的应用程序和应用服务器的各项性能参数,动态调整流量分配。
    • 服务质量(QoS):按不同的优先级对数据流进行分配。
    • 服务类型(ToS):按不同的服务类型(在Type of Field中标识)负载均衡对数据流进行分配。

如何解决接口幂等

解决接口幂等问题,只需要记住一句口令"一锁、二判、三更新",只要严格遵守这个过程,那么就可以解决并发问题。

  • 一锁:第一步,先加锁。可以加分布式锁、或者悲观锁都可以。但是一定要是一个互斥锁!(Redis分布式锁)
  • 二判:第二步,进行幂等性判断。可以基于状态机、流水表、唯一性索引等等进行重复操作的判断。
  • 三更新:第三步,进行数据的更新,将数据进行持久化。

以上操作需要有一个前提,那就是第一步加锁、和第二步判断的时候,需要有一个依据,这个就是幂等号了,通常需要和上游约定一个唯一ID作为幂等号。然后通过对幂等号加锁,再通过幂等号进行幂等判断即可。

//加分布式锁
@DistributeLock(scene = "OEDER",keyExpression = "#request.identifier", expire = 3000)
public OrderResponse apply(OrderRequest request){
		OrderResponse reponse = new PrderResponse();
		//判断请求是否执行过了
		OrderDTO orderDTO = orderService.queryOrder(request.getProduct(),request.getIdentifier());
		if(orderDTO != null){
				response.setSuccess(false);
				response.setResponseCode("DUPLICATED");
				return response;
		}
		//执行更新的业务逻辑
		return orderService.order(request);
}

Seata

阿里开源的分布式事务解决方案,目前支持4中模式:AT、TCC、Saga、XA。

XAATTCCSaga
一致性强一致性弱一致性弱一致性最终一致性性
隔离性完全隔离基于全局锁隔离基于资源预留隔离无隔离
代码入侵性有,要编写TCC三个接口有,要编写状态机和补偿代码
性能非常高非常高
适用场景对一致性、隔离性要求高的场景基于关系型数据库的大多数分布式事务场景对性能要求高的场景,有非关系型数据库要参与的事务业务流程长且多。参与者包含外部接口和遗留接口,无法做TCC模式。

Seata将所有的组件定义为3种:

  • Transaction Coordinator(TC):这是一个独立的服务,是一个独立的JVM进程,里面不包含任何业务代码,它
    的主要职责:维护着整个事务的全局状态,负责通知RM执行回滚或提交;
  • Transaction Manager(TM):在微服务架构中可对应为聚合服务,即将不同的微服务组合起来成一个完成的业务流
    程,TM的职责是开启一个全局事务或者提交或回滚一个全局事务;
  • Resource Manager(RM):RM在微服务框架中对应具体的某个微服务为事务的分支,RM的职责是:执行每个事务分支的操作。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Seata大致的执行流程:

image-20231019185521123
  1. TM在接收到用户的下单请求后,会先调用TC创建一个全局事务,并且从TC获取到他生成的XID。
  2. TM开始通过RPC/Restfu调用各个RM,调用过程中需要把XID同时传递过去。
  3. RM通过其接收到的XID,将其所管理的资源且被该调用所使用到的资源注册为一个事务分支(Branch Transaction)。
  4. 当该请求的调用链全部结束时,TM根据本次调用是否有失败的情况,如果所有调用都成功,则决议Commit,如果有超时或者失败,则决议Rollback。
  5. TM将事务的决议结果通知TC,TC将协调所有RM进行事务的二阶段动作,该回滚回滚,该提交提交。

一致性Hash

一致性哈希(Consistent Hashing)是一种用于分布式系统中数据分片和负载均衡的算法。它的目标是在节点的动态增加或删除时,尽可能地减少数据迁移和重新分布的成本

image-20231019190515004
  1. 实现一致性哈希算法首先需要构造一个哈希环,然后把他划分为固定数量的虚拟节点,如232。那么他的节点编号就是0-232-1;
  2. 我们把128张表作为节点映射到这些虚拟节点上,每个节点在哈希空间上都有一个对应的虚拟节点;
  3. 再把这些表做好hash映射之后,我们就需要存储数据了,现在我们要把一些需要分表的数据也根据同样的算法进行hash,并且也将其映射哈希环上;
  4. 按照数据的位置,沿着顺时针方向查找,找到的第一个分表节点就是数据应该存放的节点;
  5. 因为要存储的数据,以及存储这些数据的数据库分表,hash后的值都是固定的,所以在数据库数量不变的情况下,下次想要查询数据的时候,只需要按照同样的算法计算一次就能找到对应的分表了。

哈希倾斜问题:

其实,hash倾斜带来的主要问题就是如果数据过于集中的话,就会使得节点数量发生变化时,数据的迁移成本过高。

最好的办法就是增加服务器节点,如果没有多余服务器,就将一个服务器节点拆分成多个虚拟节点,然后数据映射的时候先映射到虚拟节点,然后虚拟节点在找到对应的物理节点进行存储和读取就行了。

链路追踪

开源工具skywalking,还有Googlel的dapper、twitter的zipkin、京东的nydra、大众点评的cat。

  • 生成一个全局的traceld
    • 想要追踪一个完成的链路,就需要有一个标识来标记这次调用链,业内把他叫做traceld,一般会在入口处生成一个全局唯一的traceld,比如说在HTTP的请求入口,在定时任务的调度入口等,生成一个traceld。
  • 把这个traceld传递下去
    • 想要实现一个链路追踪,需要很多中间件一起配合才行,通常这个traceld会存放在RPC的请求头HTTP的请求头MQ消息的消息头中进行传递。
image-20231019193341541

系统之间通过这种方式,那系统内部也是需要传递的,所以一般都是用ThreadLocal来实现的,在接收到请求后,会把这个traceld存储在ThreadLocal中,然后就能在当前线程中一直传递下去,并且在记录日志的时候取出来打印到日志中,在需要调远程的时候,取出来传递下去。

如果是多线程就要用到TTL(TransmittableThreadLoca)了。

光有traceId不够,它只是帮我们把一次调用串联起来,我们还需要一个Span记录信息。Span的内容如下:

  • Operation Name:描述了当前接口的行为语义,如具体的哪个接口,哪个URL地址。
  • Spanld/ParentSpanld:接口调用的层级标识,用于还原Trace内部的层次调用关系。
  • Stat/FinishTime:接口调用的开始和结束时间,二者相减就是该次调用的耗时。
  • StatusCode:响应状态,标识当次调用是成功或失败。
  • Tags&Events:调用附加信息。

Canal

Canal是阿里巴巴开源的数据同步工具,大致的工作原理如下:

Canal会模拟MySQL slave的交互协议,把自己伪装成为一个MySQL slave,向MySQL master发送dump协议,MySQL master收到dump请求后,会开始向这个伪装的slavee(canal)推送binlog, canal把binlog解析成流,然后对接到各个后续的消费者中,如ES、数据库等。

分布式系统的一致性

  • 强一致性模型(Strong Consistency):在强一致性模型下,系统保证每个读操作都将返回最近的写操作的结果,即任何时间点,客户端都将看到相同的数据视图。这包括线性一致性(Linearizability)、顺序一致性(Sequential Consistency)和严格可串行性(Strict Serializability)等子模型。强一致性模型通常牺牲了可用性来实现数据一致性。
    • 线性一致性是一种最强的一致性模型,它强调在分布式系统中的任何时间点,读操作都应该返回最近的写操作的结果。
    • 顺序一致性也是一种强一致性模型,但相对于线性一致性而言,它放宽了一些限制。在顺序一致性模型中,系统维护一个全局的操作顺序,以确保每个客户端看到的操作顺序都是一致的顺序一致性不强调实时性
  • 弱一致性模型(Neak Consistency):弱一致性模型放宽了一致性保证,允许在不同的时间点和不同的客户端之间看到不同的数据视图。这包括因果一致性(Causal Consistency)、会话一致性(SessionConsistency)和单调一致性(Monotonic Consistency)等子模型。弱一致性模型通常更注重可用性,允许一定程度的数据不一致性。
  • 最终一致性模型(Eventual Consistency):最终一致性模型是一种最大程度放宽了一致性要求的模型。它允许在系统发生分区或网络故障后,经过一段时间,系统将最终达到一致状态。这个模型在某些情况下提供了很高的可用性,但在一段时间内可能会出现数据不一致的情况。相比顺序一致性,最终一致性只要求最终的结果一致就可以了,而顺序一致性还要求操作顺序的一致
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值