.NET分布式Orleans - 6 - 事件溯源

这篇具有很好参考价值的文章主要介绍了.NET分布式Orleans - 6 - 事件溯源。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

基本概念

事件溯源(Event Sourcing)是一种设计模式,它记录并存储了应用程序状态变化的所有事件。

其核心思想是将系统中的每次状态变化都视为一个事件,并将这些事件以时间顺序的方式持久化存储。

这样,通过重放这些事件,我们可以重建系统在任何特定时间点的状态。

每个事件通常都包含了描述状态变化的必要信息,以及发生状态变化的原因和时间戳。

工作原理

工作原理方面,事件溯源主要依赖于两个关键部分:事件生成和事件存储。

当系统中发生状态变化时,会生成一个或多个事件,这些事件随后被存储到事件存储中。

事件存储需要设计成高可用、高一致且可伸缩的,以支持大规模的系统操作。

之后,当需要重建系统状态时,只需从事件存储中按顺序读取事件,并依次应用这些事件到系统状态即可。

使用场景

在Orleans7中,事件溯源主要应用在以下几个场景:

  1. 分布式系统状态同步:在分布式系统中,各个节点之间的状态同步是一个重要问题。通过事件溯源,每个节点都可以记录并发送自己的状态变化事件,其他节点则可以通过订阅这些事件来同步自己的状态。

  2. 历史数据追踪和审计:在某些业务场景下,需要追踪系统的历史操作记录,以进行审计或分析。事件溯源提供了完整的操作历史,可以方便地查询和回放历史事件。

  3. 容错和恢复:当系统发生故障时,通过事件溯源可以方便地恢复到故障发生前的状态,或者根据事件日志进行故障排查。

优势

事件溯源在Orleans7中带来了以下优势:

  1. 数据完整性和一致性:由于事件溯源记录了所有状态变化的历史,因此可以确保数据的完整性和一致性。

  2. 灵活性和可扩展性:事件溯源的设计使得系统可以很容易地添加新的状态变化事件,同时也支持大规模的系统扩展。

  3. 容错和恢复能力:通过事件溯源,可以轻松地恢复到系统的任何历史状态,大大提高了系统的容错和恢复能力。

  4. 清晰的业务逻辑:每个事件都代表了一个具体的业务操作,因此通过查看事件日志,可以清晰地了解系统的业务逻辑和操作流程。

总的来说,事件溯源是一种强大而灵活的设计模式,它在Orleans7中的应用为分布式系统带来了诸多优势。对于软件开发者来说,理解和掌握事件溯源机制,将有助于构建更加健壮、可靠和可扩展的分布式系统。

示例

下面使用事件溯源,来跟踪一个账户的变更记录。

首先需要安装必须的nuget包

<PackageReference Include="Microsoft.Orleans.EventSourcing" Version="8.0.0" />
<PackageReference Include="Microsoft.Orleans.Clustering.AdoNet" Version="8.0.0" />
<PackageReference Include="Microsoft.Orleans.Persistence.AdoNet" Version="8.0.0" />
<PackageReference Include="Microsoft.Orleans.Server" Version="8.0.0" />

然后设置Orleans,除了Orleans的常规设置外,还需要 siloHostBuilder.AddLogStorageBasedLogConsistencyProvider("LogStorage") 来设置LogConsistencyProvider

builder.Host.UseOrleans(static siloHostBuilder =>
{
    var invariant = "System.Data.SqlClient";

    var connectionString = "Data Source=localhost\\SQLEXPRESS;Initial Catalog=orleanstest;User Id=sa;Password=12334;";

    siloHostBuilder.AddLogStorageBasedLogConsistencyProvider("LogStorage");

    // Use ADO.NET for clustering
    siloHostBuilder.UseAdoNetClustering(options =>
    {
        options.Invariant = invariant;
        options.ConnectionString = connectionString;
    }).ConfigureLogging(logging => logging.AddConsole());

    siloHostBuilder.Configure<ClusterOptions>(options =>
    {
        options.ClusterId = "my-first-cluster";
        options.ServiceId = "SampleApp";
    });

    // Use ADO.NET for persistence
    siloHostBuilder.AddAdoNetGrainStorage("GrainStorageForTest", options =>
    {
        options.Invariant = invariant;
        options.ConnectionString = connectionString;
        //options.GrainStorageSerializer = new JsonGrainStorageSerializer()
     
    });
});

