经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
.NET分布式Orleans - 6 - 事件溯源
来源:cnblogs  作者:chester·chen  时间:2024/3/29 11:34:47  对本文有异议

基本概念

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

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

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

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

工作原理

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

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

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

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

使用场景

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

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

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

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

优势

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

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

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

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

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

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

示例

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

首先需要安装必须的nuget包

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

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

  1. builder.Host.UseOrleans(static siloHostBuilder =>
  2. {
  3. var invariant = "System.Data.SqlClient";
  4. var connectionString = "Data Source=localhost\\SQLEXPRESS;Initial Catalog=orleanstest;User Id=sa;Password=12334;";
  5. siloHostBuilder.AddLogStorageBasedLogConsistencyProvider("LogStorage");
  6. // Use ADO.NET for clustering
  7. siloHostBuilder.UseAdoNetClustering(options =>
  8. {
  9. options.Invariant = invariant;
  10. options.ConnectionString = connectionString;
  11. }).ConfigureLogging(logging => logging.AddConsole());
  12. siloHostBuilder.Configure<ClusterOptions>(options =>
  13. {
  14. options.ClusterId = "my-first-cluster";
  15. options.ServiceId = "SampleApp";
  16. });
  17. // Use ADO.NET for persistence
  18. siloHostBuilder.AddAdoNetGrainStorage("GrainStorageForTest", options =>
  19. {
  20. options.Invariant = invariant;
  21. options.ConnectionString = connectionString;
  22. //options.GrainStorageSerializer = new JsonGrainStorageSerializer()
  23. });
  24. });

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

  1. // the classes below represent events/transactions on the account
  2. // all fields are user-defined (none have a special meaning),
  3. // so these can be any type of object you like, as long as they are serializable
  4. // (so they can be sent over the wire and persisted in a log).
  5. [Serializable]
  6. [GenerateSerializer]
  7. public abstract class Transaction
  8. {
  9. /// <summary> A unique identifier for this transaction </summary>
  10. [Id(0)]
  11. public Guid Guid { get; set; }
  12. /// <summary> A description for this transaction </summary>
  13. [Id(1)]
  14. public string Description { get; set; }
  15. /// <summary> time on which the request entered the system </summary>
  16. [Id(2)]
  17. public DateTime IssueTime { get; set; }
  18. }
  19. [Serializable]
  20. [GenerateSerializer]
  21. public class DepositTransaction : Transaction
  22. {
  23. [Id(0)]
  24. public uint DepositAmount { get; set; }
  25. }
  26. [Serializable]
  27. [GenerateSerializer]
  28. public class WithdrawalTransaction : Transaction
  29. {
  30. [Id(0)]
  31. public uint WithdrawalAmount { get; set; }
  32. }

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

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

  1. /// <summary>
  2. /// An example of a journaled grain that models a bank account.
  3. ///
  4. /// Configured to use the default storage provider.
  5. /// Configured to use the LogStorage consistency provider.
  6. ///
  7. /// This provider persists all events, and allows us to retrieve them all.
  8. /// </summary>
  9.  
  10. /// <summary>
  11. /// A grain that models a bank account
  12. /// </summary>
  13. public interface IAccountGrain : IGrainWithStringKey
  14. {
  15. Task<uint> Balance();
  16. Task Deposit(uint amount, Guid guid, string desc);
  17. Task<bool> Withdraw(uint amount, Guid guid, string desc);
  18. Task<IReadOnlyList<Transaction>> GetTransactionLog();
  19. }
  20. [StorageProvider(ProviderName = "GrainStorageForTest")]
  21. [LogConsistencyProvider(ProviderName = "LogStorage")]
  22. public class AccountGrain : JournaledGrain<AccountGrain.GrainState, Transaction>, IAccountGrain
  23. {
  24. /// <summary>
  25. /// The state of this grain is just the current balance.
  26. /// </summary>
  27. [Serializable]
  28. [Orleans.GenerateSerializer]
  29. public class GrainState
  30. {
  31. [Orleans.Id(0)]
  32. public uint Balance { get; set; }
  33. public void Apply(DepositTransaction d)
  34. {
  35. Balance = Balance + d.DepositAmount;
  36. }
  37. public void Apply(WithdrawalTransaction d)
  38. {
  39. if (d.WithdrawalAmount > Balance)
  40. throw new InvalidOperationException("we make sure this never happens");
  41. Balance = Balance - d.WithdrawalAmount;
  42. }
  43. }
  44. public Task<uint> Balance()
  45. {
  46. return Task.FromResult(State.Balance);
  47. }
  48. public Task Deposit(uint amount, Guid guid, string description)
  49. {
  50. RaiseEvent(new DepositTransaction()
  51. {
  52. Guid = guid,
  53. IssueTime = DateTime.UtcNow,
  54. DepositAmount = amount,
  55. Description = description
  56. });
  57. // we wait for storage ack
  58. return ConfirmEvents();
  59. }
  60. public Task<bool> Withdraw(uint amount, Guid guid, string description)
  61. {
  62. // if the balance is too low, can't withdraw
  63. // reject it immediately
  64. if (State.Balance < amount)
  65. return Task.FromResult(false);
  66. // use a conditional event for withdrawal
  67. // (conditional events commit only if the version hasn't already changed in the meantime)
  68. // this is important so we can guarantee that we never overdraw
  69. // even if racing with other clusters, of in transient duplicate grain situations
  70. return RaiseConditionalEvent(new WithdrawalTransaction()
  71. {
  72. Guid = guid,
  73. IssueTime = DateTime.UtcNow,
  74. WithdrawalAmount = amount,
  75. Description = description
  76. });
  77. }
  78. public Task<IReadOnlyList<Transaction>> GetTransactionLog()
  79. {
  80. return RetrieveConfirmedEvents(0, Version);
  81. }
  82. }

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

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

 

原文链接:https://www.cnblogs.com/chenyishi/p/18099103

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号