需要注意的是标题中的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. ADT基础(一)—— List,Stack,and Queue

    ADT基础(一)-- List,Stack,and Queue 1 List 表示 数组:易于search,难于insert和remove 链表:难于search,易于insert和remove // ...

  2. Java方法详解

    Java方法详解 什么是方法? Java方法是语句的集合,它们在一起执行一个功能. 方法是解决一类问题的步骤的有序组合 方法包含于类或对象中 方法在程序中被创建,在其他地方被引用 示例: packag ...

  3. MySql_176. 第二高的薪水 + limit + distinct + null

    MySql_176. 第二高的薪水 LeetCode_MySql_176 题目描述 题解分析 代码实现 # Write your MySQL query statement below select( ...

  4. 剑指 Offer 46. 把数字翻译成字符串 + 动态规划

    剑指 Offer 46. 把数字翻译成字符串 Offer_46 题目描述 题解分析 本题的解题思路是使用动态规划,首先得出递推公式如下 dp[i] = dp[i-1]+dp[i-2](如果s[i-1] ...

  5. POJ-2516(最小费用最大流+MCMF算法)

    Minimum Cost POJ-2516 题意就是有n个商家,有m个供货商,然后有k种商品,题目求的是满足商家的最小花费供货方式. 对于每个种类的商品k,建立一个超级源点和一个超级汇点.每个商家和源 ...

  6. hive学习笔记之一:基本数据类型

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  7. 【odoo14】第二章、管理odoo实例

    本章主要介绍肖odoo实例添加用户自定义的模块.你可以从多个路径载入模块.但是建议你将自己的模块儿放在特定的目录当中,避免与odoo的核心模块混淆. 在这一章节,中我们主要涉及以下内容: 配置插件路径 ...

  8. Lzzy高级语言程序设计之for循环

    public class Mq { public static void main(String[]args){ int a = 3 ; a++;// do{ System.out.println(& ...

  9. Spring Boot 自动配置 源码分析

    Spring Boot 最大的特点(亮点)就是自动配置 AutoConfiguration 下面,先说一下 @EnableAutoConfiguration ,然后再看源代码,到底自动配置是怎么配置的 ...

  10. 正则表达式-Python实现

    1.概述: Regular Expression.缩写regex,regexp,R等: 正则表达式是文本处理极为重要的工具.用它可以对字符串按照某种规则进行检索,替换. Shell编程和高级编程语言中 ...