.Net微服务实战之必须得面对的分布式问题(二)

本文涉及的产品
云原生网关 MSE Higress,422元/月
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: .Net微服务实战之必须得面对的分布式问题(二)

分布式事务

  

分布式事务分刚性事务与柔性事务,刚性事务对应ACID理论,而柔性事务也就是最终一致性,对应BASE理论。最终一致性指如果数据再一段时间内没有被另外的数据操作所更改,那它最终会达到与强一致性过程相同的结果。

  

分布式系统场景下很少使用xa事务,主要原因是xa事务是基于基础设施层面的强一致性事务,场景主要在一个服务多个数据源,追求强一致性,复杂度高,吞吐量低。

  

而最终一致性方案更多是基于服务应用层的弱一致性事务,场景主要是多服务多数据源与多服务单数据源,满足了BASE理论的三个特点:基本可用软状态最终一致性

  

以订单支付为例讲述下BASE理论,客户在A平台发起了订单支付,订单支付时状态为支付中,完成后支付后,等待支付系统的回调,但是这个时候,A平台的回调API接口异常了,订单状态无法同步为已支付状态,这个时候客户看到订单的金额支付出去了,但是去搜索订单模块的时候发现还是未支付,于是反馈给了客服,开发部经过一段时间的问题定位与排查,发现是回调API挂了于是重启后,数分钟订单状态就同步成已完成了。


BASE理论
基本可用(Basically Available) 分布式系统在出现不可预知故障时,允许损失部分可用性
软状态(Soft state) 允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性
最终一致性(Eventually consistent) 系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态

  

从上面的例子来看,支付中就是软状态,回调API服务虽然挂了,但是前台系统还是可以提供给客户端查询使用就是基本可用,只不过订单状态不对,当然最后服务也恢复后达成数据最终一致性

 

分布式数据一致性方案

名称

场景

优点

缺点

异步请求/回调

跨网络环境、同网络环境

实现简单

强业务

TCC

跨网络环境、同网络环境

有现成的框架、实现简单

强业务

基于消息可靠的最终一致性

同网络环境

有现成的框架、通用性强

中间件依赖

  

分布式事务方案常见的主要有这几种:异步请求/回调、TCC、基于消息可靠的最终一致性,TCC与基于消息可靠的最终一致性在Java和.Net都是有现成的框架,而异步请求/回调更多是与支付机构对接的场景会比较多,实现简单、通用性强,如果团队技术能力不足也可以使用该方案代替。

  

异步请求/回调更多是应对并发处理的异步解决方案,查过相关资料并没有纳入相关分布式事务方案中,但是在我的实际工作经验中该方案也是可以达成最终一致性。


异步请求/回调


image.png

该方案在与支付机构对接的场景比较常见,其核心以业务发起请求,被调用端以数据优先入库,稍后异步处理,处理完成后则回调请求业务端提供的API。

  

这种异步处理方式一般获取结果的方式推拉结合,外部系统主动回调给本地称之为推,本地系统每隔一段时间主动查询外部系统结果称为拉,两者可以按照业务的时效性结合策略使用。

     

公司内部系统之间也可以这么做,业务系统请求对接系统,被请求后数据库直接入库,然后通过定时调度任务异步做业务处理,业务处理成功还是失败都修改状态,最后由回调调度任务把业务处理的状态、处理信息回调给业务系统的回调API,为了避免回调调度任务因故障无法回调,可以设置策略由业务系统主动查询对接系统提供的查询API,推拉结合保证了系统可用性和数据时效性。


TCC


image.png

  

TCC是Try、Comfirm、Cancel三个单词的缩写,Try是资源预留、锁定,Comfirm是确认提交,Cancel是指撤销。 一个资源的处理需要提供三个接口,从业务侵入性来看是比较强的。

  

TCC的执行步骤与2PC有点相似,先进入预提交阶段,对A、B、C三个资源的分别进行try处理,如果try请求成功,相应的资源就会被修改成中间状态,可以理解成被冻结。接下来就会根据每个资源try后的情况判断如何执行。如果全部try成功,则会进入Comfirm处理,只要能try成功就能Comfirm成功。如果其中一个资源try失败了,则会对所有进行Cancel处理。

  

