《通过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. ...
随机推荐
- 百万级开源MQTT消息服务器 搭建
下载地址:http://emqtt.com/downloads 文档地址:http://emqtt.com/docs/v2/index.html 开始使用EMQ 2.0 消息服务器简介EMQ (Erl ...
- DataTable转换成List集合,传递到HTML页面
public string GetPwd(string str) { var dt= bll.Gets(str); List<string> list = new List<stri ...
- vue element-ui 2.3.4版本 input number值为0时 显示不出来
解决:官方修复了这个bug.升级element-ui为2.3.5版本就好了
- js中slice,SubString和SubStr的区别
来自:https://blog.csdn.net/qq_37120738/article/details/79086706 侵删 slice() 定义和用法 slice() 方法可从已有的数组中返回选 ...
- sublime实现背景透明化
预览 老司机们就不要吐槽背景图了 实现方法 首先下载插件,直接打包下载zip即可 地址:https://github.com/vhanla/SublimeTextTrans 下载完成后解压到packa ...
- Dynamics CRM项目实例之八:CRM 2015的产品系列,克隆,修订
关注本人微信和易信公众号: 微软动态CRM专家罗勇,回复139或者20150106可方便获取本文,同时可以在第一时间得到我发布的最新的博文信息,follow me! 今天的博客主要是关于D ...
- 驰骋工作流引擎JFlow与activiti的对比之4种包含多实例的模式
1. 无同步的多实例(MIwithout) 在流程中,一个活动可以激活多个实例,每个实例相互独立,并不需要在后面进行同步. 例子:比如用户购买了N本书,于是后续的支付账单.更新客户可以以本书为单位各自 ...
- iOS----------APP怎样做更安全
1 网络请求的安全方案 1.1 https请求,最好有安全交互平台. 1.2 对重要的参数请求进行加密(推荐AES,ERSA加密). 1.3 服务器返回数据时,对重要数据进行加密. 1.4 不要把密钥 ...
- 自定义一个全屏的AlertDialog。
........... final MyDialog dialog = new MyDialog(this); LayoutInflater inflater = getLayoutInflater( ...
- git清空版本记录
在网上找的,记录下来自己使用 1.新增分支 git checkout --orphan latest_branch 2. 添加问题 git add -A 3. 提交 git commit -am &q ...