分布式事务开源框架 提供跨数据库访问,分布式事务TCC、SAGA解决方案

https://github.com/2881099/FreeSql.Cloud

提供跨数据库访问,分布式事务TCC、SAGA解决方案。

 

🦄 FreeSql.Cloud

为 FreeSql 提供跨数据库访问,分布式事务TCC、SAGA解决方案,支持 .NET Core 2.1+, .NET Framework 4.0+.

快速开始

dotnet add package FreeSql.Cloud

or

Install-Package FreeSql.Cloud

public enum DbEnum { db1, db2, db3 }

var fsql = new FreeSqlCloud<DbEnum>("myapp"); //提示:泛型可以传入 string
fsql.DistributeTrace = log => Console.WriteLine(log.Split('\n')[0].Trim());

fsql.Register(DbEnum.db1, () => new FreeSqlBuilder()
    .UseConnectionString(DataType.Sqlite, @"Data Source=db1.db")
    .Build());

fsql.Register(DbEnum.db2, () => new FreeSqlBuilder()
    .UseConnectionString(DataType.Sqlite, @"Data Source=db2.db")
    .Build());

fsql.Register(DbEnum.db3, () => new FreeSqlBuilder()
    .UseConnectionString(DataType.Sqlite, @"Data Source=db3.db")
    .Build());

FreeSqlCloud 必须定义成单例模式

如何使用?

FreeSqlCloud 的访问方式和 IFreeSql 一样:

fsql.Select<T>();
fsql.Insert<T>();
fsql.Update<T>();
fsql.Delete<T>();

//...

切换数据库:

fsql.Change(DbEnum.db3).Select<T>();
//以后所有 fsql.Select/Insert/Update/Delete 操作是 db3

关于分布式事务

1、简介

FreeSqlCloud 提供 TCC/SAGA 分布式事务调度,遇错重试、程序重启不影响的事务单元管理功能。

2、唯一标识

FreeSqlCloud 使用唯一标识区分,达到事务管理互不冲突的目的,举例:

var fsql = new FreeSqlCloud<DbEnum>("myapp");
var fsql2 = new FreeSqlCloud<DbEnum>("myapp2");

fsql2 访问不到 fsql 产生的事务,如果我们的 webapi 程序发布多实例,需要设置多个实例对应的 name,以作区分。

3、主库

fsql.Register 第一个注册的称之为【主库】,存储 TCC/SAGA 相关数据,当程序重新启动的时候,会将未处理完的事务载入内存重新调度。

自动创建表 tcc_myapp、saga_myapp:

提示:fsql2 会创建表 tcc_myapp2、saga_myapp2

字段名描述
tid 事务ID
title 事务描述,查看日志更直观
total 所有单元数量
create_time 创建时间
finish_time 完成时间
status Pending, Confirmed, Canceled, ManualOperation
max_retry_count 最大重试次数,如果仍然失败将转为【人工干预】
retry_interval 重试间隔(秒)
retry_count 已重试次数
retry_time 重试时间

自动创建表 tcc_myapp_unit、saga_myapp_unit:

提示:fsql2 会创建表 tcc_myapp2_unit、saga_myapp2_unit

字段名描述
tid 事务ID
index 单元下标,1到N
description 单元描述,使用 [Description("xx")] 特性设置,查看日志更直观
stage Try, Confirm, Cancel
type_name 对应 c# TccUnit/SagaUnit 反射类型信息,用于创建 TccUnit/SagaUnit 对象
state 状态数据
state_type_name 状态数据对应的 c# 反射类型信息
create_time 创建时间

4、单元

TccUnit、SagaUnit 内部支持调用 webapi/grpc,当调用异常触发重试调度。

由于网络不确定因素,较坏的情况比如单元调用 webapi/grpc 成功,但是 tcc_unit 表保存状态失败,单元又会进入重试调用,最终导致多次调用 webapi/grpc,所以 web/grpc 提供方应该保证幂等操作,无论多少次调用结果都一致。

