《通过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
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的更多相关文章
- 《通过C#学Proto.Actor模型》之 HelloWorld
在微服务中,数据最终一致性的一个解决方案是通过有状态的Actor模型来达到,那什么是Actor模型呢? Actor是并行的计算模型,包含状态,行为,并且包含一个邮箱,来异步处理消息. 关于Actor的 ...
- 《通过C#学Proto.Actor模型》之Mailbox
邮箱是Actor模型的一个重要组成部分,负责接收发过来的消息,并保存起来,等待Actor处理.邮箱中维护着两种队列,一种是存系统消息,另一个是存用户消息,系统省是指Started,Stoping,St ...
- 《通过C#学Proto.Actor模型》之Prpos
在第一篇Proto.Actor博文中,HelloWorld的第一行真正代码是: var props = Actor.FromProducer(() => new HelloActor()) ...
- 通过C#学Proto.Actor模型》之Remote
Proto.Actor中提供了基于tcp/ip的通迅来实现Remote,可以通过其Remot实现对Actor的调用. 先来看一个极简单片的远程调用. 码友看码: 引用NuGet包 Proto.Acto ...
- 《通过C#学Proto.Actor模型》之Behaviors
Behaviors就是Actor接收到消息后可以改变处理的方法,相同的Actor,每次调用,转到不同的Actor内方法执行,非常适合按流程进行的场景.Behaviors就通过在Actor内部实例化一个 ...
- 《通过C#学Proto.Actor模型》之Supervision
Supervision,字面意思是监督,是父Actor发现子Actor有异常发生后,对子Actor产用保种策略处理的机制,如果父Actor不处理,则往上传递. 子Actor发生异常后处理的策略有: R ...
- 《通过C#学Proto.Actor模型》之PID
PID对象是代表Actor对象的进程,是能过Actor.Spawn(props)获取的:它有什么成员呢?既然代理Actor,首先有一个ID,标识自己是谁,Actor在Spawn时可以命名这个ID,否则 ...
- 《通过C#学Proto.Actor模型》之Spawning
Props是配置Actor和实例化Actor,那实例化后,就应该访问了,Props.Actor提供了Actor.Spawn(),Actor.SpawnPrefix(),Actor.SpawnNamed ...
- Proto.Actor模型
Proto.Actor模型 http://proto.actor/ https://github.com/axzxs2001/ProtoActorSample https://www.cnblogs. ...
随机推荐
- C语言实现二叉树中统计叶子结点的个数&度为1&度为2的结点个数
算法思想 统计二叉树中叶子结点的个数和度为1.度为2的结点个数,因此可以参照二叉树三种遍历算法(先序.中序.后序)中的任何一种去完成,只需将访问操作具体变为判断是否为叶子结点和度为1.度为2的结点及统 ...
- 补习系列(10)-springboot 之配置读取
目录 简介 一.配置样例 二.如何注入配置 1. 缺省配置文件 2. 使用注解 3. 启动参数 还有.. 三.如何读取配置 @Value 注解 Environment 接口 @Configuratio ...
- Android事件机制之二:onTouch详解
<Android事件机制之一:事件传递和消费>一文总结了Android中的事件传递和消费机制. 在其中对OntachEvent中的总结中,不是很具体.本文将主要对onTach进行总结. o ...
- Servlet主要相关类核心类 容器调用的过程浅析 servlet解读 怎么调用 Servlet是什么 工作机制
WEB简介 Web项目 是 B/S结构 浏览器/服务器模式的 浏览器发起请求,服务器作出响应 请求的发起和响应使用HTTP协议进行通讯 所谓协议也就是一种固定格式 而Socket是应用 ...
- Linux系统命令行整理
1.pwd 查看当前所在目录 2.cd / 跳往根目录 3.ls 查看当前目录所有子目录或文件 4.ls -l 列出当前目录详细信息 5.ls -lh h=human 人性化列出当前目录详细 ...
- MySQL 笔记整理(4) --深入浅出索引(上)
笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> 4) --深入浅出索引(上) 一句话简单来说,索引的出现其实就是为了提高数据查询的效率,就像书的目录一样. 索引的常见模型 哈希表: ...
- Python判断相等
判断相等方法有好几个:== .is . isinstance .issubclass .operator 模块. == :两个对象内容是否相等. >>> a = [22,44]> ...
- 你必须知道的.net读书笔记第四回:后来居上:class和struct
基本概念 1.1. 什么是class? class(类)是面向对象编程的基本概念,是一种自定义数据结构类型,通常包含字段.属性.方法.属性.构造函数.索引器.操作符等.因为是基本的概念,所以不必在此 ...
- 常见六大Web安全攻防解析
前言 在互联网时代,数据安全与个人隐私受到了前所未有的挑战,各种新奇的攻击技术层出不穷.如何才能更好地保护我们的数据?本文主要侧重于分析几种常见的攻击的类型以及防御的方法. 想阅读更多优质原创文章请猛 ...
- 如何搭建一个VUE项目
搭建环境 搭建node环境 下载 1.进入node.js官方网站下载页,点击下图中框出位置,进行下载即可,当前版本为8.9.4,下载网址为:https://nodejs.org/zh-cn/downl ...