TCC与2PC看起来相似,但还是有区别的,TCC是应用服务层面的,而2PC则是基础设施层,而2PC因为是强一致性基于遵守ACID,在事务未提交时处于阻塞状态,如果失败则会事务回滚,而TCC是没有事务回滚的,每个阶段处理都穿透到数据库都是Commit操作。


基于消息的最终一致性


image.png


该方案其实是ebay多年前提出的本地消息表的解决方案,该方案的核心点在于,执行本地事务后再提交队列消息,这两步骤操作因为非原子性的跨进程操作,因为需要保证发送到消息队列的消息能正常发布与正常的消费,这就是我们常说的保证消息可靠,那么在执行本地事务的时候,本地业务表与消息凭据表会作为一个原子性事务提交到数据库,消息凭据表会记录着消息队列的消息序列化数据,如果本地事务提交成功了,但是发送消息队列的时候失败了,就会通过后台线程(进程)查询消息凭据表,把未发送成功的消息反序列化出来重新发起。

  

无论再消息发布端还是消息消费端都会因为与消息队列交互后,修改消息凭据表状态的情况,如果与消息队列交互是正常的,但是修改消息凭据状态失败了,补偿服务仍然会进行不必要的重发,那么这个场景容易导致数据重复创建与覆盖,因此需要关注幂等性的处理了。

  

该方案在.Net有CAP这个分布式事务框架,无需开发人员自己自己实现。


幂等性

  

幂等性的定义,相同的参数在同一个方法里,无论执行一次还是多次都会响应相同的结果

  

举个例子银行转行,A银行账户扣了100元,B银行账户加100元,这样数据一致的。但是在给B账户加100元的时候,B银行系统处理超时,但是其实这个时候B银行是已经处理成功了,只不过没响应回去,那么A银行系统就会重发,如果没有幂等性处理的话,A重试了3次,B账户就会加3次100。一边扣100,一边加300,那么数据就不一致了。

  

对于查询和删除数据的场景都有天然的幂等性,那么我们考虑幂等性处理更多是关注于新建数据更新数据。

  

新建数据的场景,如果没有处理好幂等性,那么就会导致数据重复创建,原因有可能是用户连续点击后发起请求,也有可能是API网关的retry请求。解决方案也相对比较简单,API提供主键参数(流水号)传入,就是由调用端预生成主键(流水号)传入API进行请求,API端生成流水与余额扣减作为同一个事务处理。此时如果因为某个原因进行了两次调用,因为第一次创建成功了,第二次则会因为主键的唯一性抛出了异常,这里需要注意的是得捕获到的唯一键异常应处理成执行成功的响应。

  

更新数据的场景,如果没处理号幂等性,可能会因为RPC框架或者API网关的Retry机制导致重复请求,这样就会造成了ABA数据覆盖问题,所谓的ABA就是,第一次请求A数据已经进行写处理了,接着到了第二次请求B数据进行对A数据进行了修改成功了,但是因为第一次请求因为某个原因导致客户端无法接收到响应,因此API网关或者RPC框架进行了重发,所以第三次把A数据又对已有的B数据进行修改覆盖。针对该问题解决方案主要是使用数据版本判断。

 

幂等性处理方案

场景

问题

方案

新建数据

重复创建

由调用端预生成订单号,唯一键约束

更新数据

ABA覆盖问题

添加版本号判断

  

以上两种方法处理方式从数据库层面解决,相对比较简单直接,侵入性比较强,还有一种方案可以从Web框架层面解决,结合Web框架的AOP与Redis判断,每次请求都会附带一个requestID传入到接口,由Filter拦截后Add到Redis。此方案需要引入Redis,从实现上比前面两个相对复杂,但是通用性相对高一些。


结束

  

该篇到这里就结束了,主要总结了平常在分布式系统不得不去面对的问题,虽然大家会通过一些设计,尽可能去避免,但是唯一不变的是需求的变化,因此我们尽可能优先了解各种处理方案,如有遇到就可针对场景选择合适的方案。