TCC 事务

var tid = Guid.NewGuid().ToString();
await fsql
    .StartTcc(tid, "支付购买", 
        new TccOptions
        {
            MaxRetryCount = 10,
            RetryInterval = TimeSpan.FromSeconds(10)
        })
    .Then<Tcc1>(new LocalState { Id = 1, Name = "tcc1" })
    .Then<Tcc2>()
    .Then<Tcc3>(new LocalState { Id = 3, Name = "tcc3" })
    .ExecuteAsync();

// 状态数据
class LocalState
{
    public int Id { get; set; }
    public string Name { get; set; }
}
[Description("第1步")]
class Tcc1 : TccUnit<LocalState>
{
    public override Task Cancel() => throw new Exception("dkdkdk");
    public override Task Confirm() => Task.CompletedTask;
    public override Task Try() => Task.CompletedTask;
}
[Description("第2步")]
class Tcc2 : TccUnit<LocalState>
{
    public override Task Cancel() => Task.CompletedTask;
    public override Task Confirm() => Task.CompletedTask;
    public override Task Try() => Task.CompletedTask;
}
[Description("第3步")]
class Tcc3 : TccUnit<LocalState>
{
    public override Task Cancel() => Task.CompletedTask;
    public override Task Confirm() => Task.CompletedTask;
    public override Task Try() => throw new Exception("xxx");
}

执行结果:

2020-12-02 14:03:34 【myapp】db1 注册成功, 并存储 TCC/SAGA 事务相关数据
2020-12-02 14:07:31 【myapp】成功加载历史未完成 TCC 事务 0 个
2020-12-02 14:07:31 【myapp】成功加载历史未完成 SAGA 事务 0 个
2020-12-02 14:03:35 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Created successful, retry count: 10, interval: 10S
2020-12-02 14:03:35 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Unit1(第1步) TRY successful
2020-12-02 14:03:35 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Unit2(第2步) TRY successful
2020-12-02 14:03:35 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Unit3(第3步) TRY failed, ready to CANCEL, -ERR xxx
2020-12-02 14:03:35 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Unit2(第2步) CANCEL successful
2020-12-02 14:03:35 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Unit1(第1步) CANCEL failed , -ERR dkdkdk
2020-12-02 14:03:45 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Unit1(第1步) CANCEL failed retry again 1 times , -ERR dkdkdk
2020-12-02 14:03:55 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Unit1(第1步) CANCEL failed retry again 2 times , -ERR dkdkdk
2020-12-02 14:04:06 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Unit1(第1步) CANCEL failed retry again 3 times , -ERR dkdkdk
2020-12-02 14:04:16 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Unit1(第1步) CANCEL failed retry again 4 times , -ERR dkdkdk
2020-12-02 14:04:26 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Unit1(第1步) CANCEL failed retry again 5 times , -ERR dkdkdk
2020-12-02 14:04:36 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Unit1(第1步) CANCEL failed retry again 6 times , -ERR dkdkdk
2020-12-02 14:04:46 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Unit1(第1步) CANCEL failed retry again 7 times , -ERR dkdkdk
2020-12-02 14:04:57 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Unit1(第1步) CANCEL failed retry again 8 times , -ERR dkdkdk
2020-12-02 14:05:07 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Unit1(第1步) CANCEL failed retry again 9 times , -ERR dkdkdk
2020-12-02 14:05:17 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Unit1(第1步) CANCEL failed retry again 10 times , -ERR dkdkdk
2020-12-02 14:05:17 【myapp】TCC (5fec6379-d43e-4d5f-95a2-42ea8710f176, 支付购买) Not completed, waiting for manual operation 【人工干预】

Saga 事务