定义账户的存储和提取事件类

// the classes below represent events/transactions on the account
// all fields are user-defined (none have a special meaning),
// so these can be any type of object you like, as long as they are serializable
// (so they can be sent over the wire and persisted in a log).

[Serializable]
[GenerateSerializer]
public abstract class Transaction
{
    /// <summary> A unique identifier for this transaction  </summary>
    [Id(0)]
    public Guid Guid { get; set; }

    /// <summary> A description for this transaction  </summary>
    [Id(1)]
    public string Description { get; set; }

    /// <summary> time on which the request entered the system  </summary>
    [Id(2)]
    public DateTime IssueTime { get; set; }
}

[Serializable]
[GenerateSerializer]
public class DepositTransaction : Transaction
{
    [Id(0)]
    public uint DepositAmount { get; set; }
}

[Serializable]
[GenerateSerializer]
public class WithdrawalTransaction : Transaction
{
    [Id(0)]
    public uint WithdrawalAmount { get; set; }
}

再定义账户的Grain,其中有存钱,取钱,获取余额,与变更记录操作

Grain类必须具有 LogConsistencyProviderAttribute 才能指定日志一致性提供程序。 还需要 StorageProviderAttribute设置存储。

/// <summary>
/// An example of a journaled grain that models a bank account.
/// 
/// Configured to use the default storage provider.
/// Configured to use the LogStorage consistency provider.
/// 
/// This provider persists all events, and allows us to retrieve them all.
/// </summary>

/// <summary>
/// A grain that models a bank account
/// </summary>
public interface IAccountGrain : IGrainWithStringKey
{
    Task<uint> Balance();

    Task Deposit(uint amount, Guid guid, string desc);

    Task<bool> Withdraw(uint amount, Guid guid, string desc);

    Task<IReadOnlyList<Transaction>> GetTransactionLog();
}

[StorageProvider(ProviderName = "GrainStorageForTest")]
[LogConsistencyProvider(ProviderName = "LogStorage")]

public class AccountGrain : JournaledGrain<AccountGrain.GrainState, Transaction>, IAccountGrain
{
    /// <summary>
    /// The state of this grain is just the current balance.
    /// </summary>
    [Serializable]
    [Orleans.GenerateSerializer]
    public class GrainState
    {
        [Orleans.Id(0)]
        public uint Balance { get; set; }

        public void Apply(DepositTransaction d)
        {
            Balance = Balance + d.DepositAmount;
        }

        public void Apply(WithdrawalTransaction d)
        {
            if (d.WithdrawalAmount > Balance)
                throw new InvalidOperationException("we make sure this never happens");

            Balance = Balance - d.WithdrawalAmount;
        }
    }

    public Task<uint> Balance()
    {
        return Task.FromResult(State.Balance);
    }

    public Task Deposit(uint amount, Guid guid, string description)
    {
        RaiseEvent(new DepositTransaction()
        {
            Guid = guid,
            IssueTime = DateTime.UtcNow,
            DepositAmount = amount,
            Description = description
        });

        // we wait for storage ack
        return ConfirmEvents();
    }

    public Task<bool> Withdraw(uint amount, Guid guid, string description)
    {
        // if the balance is too low, can't withdraw
        // reject it immediately
        if (State.Balance < amount)
            return Task.FromResult(false);

        // use a conditional event for withdrawal
        // (conditional events commit only if the version hasn't already changed in the meantime)
        // this is important so we can guarantee that we never overdraw
        // even if racing with other clusters, of in transient duplicate grain situations
        return RaiseConditionalEvent(new WithdrawalTransaction()
        {
            Guid = guid,
            IssueTime = DateTime.UtcNow,
            WithdrawalAmount = amount,
            Description = description
        });
    }

    public Task<IReadOnlyList<Transaction>> GetTransactionLog()
    {
        return RetrieveConfirmedEvents(0, Version);
    }
}

最后即可通过client生成grain,并获取账户变动记录

var palyer = client.GetGrain<IAccountGrain>("zhangsan");
await palyer.Deposit(1000, Guid.NewGuid(), "aaa");
var logs = await palyer.GetTransactionLog();
return Results.Ok(logs);

 文章来源地址https://www.toymoban.com/news/detail-844004.html

