需要注意的是标题中的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. CSS过渡约束的计算

    CSS过度约束性质 什么是CSS过度约束 当没有开启绝对定位或固定定位时 水平布局必须要满足以下等式 探寻能够设置成auto的CSS属性 等式不成立(过度约束)时的几种情况 当margin与width ...

  2. HTML:HTML基础

    HTML不是一门编程语言,而是一种用于定义内容结构的标记语言.HTML由一系列元素(elements)组成,这些元素可以用来包围不同部分的内容,使其以某种方式呈现或工作.一对标签可以为一段文字或者一张 ...

  3. MySQL索引由浅入深

    索引是SQL优化中最重要的手段之一,本文从基础到原理,带你深度掌握索引. 一.索引基础 1.什么是索引 MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构,索引对于 ...

  4. Java实现文件的读写

    需求:实现基本的读写 package com.sbx.io; import java.io.File; import java.io.FileReader; import java.io.FileWr ...

  5. Excel_不打开文件进行跨工作簿查询

    在使用Excel时,我们经常会遇到这种问题,我的数据源在表1里面,但是我要在表2里面做报表,用lookup和offset等公式都需要打开表1操作,否则就会报错.那么有没有办法在不打开表1的情况下在表2 ...

  6. 【DB宝42】MySQL高可用架构MHA+ProxySQL实现读写分离和负载均衡

    目录 一.MHA+ProxySQL架构 二.快速搭建MHA环境 2.1 下载MHA镜像 2.2 编辑yml文件,创建MHA相关容器 2.3 安装docker-compose软件(若已安装,可忽略) 2 ...

  7. spring-cloud-alibaba之Nacos

    在微服务构架中,集群服务间的需要调用时就需要知道各个服务的IP和提供服务的端口等信息,如果每个部署一个服务就配置一次,那么必然时非常麻烦的,因此我们需要一个能够统一管理的东西来解决这个问题,由此诞生了 ...

  8. 关闭ubuntu防火墙

    1.关闭ubuntu的防火墙 ufw disable 开启防火墙 ufw enable 2.卸载了iptables apt-get remove iptables 3.关闭ubuntu中的防火墙的其余 ...

  9. HDU_3359 Kind of a Blur 【浮点型高斯消元+BFS】

    一.题目 Kind of a Blur 二.分析 题目读起来挺费劲的. 主要就是要求一个矩阵,其中每个点及其于这个的曼哈顿距离小于D的点的值总和的平均值就是新生成的矩阵. 给定新生成的矩阵,求初始矩阵 ...

  10. React实用技巧

    取消请求 React 中当前正在发出请求的组件从页面上卸载了,理想情况下这个请求也应该取消掉,那么如何把请求的取消和页面的卸载关联在一起呢? 这里要考虑利用 useEffect 传入函数的返回值: u ...