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

 using Microsoft.Data.Sqlite;
using Proto;
using Proto.Persistence;
using Proto.Persistence.SnapshotStrategies;
using Proto.Persistence.Sqlite;
using System;
using System.Threading.Tasks; namespace P008_Persistence
{
class Program
{
static void Main(string[] args)
{
//用sqlite持久化后
var actorid = "myactorid";
var dbfile = @"C:\MyFile\Source\Repos\ProtoActorSample\ProtoActorSample\P008_Persistence\data.sqlite";
var sqliteProvider = new SqliteProvider(new SqliteConnectionStringBuilder() { DataSource = dbfile });
while (true)
{
Console.WriteLine("1、事件溯源 2、快照 3、带快照的事件溯源 4、退出");
switch (Console.ReadLine())
{
case "":
CallEventSource(actorid, sqliteProvider);
break;
case "":
CallSnapShoot(actorid, sqliteProvider);
break;
case "":
CallSnapShootEventSource(actorid, sqliteProvider);
break;
case "":
return;
}
}
}
/// <summary>
/// 事件溯源
/// </summary>
/// <param name="actorid"></param>
/// <param name="sqliteProvider"></param>
private static void CallEventSource(string actorid, SqliteProvider sqliteProvider)
{
var props = Actor.FromProducer(() => new EventSourceDataActor(sqliteProvider, actorid));
var pid = Actor.Spawn(props);
var result = true;
while (result)
{
Console.WriteLine("1、Tell 2、删除持久化 3、退出"); switch (Console.ReadLine())
{
case "":
var random = new Random();
var no = random.Next(, );
Console.WriteLine($"随机产生的数字:{no}");
pid.Tell(new Data { Amount = no });
break;
case "":
//完成处理后清理持久化的操作
sqliteProvider.DeleteEventsAsync(actorid, ).Wait();
break;
case "":
result = false;
break;
}
}
} /// <summary>
/// 快照
/// </summary>
/// <param name="actorid"></param>
/// <param name="sqliteProvider"></param>
private static void CallSnapShoot(string actorid, SqliteProvider sqliteProvider)
{
var props = Actor.FromProducer(() => new SnapShootDataActor(sqliteProvider, actorid));
var pid = Actor.Spawn(props);
var result = true;
while (result)
{
Console.WriteLine("1、Tell 2、删除持久化 3、退出"); switch (Console.ReadLine())
{
case "":
var random = new Random();
var no = random.Next(, );
Console.WriteLine($"随机产生的数字:{no}");
pid.Tell(new Data { Amount = no });
break;
case "":
//完成处理后清理持久化的操作
sqliteProvider.DeleteEventsAsync(actorid, ).Wait();
break;
case "":
result = false;
break;
}
} }
/// <summary>
/// 快照事件溯源
/// </summary>
/// <param name="actorid"></param>
/// <param name="sqliteProvider"></param>
private static void CallSnapShootEventSource(string actorid, SqliteProvider sqliteProvider)
{
var props = Actor.FromProducer(() => new SnapShootEventSourceDataActor(sqliteProvider, sqliteProvider, actorid));
var pid = Actor.Spawn(props);
var result = true;
while (result)
{
Console.WriteLine("1、Tell 2、删除持久化 3、退出"); switch (Console.ReadLine())
{
case "":
var random = new Random();
var no = random.Next(, );
Console.WriteLine($"随机产生的数字:{no}");
pid.Tell(new Data { Amount = no });
break;
case "":
//完成处理后清理持久化的操作
sqliteProvider.DeleteEventsAsync(actorid, ).Wait();
sqliteProvider.DeleteSnapshotsAsync(actorid, ).Wait();
break;
case "":
result = false;
break;
}
}
}
} public class Data
{
public long Amount { get; set; }
} #region 事件溯源
public class EventSourceDataActor : IActor
{
private long _value = ;
private readonly Persistence _persistence; public EventSourceDataActor(IEventStore eventStore, string actorId)
{
//事件溯源持久化方式
_persistence = Persistence.WithEventSourcing(eventStore, actorId, ApplyEvent);
}
private void ApplyEvent(Proto.Persistence.Event @event)
{
switch (@event.Data)
{
case Data msg:
_value = _value + msg.Amount;
Console.WriteLine($"累计:{_value}");
break;
}
}
public async Task ReceiveAsync(IContext context)
{
switch (context.Message)
{
case Started _:
await _persistence.RecoverStateAsync();
break;
case Data msg:
await _persistence.PersistEventAsync(new Data { Amount = msg.Amount });
break;
}
}
}
#endregion #region 快照
public class SnapShootDataActor : IActor
{
private long _value = ;
private readonly Persistence _persistence; public SnapShootDataActor(ISnapshotStore snapshotStore, string actorId)
{
//快照持久化方式
_persistence = Persistence.WithSnapshotting(snapshotStore, actorId, ApplySnapshot);
}
private void ApplySnapshot(Proto.Persistence.Snapshot snapshot)
{
switch (snapshot.State)
{
case long value:
_value = value;
Console.WriteLine($"累计:{_value}");
break;
}
}
public async Task ReceiveAsync(IContext context)
{
switch (context.Message)
{
case Started _:
await _persistence.RecoverStateAsync();
break;
case Data msg:
_value = _value + msg.Amount;
await _persistence.DeleteSnapshotsAsync();
await _persistence.PersistSnapshotAsync(_value);
break;
}
}
}
#endregion #region 事件溯源and快照
public class SnapShootEventSourceDataActor : IActor
{
private long _value = ;
private readonly Persistence _persistence; public SnapShootEventSourceDataActor(IEventStore eventStore, ISnapshotStore snapshotStore, string actorId)
{
//注释快照策略
//_persistence = Persistence.WithEventSourcingAndSnapshotting(eventStore, snapshotStore, actorId, ApplyEvent, ApplySnapshot, new IntervalStrategy(5), () => { return _value; });
//无快照策略
_persistence = Persistence.WithEventSourcingAndSnapshotting(eventStore, snapshotStore, actorId, ApplyEvent, ApplySnapshot);
}
private void ApplyEvent(Proto.Persistence.Event @event)
{
switch (@event.Data)
{
case Data msg:
_value = _value + msg.Amount;
Console.WriteLine($"事件溯源累计:{_value}");
break;
}
}
private void ApplySnapshot(Proto.Persistence.Snapshot snapshot)
{
switch (snapshot.State)
{
case long value:
_value = value;
Console.WriteLine($"快照累计:{_value}");
break;
}
}
public async Task ReceiveAsync(IContext context)
{
switch (context.Message)
{
case Started _:
await _persistence.RecoverStateAsync();
break;
case Data msg:
await _persistence.PersistEventAsync(new Data { Amount = msg.Amount });
//无快照策略时启用
await _persistence.PersistSnapshotAsync(_value);
break;
}
}
}
#endregion
}
通过代码看到,持久化是通过在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、带快照的事件溯源

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

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

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

