需要注意的是标题中的CAP不是指的CAP理论,而是园区大神杨晓东实现的框架,CAP框架基于本地消息表用最终一致性实现分布式事务。

本地消息表

首先我们考虑一个场景,在将用户信息更改后,需要发送一条消息到消息队列、缓存或是写入到其他库中。这个过程涉及到一个本地库与MQ、本地库与Cache或是本地库与其他库两者之间的事务问题,不能用简单的数据库事务控制了。

这种分布式事务下,常用的解决方案有2PC、3PC等强一致性保证的,也有TCC、Sagas模型、本地消息表、内嵌本地消息表的MQ等最终一致性保证的。

而在很多异步场景下,允许系统存在短暂的不一致,只需达到最终一致,比起强一致性那种刚性事务,采用柔性事务,在很多场景下更有利于我们去实现。

执行过程

在使用CAP框架前,先熟悉下作为分布式事务解决方案之一的本地消息表工作过程。

  1. 消息发起方(如图左侧部分)和消息接收方(如图右侧部分),先额外建一套消息表,用来记录及跟踪消息内容及状态。
  2. 当有请求到消息发起方时,处理完业务逻辑发布消息将业务数据和消息数据一同提交到本地表中,此时为本地事务。
  3. 本地事务没有问题后,将消息发送到MQ传递给消息消费方。如果消息发送失败,会进行重试发送。
  4. 消息消费方,接收并处理消息,完成自己的业务逻辑,此时为消息消费方本地事务,如果本地事务完成,则更改接收消息的状态,更改本地,如果处理失败,那么可再次重试执行。
  5. 最终,左侧事务与右侧事务达到最终一致。

CAP框架

CAP是一个在分布式系统中(SOA,MicroService)实现事件总线及最终一致性(分布式事务)的一个开源的 C# 库,具有轻量级,高性能,易使用等特点。

  • 具有 Event Bus 的所有功能,提供了更加简化的方式来处理EventBus中的发布/订阅。
  • 具有消息持久化的功能,当服务进行重启或者宕机时,可以保证消息的可靠性。
  • 基于本地消息表实现了分布式事务中的最终一致性。

  • 集成了可视化页面方便观察消息状态。

  • 提供了一系列Nuget包以选择需要的工具接入。

  1. 第一个包DotNetCore.CAP为必须要安装的。
  2. 可以依据消息队列的不同选择用RabbitMQ、Kafka或是AzureServiceBus等。
  3. 根据服务使用的数据库情况选择需要将本地消息表落库,可以选择SqlServer、MySql、PostgreSql、MongoDB等,或是直接使用内存存储,方便快速实践。

场景案例

依照EShopOnContainers中的一张图来实现一个例子,用户更新用户信息,将更新的部分通过事件发送到消息队列中,下游的购物车和订单服务侦听到消息,更改买家信息。

在此基础上行,设计三个上下文,并分别集成CAP,借助RabbitMQ作为消息队列,对于UserService、BasketService和OrderService,都直接使用了数据库(当然可以不仅限于数据库)。

服务建立

项目创建

开始建立几个服务,新建空白解决方案,依次建立三个WebApi项目,并移除默认的控制器。

简单设计下,在三个服务中创建三个DbContext,对应三个独立的数据库。

  • UserService中创建UserInfo实体及UserDbContext
  • BasketService中创建Basket实体及BasketDbContext
  • OrderService中创建Order实体及OrderDbContext

安装Nuget包

在三个服务中均安装完如下选中的包,此次Demo中为方便快速实践,选择RabbitMQ作为消息队列,MySql作为数据库存储。

对于EFCore及MySql包,安装了如下几个包

Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.Tools
Pomelo.EntityFrameworkCore.MySql

注意此处EFCore中MySql版本和CAP中MySql版本两者间依赖的MySqlConnector不一致会优点问题

配置服务

需要对CAP进行设置,比如使用的是什么数据库、什么消息队列及配置下消息队列参数,这一系列初始化设置在Startup.cs中配置好。

  1. ConfigureService中配置DbContext和CAP服务

  1. Configure中CAP的引入中间件

  1. 利用EFCore的迁移命令生成下数据库迁移脚本,将DbContext内实体生成到数据库中
  2. 单个服务启动后,CAP组件会将内置表创建到数据库中。

  1. 服务全部启动后,RabbitMQ Client会自动注册到RabbitMQ Server中同时创建好给定的Exchange(不给定则使用默认值),存在订阅的服务则注册队列绑定到给定的Exchange下。

发布事件

在 UserService中UserController 中注入ICapPublisher,使用Patch接口更新一个Address,然后使用ICapPublisher发布一条消息。

  1. 更新本地User表内信息。
  2. 借助_capPublisher发布事件,先将事件信息记录到本地MqPublish表。
  3. 前两步都是针对本地表操作,一个事务保证,写入MqPublish成功后再由CAP将记录发送到RabbitMQ中。

订阅事件

在BasketService和OrderService中完成事件的订阅。各自新建了一个Handler来处理消息。在Handler中对处理的方法加上CapSubscribe特性,其中监听的是发布事件时发送的事件名或消息名。

  1. BasketService收到RabbitMQ中的消息,CAP将消息写入到MqReceive中。
  2. 调用相应的Handler处理事件。
  3. 更新Basket本地表,本地事务完成被提交。
  4. CAP组件将本地的MqReceive相关记录更改状态到完成,如本地事务提交失败,则再次重试。

总结

抛弃强一致性想法借助最终一致性完成,将分布式事务拆分成多个本地事务进行处理。采用最终一致性来使得所有本地事务完成,即使部分出现失败,也可重试,如重试机制无效最终借助人力完成。