到了这里,关于.NET分布式Orleans - 6 - 事件溯源的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • .NET分布式Orleans - 2 - Grain的通信原理与定义

    Grain 是 Orleans 框架中的基本单元,代表了应用程序中的一个实体或者一个计算单元。 每个Silo都是一个独立的进程,Silo负责加载、管理和执行Grain实例,并处理来自客户端的请求以及与其他Silo之间的通信。 通信原理 在相同的Silo中,Grain与Grain之间的通信通过直接的方法调用实

    2024年03月24日
    浏览(60)
  • Orleans 微软基于 Actor 的分布式框架

            Actor模型是一种并发编程模型,它基于消息传递实现,是一种轻量级的并发模型。在Actor模型中,每个Actor都是一个独立的执行单元,它可以接收和发送消息,并且可以执行一些本地操作,但是不能直接访问其他Actor的状态。 Actor模型的基本工作原理如下: 1.每个

    2024年02月17日
    浏览(40)
  • 【分布式和微服务1】一篇文章详细了解分布式和微服务的基本概念

    🍀 通俗一点说,高可用的意思是:在 高 并发的情况下,系统仍然是 可用 的 🍀 高可用的目的:保障业务的连续性( 实现在用户眼里,业务永远是正常对外提供服务的 ) 🍀 🍬 【上图】一个 SpringBoot 项目( apple.jar )被部署到服务器上运行,可向其发送 网络请求 对 数据

    2024年02月02日
    浏览(69)
  • Git 分布式版本控制系统基本概念和操作命令

    目录 Git 基本概念 功能特点 工作流程 操作命令 新建代码库 配置 增删文件 代码提交 分支 标签 查看信息 远程同步 撤销 其他 小结 Git 是一个开源的分布式版本控制系统,用于跟踪文件的变更历史。它最初由 Linux Torvalds 设计,用于 Linux 内核的开发,但由于其强大的功能和灵

    2024年03月27日
    浏览(65)
  • Git简介与工作原理:了解Git的基本概念、版本控制系统和分布式版本控制的工作原理

    🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐 🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬

    2024年02月16日
    浏览(68)
  • 分布式系统概念和设计——分布式共享内存

    分布式共享内存 分布式共享内存是在不共享物理内存的计算机之间实现数据的共享的一个抽象。 有一个底层运行的系统保证其透明性,但是进程还是根据内存的分布处理物理内存的分布式能力 DMS最关键点: 不需要关心数据的通信,消息传递能力是巨大的底层支持。 生存周

    2024年02月10日
    浏览(54)
  • SpringCloud溯源——从单体架构到微服务Microservices架构 & 分布式和微服务 & 为啥要用微服务

    单体架构好好的,为啥要用微服务呢?微服务究竟是啥,怎么来的,有啥优缺点,本篇博客尝试追根溯源,阐述单体应用到分布式,微服务的演变,微服务架构的定义及优缺点,厘清相关的概念。 1.网络架构变迁史:单体—SOA----微服务; 2.分布式系统,多节点,springcloud是第二

    2024年02月06日
    浏览(42)
  • 分布式锁实现(mysql,以及redis)以及分布式的概念

    我旁边的一位老哥跟我说,你知道分布式是是用来干什么的嘛?一句话给我干懵了,我能隐含知道,大概是用来做分压处理的,并增加系统稳定性的。但是具体如何,我却道不出个1,2,3。现在就将这些做一个详细的总结。至少以后碰到面试官可以说上个123。 那么就正式进入

    2024年01月21日
    浏览(61)
  • 分布式基础概念

    微服务架构风格,就是把一个单体架构按照业务拆分成多个服务模块,每个模块之间独立部署运行、互不影响,并使用轻量级机制通信,通常是 HTTP API。 集群是个物理形态,分布式是个工作方式。 只要是一堆机器,就可以叫集群,他们是不是一起协作着干活,这个谁也不知

    2024年02月07日
    浏览(41)
  • 分布式概念

    1.1 CAP定理   在分布式系统中,一个Web应用最多只能同时支持的两个属性: 一致性(C): 在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本) 可用性(A): 在集群中一部分节点故障后,集群整体是否还能响应客户

    2024年01月19日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包