var tid = Guid.NewGuid().ToString();
await fsql
    .StartSaga(tid, "发表评论", 
        new SagaOptions
        {
            MaxRetryCount = 5,
            RetryInterval = TimeSpan.FromSeconds(5)
        })
    .Then<Saga1>(new LocalState { Id = 1, Name = "tcc1" })
    .Then<Saga2>()
    .Then<Saga3>(new LocalState { Id = 3, Name = "tcc3" })
    .ExecuteAsync();

// 状态数据
class LocalState
{
    public int Id { get; set; }
    public string Name { get; set; }
}
[Description("第1步")]
class Saga1 : SagaUnit<LocalState>
{
    public override Task Cancel() => throw new Exception("dkdkdk");
    public override Task Commit() => Task.CompletedTask;
}
[Description("第2步")]
class Saga2 : SagaUnit<LocalState>
{
    public override Task Cancel() => Task.CompletedTask;
    public override Task Commit() => Task.CompletedTask;
}
[Description("第3步")]
class Saga3 : SagaUnit<LocalState>
{
    public override Task Cancel() => Task.CompletedTask;
    public override Task Commit() => throw new Exception("xxx");
}

执行结果:

2020-12-02 14:07:30 【myapp】db1 注册成功, 并存储 TCC/SAGA 事务相关数据
2020-12-02 14:07:31 【myapp】成功加载历史未完成 TCC 事务 0 个
2020-12-02 14:07:31 【myapp】成功加载历史未完成 SAGA 事务 0 个
2020-12-02 14:07:31 【myapp】SAGA(e5469b8f-c27f-498a-a0f8-6dd128967dca, 发表评论) Created successful, retry count: 5, interval: 5S
2020-12-02 14:07:31 【myapp】SAGA(e5469b8f-c27f-498a-a0f8-6dd128967dca, 发表评论) Unit1(第1步) COMMIT successful
2020-12-02 14:07:31 【myapp】SAGA(e5469b8f-c27f-498a-a0f8-6dd128967dca, 发表评论) Unit2(第2步) COMMIT successful
2020-12-02 14:07:31 【myapp】SAGA(e5469b8f-c27f-498a-a0f8-6dd128967dca, 发表评论) Unit3(第3步) COMMIT failed, ready to CANCEL, -ERR xxx
2020-12-02 14:07:31 【myapp】SAGA(e5469b8f-c27f-498a-a0f8-6dd128967dca, 发表评论) Unit2(第1步) CANCEL successful
2020-12-02 14:07:31 【myapp】SAGA(e5469b8f-c27f-498a-a0f8-6dd128967dca, 发表评论) Unit1(第1步) CANCEL failed, -ERR dkdkdk
2020-12-02 14:07:36 【myapp】SAGA(e5469b8f-c27f-498a-a0f8-6dd128967dca, 发表评论) Unit1(第1步) CANCEL failed after 1 retries, -ERR dkdkdk
2020-12-02 14:07:41 【myapp】SAGA(e5469b8f-c27f-498a-a0f8-6dd128967dca, 发表评论) Unit1(第1步) CANCEL failed after 2 retries, -ERR dkdkdk
2020-12-02 14:07:47 【myapp】SAGA(e5469b8f-c27f-498a-a0f8-6dd128967dca, 发表评论) Unit1(第1步) CANCEL failed after 3 retries, -ERR dkdkdk
2020-12-02 14:07:52 【myapp】SAGA(e5469b8f-c27f-498a-a0f8-6dd128967dca, 发表评论) Unit1(第1步) CANCEL failed after 4 retries, -ERR dkdkdk
2020-12-02 14:07:57 【myapp】SAGA(e5469b8f-c27f-498a-a0f8-6dd128967dca, 发表评论) Unit1(第1步) CANCEL failed after 5 retries, -ERR dkdkdk
2020-12-02 14:07:57 【myapp】SAGA(e5469b8f-c27f-498a-a0f8-6dd128967dca, 发表评论) Not completed, waiting for manual
posted @ 2021-12-31 11:41  心冰之海  阅读(230)  评论(0编辑  收藏  举报