目录
相关文章
|
14天前
|
存储 消息中间件 Apache
比较微服务中的分布式事务模式
比较微服务中的分布式事务模式
31 2
|
9天前
|
消息中间件 Java Kafka
"Kafka快速上手:从环境搭建到Java Producer与Consumer实战,轻松掌握分布式流处理平台"
【8月更文挑战第10天】Apache Kafka作为分布式流处理平台的领头羊,凭借其高吞吐量、可扩展性和容错性,在大数据处理、实时日志收集及消息队列领域表现卓越。初学者需掌握Kafka基本概念与操作。Kafka的核心组件包括Producer(生产者)、Broker(服务器)和Consumer(消费者)。Producer发送消息到Topic,Broker负责存储与转发,Consumer则读取这些消息。首先确保已安装Java和Kafka,并启动服务。接着可通过命令行创建Topic,并使用提供的Java API实现Producer发送消息和Consumer读取消息的功能。
31 8
|
11天前
|
Cloud Native 云计算 微服务
云原生时代:企业分布式应用架构的惊人蜕变,从SOA到微服务的大逃亡!
【8月更文挑战第8天】在云计算与容器技术推动下,企业分布式应用架构正经历从SOA到微服务再到云原生的深刻变革。SOA强调服务重用与组合,通过标准化接口实现服务解耦;微服务以细粒度划分服务,增强系统灵活性;云原生架构借助容器化与自动化技术简化部署与管理。每一步演进都为企业带来新的技术挑战与机遇。
42 6
|
8天前
|
开发框架 前端开发 .NET
七天.NET 8操作SQLite入门到实战 - (3)第七天Blazor学生管理页面编写和接口对接
七天.NET 8操作SQLite入门到实战 - (3)第七天Blazor学生管理页面编写和接口对接
|
17天前
|
存储 监控 安全
|
17天前
|
监控 负载均衡 Java
(九)漫谈分布式之微服务组件篇:探索分布式环境下各核心组件的必要性!
本文将深入探讨微服务中各个组件的必要性,以此帮助各位更好地加深对分布式系统的掌握度。
|
4天前
|
Kubernetes Nacos 微服务
【技术难题破解】Nacos v2.2.3 + K8s 微服务注册:强制删除 Pod 却不消失?!7步排查法+实战代码,手把手教你解决Nacos Pod僵死问题,让服务瞬间满血复活!
【8月更文挑战第15天】Nacos作为微服务注册与配置中心受到欢迎,但有时会遇到“v2.2.3 k8s 微服务注册nacos强制删除 pod不消失”的问题。本文介绍此现象及其解决方法,帮助开发者确保服务稳定运行。首先需检查Pod状态与事件、配置文件及Nacos配置,确认无误后可调整Pod生命周期管理,并检查Kubernetes版本兼容性。若问题持续,考虑使用Finalizers、审查Nacos日志或借助Kubernetes诊断工具。必要时,可尝试手动强制删除Pod。通过系统排查,通常能有效解决此问题。
10 0
|
4天前
|
存储 缓存 开发框架
看看 Asp.net core Webapi 项目如何优雅地使用分布式缓存
看看 Asp.net core Webapi 项目如何优雅地使用分布式缓存
|
1月前
|
监控 Java 微服务
Spring Boot微服务部署与监控的实战指南
【7月更文挑战第19天】Spring Boot微服务的部署与监控是保障应用稳定运行和高效维护的重要环节。通过容器化部署和云平台支持,可以实现微服务的快速部署和弹性伸缩。而利用Actuator、Prometheus、Grafana等监控工具,可以实时获取应用的运行状态和性能指标,及时发现并解决问题。在实际操作中,还需根据应用的具体需求和场景,选择合适的部署和监控方案,以达到最佳效果。
|
1月前
|
消息中间件 Java 开发者
Spring Cloud微服务框架:构建高可用、分布式系统的现代架构
Spring Cloud是一个开源的微服务框架,旨在帮助开发者快速构建在分布式系统环境中运行的服务。它提供了一系列工具,用于在分布式系统中配置、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态等领域的支持。
119 5