• 《通过C#学Proto.Actor模型》之Persistence


    Actor是有状态的,当每一步执行失败后,返回失败地方继续执行时,希望此时的状态是正确的,为了保证这一点,持久化就成了必要的环节了。

    Proto.Actor提供了三种方式执久化:

    • Event Sourcing事件溯源
    • Snapshotting快照
    • Event Sourcing with Snapshotting带快照的事件溯源
    不管是那种持久化方式,首先要构造一个持久化的提供者,这个提者是内存也好,数据库也罢,本例中用Sqlite作为持久化的载体;在Actor中,实现持久化,首先要创建一个Persistence对象,用来将快照或事件保存起来,最重要的一点是,我们用事件溯源或快照,是帮我们保留住Actor在某刻的状,保留下来,以便我们再次启动时能延续这个状态,所以Persistence有一个很关键的作用就是能从持久化的载体中把原来的状态回复过来,这里,Event Source的把原来的状态步骤走再走一次,到达当前流程的点,但快照不然,直接取的是最后时刻的状态;带快照的事件溯源则是两者的结合。
    码友看码:

    NuGet安装

    Proto.Actor

    Proto.Persistence

    Proto.Persistence.Sqlite

      1 using Microsoft.Data.Sqlite;
      2 using Proto;
      3 using Proto.Persistence;
      4 using Proto.Persistence.SnapshotStrategies;
      5 using Proto.Persistence.Sqlite;
      6 using System;
      7 using System.Threading.Tasks;
      8  
      9 namespace P008_Persistence
     10 {
     11     class Program
     12     {
     13         static void Main(string[] args)
     14         {
     15             //用sqlite持久化后
     16             var actorid = "myactorid";
     17             var dbfile = @"C:MyFileSourceReposProtoActorSampleProtoActorSampleP008_Persistencedata.sqlite";
     18             var sqliteProvider = new SqliteProvider(new SqliteConnectionStringBuilder() { DataSource = dbfile });
     19             while (true)
     20             {
     21                 Console.WriteLine("1、事件溯源   2、快照   3、带快照的事件溯源  4、退出");
     22                 switch (Console.ReadLine())
     23                 {
     24                     case "1":
     25                         CallEventSource(actorid, sqliteProvider);
     26                         break;
     27                     case "2":
     28                         CallSnapShoot(actorid, sqliteProvider);
     29                         break;
     30                     case "3":
     31                         CallSnapShootEventSource(actorid, sqliteProvider);
     32                         break;
     33                     case "4":
     34                         return;
     35                 }
     36             }
     37         }
     38         /// <summary>
     39         /// 事件溯源
     40         /// </summary>
     41         /// <param name="actorid"></param>
     42         /// <param name="sqliteProvider"></param>
     43         private static void CallEventSource(string actorid, SqliteProvider sqliteProvider)
     44         {          
     45             var props = Actor.FromProducer(() => new EventSourceDataActor(sqliteProvider, actorid));
     46             var pid = Actor.Spawn(props);
     47             var result = true;
     48             while (result)
     49             {
     50                 Console.WriteLine("1、Tell  2、删除持久化  3、退出");
     51  
     52                 switch (Console.ReadLine())
     53                 {
     54                     case "1":
     55                         var random = new Random();
     56                         var no = random.Next(5, 15);
     57                         Console.WriteLine($"随机产生的数字:{no}");
     58                         pid.Tell(new Data { Amount = no });
     59                         break;
     60                     case "2":
     61                         //完成处理后清理持久化的操作          
     62                         sqliteProvider.DeleteEventsAsync(actorid, 100).Wait();
     63                         break;
     64                     case "3":
     65                         result = false;
     66                         break;
     67                 }
     68             }       
     69         }
     70  
     71         /// <summary>
     72         /// 快照
     73         /// </summary>
     74         /// <param name="actorid"></param>
     75         /// <param name="sqliteProvider"></param>
     76         private static void CallSnapShoot(string actorid, SqliteProvider sqliteProvider)
     77         {
     78             var props = Actor.FromProducer(() => new SnapShootDataActor(sqliteProvider, actorid));
     79             var pid = Actor.Spawn(props);
     80             var result = true;
     81             while (result)
     82             {
     83                 Console.WriteLine("1、Tell  2、删除持久化  3、退出");
     84  
     85                 switch (Console.ReadLine())
     86                 {
     87                     case "1":
     88                         var random = new Random();
     89                         var no = random.Next(5, 15);
     90                         Console.WriteLine($"随机产生的数字:{no}");
     91                         pid.Tell(new Data { Amount = no });
     92                         break;
     93                     case "2":
     94                         //完成处理后清理持久化的操作          
     95                         sqliteProvider.DeleteEventsAsync(actorid, 100).Wait();
     96                         break;
     97                     case "3":
     98                         result = false;
     99                         break;
    100                 }
    101             }
    102             
    103         }
    104         /// <summary>
    105         /// 快照事件溯源
    106         /// </summary>
    107         /// <param name="actorid"></param>
    108         /// <param name="sqliteProvider"></param>
    109         private static void CallSnapShootEventSource(string actorid, SqliteProvider sqliteProvider)
    110         {
    111             var props = Actor.FromProducer(() => new SnapShootEventSourceDataActor(sqliteProvider, sqliteProvider, actorid));
    112             var pid = Actor.Spawn(props);
    113             var result = true;
    114             while (result)
    115             {
    116                 Console.WriteLine("1、Tell  2、删除持久化  3、退出");
    117  
    118                 switch (Console.ReadLine())
    119                 {
    120                     case "1":
    121                         var random = new Random();
    122                         var no = random.Next(5, 15);
    123                         Console.WriteLine($"随机产生的数字:{no}");
    124                         pid.Tell(new Data { Amount = no });
    125                         break;
    126                     case "2":
    127                         //完成处理后清理持久化的操作          
    128                         sqliteProvider.DeleteEventsAsync(actorid, 100).Wait();
    129                         sqliteProvider.DeleteSnapshotsAsync(actorid, 100).Wait();
    130                         break;
    131                     case "3":
    132                         result = false;
    133                         break;
    134                 }
    135             }         
    136         }
    137     }
    138  
    139     public class Data
    140     {
    141         public long Amount { get; set; }
    142     }
    143  
    144     #region 事件溯源
    145     public class EventSourceDataActor : IActor
    146     {
    147         private long _value = 0;
    148         private readonly Persistence _persistence;
    149  
    150         public EventSourceDataActor(IEventStore eventStore, string actorId)
    151         {
    152             //事件溯源持久化方式
    153             _persistence = Persistence.WithEventSourcing(eventStore, actorId, ApplyEvent);
    154         }
    155         private void ApplyEvent(Proto.Persistence.Event @event)
    156         {
    157             switch (@event.Data)
    158             {
    159                 case Data msg:
    160                     _value = _value + msg.Amount;
    161                     Console.WriteLine($"累计:{_value}");
    162                     break;
    163             }
    164         }
    165         public async Task ReceiveAsync(IContext context)
    166         {
    167             switch (context.Message)
    168             {
    169                 case Started _:
    170                     await _persistence.RecoverStateAsync();
    171                     break;
    172                 case Data msg:
    173                     await _persistence.PersistEventAsync(new Data { Amount = msg.Amount });
    174                     break;
    175             }
    176         }
    177     }
    178     #endregion
    179  
    180     #region 快照
    181     public class SnapShootDataActor : IActor
    182     {
    183         private long _value = 0;
    184         private readonly Persistence _persistence;
    185  
    186         public SnapShootDataActor(ISnapshotStore snapshotStore, string actorId)
    187         {
    188             //快照持久化方式
    189             _persistence = Persistence.WithSnapshotting(snapshotStore, actorId, ApplySnapshot);
    190         }
    191         private void ApplySnapshot(Proto.Persistence.Snapshot snapshot)
    192         {
    193             switch (snapshot.State)
    194             {
    195                 case long value:
    196                     _value = value;
    197                     Console.WriteLine($"累计:{_value}");
    198                     break;
    199             }
    200         }
    201         public async Task ReceiveAsync(IContext context)
    202         {
    203             switch (context.Message)
    204             {
    205                 case Started _:
    206                     await _persistence.RecoverStateAsync();
    207                     break;
    208                 case Data msg:
    209                     _value = _value + msg.Amount;
    210                     await _persistence.DeleteSnapshotsAsync(100);
    211                     await _persistence.PersistSnapshotAsync(_value);
    212                     break;
    213             }
    214         }
    215     }
    216     #endregion
    217  
    218     #region 事件溯源and快照
    219     public class SnapShootEventSourceDataActor : IActor
    220     {
    221         private long _value = 0;
    222         private readonly Persistence _persistence;
    223  
    224         public SnapShootEventSourceDataActor(IEventStore  eventStore, ISnapshotStore snapshotStore, string actorId)
    225         {
    226             //注释快照策略
    227             //_persistence = Persistence.WithEventSourcingAndSnapshotting(eventStore, snapshotStore, actorId, ApplyEvent, ApplySnapshot, new IntervalStrategy(5), () => { return _value; });
    228             //无快照策略
    229             _persistence = Persistence.WithEventSourcingAndSnapshotting(eventStore, snapshotStore, actorId, ApplyEvent, ApplySnapshot);
    230         }
    231         private void ApplyEvent(Proto.Persistence.Event @event)
    232         {
    233             switch (@event.Data)
    234             {
    235                 case Data msg:
    236                     _value = _value + msg.Amount;
    237                     Console.WriteLine($"事件溯源累计:{_value}");
    238                     break;
    239             }
    240         }
    241         private void ApplySnapshot(Proto.Persistence.Snapshot snapshot)
    242         {
    243             switch (snapshot.State)
    244             {
    245                 case long value:
    246                     _value = value;
    247                     Console.WriteLine($"快照累计:{_value}");
    248                     break;
    249             }
    250         }
    251         public async Task ReceiveAsync(IContext context)
    252         {
    253             switch (context.Message)
    254             {
    255                 case Started _:
    256                     await _persistence.RecoverStateAsync();
    257                     break;
    258                 case Data msg:
    259                     await _persistence.PersistEventAsync(new Data { Amount = msg.Amount });
    260                     //无快照策略时启用
    261                     await _persistence.PersistSnapshotAsync(_value);
    262                     break;
    263             }
    264         }
    265     }
    266     #endregion
    267 }
    通过代码看到,持久化是通过在Actor中定义Persistence时,关联一个参数为,Event或Snapshot的方法,并且Actor的Receive方法在Stared到达是恢复(从持久载体中读取数据来恢复),在具体消息到达时,调用Persistence.PersistEventAsync或Persistence.PersisSnapshotAsync来持久化状态数据,这两个方法,都会把调用似递到Persistence产生是关联的那个方法,并把消息实体类通过Event.Data或Snapshot.State传递进去。
     此例分别演示了事件溯源,快照,带快照事件溯源,例子很简单,就是把每次产生的随机数累加起来
    1、事件溯源

    三个绿色箭头,意思是进了三次“1、事件溯源”这个选项
    三次蓝色箭头,意思是调用了三次Tell方法,用来获取三次随机数,蓝色椭圆是产生的三个数字,分别是7,7,6,蓝色方框是累计结果,从上往下,第一次是(0+7)7,第二次是(7+7)14,第三次是(14+6)20
    红色箭头是退出事件溯源的方法,返回上一级
    绿色方框是绿色箭头再次进入,自动恢复,事件溯源后的结果(即从持久化载体中把之前的所有事件重新走一次)还是之前退出时的结果,累计20,所以不管这个Actor在什么地方退出,再次运行,都会把之前的补运行回来。
    也可以打开sqlite库进行查看保存的事件结果
    2、 快照

    快照与事件溯源类似,差别在于每次再次进来,只取上次退出时的结果,同时,在数据里,只保存了最后一次的结果。
     
    3、带快照的事件溯源

    与快照类似,上面代码我们是一个事件,一个快照。 
    官方给出带快照的事件可以通过快照策略来保存快照
    在创建持久化对象时,可以添加快照策略
    1 _persistence = Persistence.WithEventSourcingAndSnapshotting(eventStore, snapshotStore, actorId, ApplyEvent, ApplySnapshot, new IntervalStrategy(5), () => { return _value; });
    您可以选择ISnapshotStrategy在保存事件时指定自动保存快照。提供的策略是:
    • EventTypeStrategy - 根据保存的事件类型保存快照
    • IntervalStrategy - 根据保存的事件数量,即每100个事件,定期保存快照
    • TimeStrategy - 根据时间以固定间隔保存快照,即在快照之间等待至少6小时
    同时要在Actor的Receive把保存快照注释掉,Demo中我用的是5个事件后保存一次快照,如下图结果

    绿色是第一次,要保存一下快照,然后之后第五个事件过来后保存第二次快照,如果在第四个事件后程序就退出,那快照保存的只有第一次的,不有担心,当再次调用时,因为记录下了所有事件,Actor会取出最后一次快照,再支执行快照后的事件,这是因为在保存快照和事件时,会把他们的索引保存起来,索引是一样的,就能用最后的快照+这个快照索引后的事件,恢复到退出的地方。

     

    记得执行后查看Sqlite数据,有助于你更好的了解Proto.Actor的Persistence机制哦!

  • 相关阅读:
    《Programming WPF》翻译 第8章 1.动画基础
    一些被遗忘的设计模式
    《Programming WPF》翻译 第4章 数据绑定
    《Programming WPF》翻译 第3章 控件
    《Programming WPF》翻译 第5章 样式和控件模板
    《Programming WPF》翻译 第7章 绘图
    《Programming WPF》翻译 第9章 自定义控件
    《Programming WPF》翻译 第7章 绘图 (2)
    《Programming WPF》翻译 第8章 前言
    关于Debug和Release之本质区别
  • 原文地址:https://www.cnblogs.com/axzxs2001/p/9569899.html
Copyright © 2020-2023  润新知