《通过C#学Proto.Actor模型》之Persistence的更多相关文章

  1. 《通过C#学Proto.Actor模型》之 HelloWorld

    在微服务中,数据最终一致性的一个解决方案是通过有状态的Actor模型来达到,那什么是Actor模型呢? Actor是并行的计算模型,包含状态,行为,并且包含一个邮箱,来异步处理消息. 关于Actor的 ...

  2. 《通过C#学Proto.Actor模型》之Mailbox

    邮箱是Actor模型的一个重要组成部分,负责接收发过来的消息,并保存起来,等待Actor处理.邮箱中维护着两种队列,一种是存系统消息,另一个是存用户消息,系统省是指Started,Stoping,St ...

  3. 《通过C#学Proto.Actor模型》之Prpos

    在第一篇Proto.Actor博文中,HelloWorld的第一行真正代码是:    var props = Actor.FromProducer(() => new HelloActor()) ...

  4. 通过C#学Proto.Actor模型》之Remote

    Proto.Actor中提供了基于tcp/ip的通迅来实现Remote,可以通过其Remot实现对Actor的调用. 先来看一个极简单片的远程调用. 码友看码: 引用NuGet包 Proto.Acto ...

  5. 《通过C#学Proto.Actor模型》之Behaviors

    Behaviors就是Actor接收到消息后可以改变处理的方法,相同的Actor,每次调用,转到不同的Actor内方法执行,非常适合按流程进行的场景.Behaviors就通过在Actor内部实例化一个 ...

  6. 《通过C#学Proto.Actor模型》之Supervision

    Supervision,字面意思是监督,是父Actor发现子Actor有异常发生后,对子Actor产用保种策略处理的机制,如果父Actor不处理,则往上传递. 子Actor发生异常后处理的策略有: R ...

  7. 《通过C#学Proto.Actor模型》之PID

    PID对象是代表Actor对象的进程,是能过Actor.Spawn(props)获取的:它有什么成员呢?既然代理Actor,首先有一个ID,标识自己是谁,Actor在Spawn时可以命名这个ID,否则 ...

  8. 《通过C#学Proto.Actor模型》之Spawning

    Props是配置Actor和实例化Actor,那实例化后,就应该访问了,Props.Actor提供了Actor.Spawn(),Actor.SpawnPrefix(),Actor.SpawnNamed ...

  9. Proto.Actor模型

    Proto.Actor模型 http://proto.actor/ https://github.com/axzxs2001/ProtoActorSample https://www.cnblogs. ...