在异步场景下,CAP及其方便了我们去处理分布式事务的过程。

当前RabbitMQ场景下,当某个服务做多个部署时,同一个队列仍能保证一个消费者消费。这也避免了有些场景下,需要对资源加锁来防止同时消费场景。

本文Demo

参考

Savorboard:分布式事务解决方案CAP

Savorboard :分布式事务与解决方案

2021-04-28,望技术有成后能回来看见自己的脚步

Asp.Net Core&CAP实现分布式事务的更多相关文章

  1. 【转】.NET Core 事件总线,分布式事务解决方案:CAP

    [转].NET Core 事件总线,分布式事务解决方案:CAP 背景 相信前面几篇关于微服务的文章也介绍了那么多了,在构建微服务的过程中确实需要这么一个东西,即便不是在构建微服务,那么在构建分布式应用 ...

  2. C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志

    C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...

  3. asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程

    最近在学习张善友老师的NanoFabric 框架的时了解到Exceptionless : https://exceptionless.com/ !因此学习了一下这个开源框架!下面对Exceptionl ...

  4. .net core 下的分布式事务锁

    原文:.net core 下的分布式事务锁 目录 系统分布式锁的用法 锁的实现 锁的使用 API内的范例: 引用链接 系统分布式锁的用法 公司框架新增功能分布式锁: 锁的性能之王: 缓存 > Z ...

  5. 【转】asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程

    最近在学习张善友老师的NanoFabric 框架的时了解到Exceptionless : https://exceptionless.com/ !因此学习了一下这个开源框架!下面对Exceptionl ...

  6. ASP.NET Core中间件实现分布式 Session(转载)

    ASP.NET Core中间件实现分布式 Session 1. ASP.NET Core中间件详解 1.1. 中间件原理 1.1.1. 什么是中间件 1.1.2. 中间件执行过程 1.1.3. 中间件 ...

  7. .NET Core 事件总线,分布式事务解决方案:CAP

    背景 相信前面几篇关于微服务的文章也介绍了那么多了,在构建微服务的过程中确实需要这么一个东西,即便不是在构建微服务,那么在构建分布式应用的过程中也会遇到分布式事务的问题,那么 CAP 就是在这样的背景 ...

  8. .NET Core 事件总线,分布式事务解决方案:CAP 基于Kafka

    背景 相信前面几篇关于微服务的文章也介绍了那么多了,在构建微服务的过程中确实需要这么一个东西,即便不是在构建微服务,那么在构建分布式应用的过程中也会遇到分布式事务的问题,那么 CAP 就是在这样的背景 ...

  9. 【无私分享:ASP.NET CORE 项目实战(第十三章)】Asp.net Core 使用MyCat分布式数据库,实现读写分离

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 MyCat2.0版本很快就发布了,关于MyCat的动态和一些问题,大家可以加一下MyCat的官方QQ群:106088787.我 ...

随机推荐

  1. Vue学习笔记-Vue.js-2.X 学习(六)===>脚手架Vue-CLI(项目说明-Babel)

    五  Vue学习-vue-cli脚手架学习(创建只选一个选项:Babel) 1. 项目目录说明 node_modules : 包管理文件夹 public : 静态资源 src : 源代码 gitign ...

  2. Centos8.2安装Mongodb4.4.2(社区版)

    1:下载 wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel80-4.4.2.tgz 官网地址: 2:解压 tar -zxv ...

  3. Java基础语法:运算符

    Java 运算符(operator)根据功能分类: 算术运算符:+,-,*,/,%,++,-- 赋值运算符:= 关系运算符:>,<,>=,<=,==,!=,instanceof ...

  4. python的基本运算符

    目录 基本运算符 1.算术运算符 2.比较运算符 3.赋值运算符 4.逻辑运算符 5.身份运算符 6.位运算符 7.成员运算符 基本运算符 1.算术运算符 运算符 描述 实例 + 加-两个对象相加 a ...

  5. Go的包

    目录 go的包 一.包的创建规则 二.包的导入规则 三.包的函数调用 go的包 一.包的创建规则 一个包就是一个文件夹. 同一个包(文件夹)下,所有go文件都只能用同一个package,也就是每个文件 ...

  6. JUC-ThreadLocalRandom

    目录 Radndom类的局限性 ThreadLocalRandom 这个类是在JDK7中新增的随机数生成器,它弥补了Random类在多线程下的缺陷. Radndom类的局限性 在JDK7之前包括现在j ...

  7. vue封装一个弹框组件

    这是一个提示框和对话框,例:   这是一个组件 eject.vue <template> <div class='kz-cont' v-show='showstate'> &l ...

  8. [个人总结]pip安装tensorboard太慢

    在执行pip install语句的时候直接指定国内豆瓣的镜像源进行下载: pip install -i https://pypi.douban.com/simple 你想下载的包的名称 例如下载ten ...

  9. 元数据管理—动态表单设计器在crudapi系统中完整实现

    表单设计 在前面文章中,我们通过一系列案例介绍了表单设计的一些基本功能,表单设计起到非常重要作用,也是crudapi核心,所以本文会详细介绍表单设计中一些其它功能. 概要 表单字段column属性 列 ...

  10. 从sql语句的角度解刨SqlServer插入语句的并发问题

    今天收到一个小学弟的求助,数据库插入偶尔重复,怎么在sql语句上进行解决. Q:学长,我导入excel数据的操作,平时使用好好的,怎么突然发生插入重复的问题? A:你是使用哪个ORM框架进行操作的? ...