随机推荐

  1. XML就是这么简单

    什么是XML? XML:extensiable markup language 被称作可扩展标记语言 XML简单的历史介绍: gml->sgml->html->xml gml(通用标 ...

  2. Python实战171201筛选数据

    Python应用:网络编程,系统网络运维,3D游戏开发,图形界面开发,科学与数字计算,web后端. 对着慕课的练习,果然慕课的实战也是差距很大-------centos7 -Python3.6.3 筛 ...

  3. 阿里云弹性容器实例产品 ECI ——云原生时代的基础设施

    阿里云弹性容器实例产品 ECI ——云原生时代的基础设施 1. 什么是 ECI 弹性容器实例 ECI (Elastic Container Instance) 是阿里云在云原生时代为用户提供的基础计算 ...

  4. Docker最全教程——从理论到实战(五)

    往期内容链接 Docker最全教程——从理论到实战(一) Docker最全教程——从理论到实战(二) Docker最全教程——从理论到实战(三) Docker最全教程——从理论到实战(四) 本篇教程持 ...

  5. [十三]JavaIO之PushBackInputStream

    功能简介 PushBackInputStream是针对于输入的一种扩展功能 装饰器模式中的具体的装饰类,抽象的装饰器为FilterInputStream PushBackInputStream的重点在 ...

  6. [二十一]JavaIO之BufferedReader 与 BufferedWriter

    功能简介 BufferedReader  从字符输入流中读取文本,内部缓冲各个字符,从而实现字符.数组和行的高效读取 BufferedWriter 将文本写入字符输出流,内部缓冲各个字符,从而提供单个 ...

  7. 痞子衡嵌入式:飞思卡尔i.MX RT系列MCU特性介绍(2)- RT1052DVL6性能实测

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔i.MX RT系列MCU的性能. 在前面的文章 i.MXRT微控制器概览 里,痞子衡给大家简介过恩智浦半导体在2017年推出的新 ...

  8. 痞子衡嵌入式:ARM Cortex-M文件那些事(3)- 工程文件(.ewp)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家讲的是嵌入式开发里的project文件. 前面两节课里,痞子衡分别给大家介绍了嵌入式开发中的两种典型input文件:源文件(.c/.h/.s). ...

  9. Spring AOP中的JDK和CGLib动态代理哪个效率更高?

    一.背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高? 二.基本概念 首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理, ...

  10. Docker快速搭建LNMP环境

    一.使用Dockerfile制作镜像 前面的博客中已经介绍了如何基于容器制作镜像,此方法的原理是使用一个正在运行的容器,根据生产所需进行配置更改等操作后,使其满足生产环境,再将这个容器打包制作为镜像, ...