#Dapr Actors
提示:以下是本篇文章正文内容,下面案例可供参考
Actors简介以及优势
-
是一种单线程执行的工作模式
-
避免代码里显示lock
-
避免死锁,避免性能问题
-
使并发编程简单
调用终结点
http://localhost:<dapr-port>/v1.0/actors/<actorType>/<actorId>/
-
<dapr-port>:Dapr正在侦听的HTTP端口
-
<actorType>:执行组件类型。
-
<actorId>:要调用的特定参与者的ID。
代码编写
-
新建类库Common,引入Nuget包:
Dapr.Actors
Dapr.Actors.AspNetCore -
新建接口IWorkflowActor,写入以下代码:
public interface IWorkflowActor : IActor { Task<bool> Approve(); }
-
BackEnd 引用Common,新建Actors文件夹,定义WorkflowActor,实现IWorkflowActor
public class WorkflowActor : Actor, IWorkflowActor { public WorkflowActor(ActorHost host) : base(host) { } public async Task<bool> Approve() { await StateManager.AddOrUpdateStateAsync(Id.ToString(), "approve", (key, currentStatus) => "approve"); return true; } }
-
注册Actors,映射Actors路由
builder.Services.AddActors(options => { options.Actors.RegisterActor<WorkflowActor>(); }); app.MapActorsHandlers();
-
FrontEnd引入Common,并新建ActorsClientController,添加以下接口
[HttpGet("{orderId}")] public async Task<ActionResult> ApproveAsync(string orderId) { var actorId = new ActorId("actorprifix-" + orderId); var proxy = ActorProxy.Create<IWorkflowActor>(actorId, "WorkflowActor"); return Ok(await proxy.Approve()); }
运行与测试
- 启动BackEnd
dapr run --dapr-http-port 3511 --app-port 5000 --app-id backend dotnet .\BackEnd\bin\Debug\net6.0\BackEnd.dll --app-ssl
- 启动FrontEnd
dapr run --dapr-http-port 3501 --app-port 5001 --app-id frontend dotnet .\FrontEnd\bin\Debug\net6.0\FrontEnd.dll
调用API可以看到成功返回true:
http://localhost:5001/api/ActorsClient/123
Redis里面也可以看到状态变为了approve
Timer和Reminders
-
timer和reminder可以设置后台定时触发Actor,两者区别在于,Timer的数据是不会持久化的且只能作用于激活状态的Actor,Reminders则反之。
-
代码实现
- 在IWorkflowActor中声明如下方法
Task RegisterTimer(); Task UnregisterTimer(); Task RegisterReminder(); Task UnregisterReminder();
- 在WorkflowActor中继承IRemindable并实现以下方法
public Task RegisterTimer() { var serializedTimerParams = JsonSerializer.SerializeToUtf8Bytes("now is " + DateTime.Now.ToString()); //TestTimer 注册进Actor的名称 //nameof(this.TimerCallback) 注册进Actor的方法 //serializedTimerParams 注册进Actor的方法的参数 //TimeSpan.FromSeconds(3) 多长时间后第一次执行 //TimeSpan.FromSeconds(3) 间隔多长时间执行 return this.RegisterTimerAsync("TestTimer", nameof(this.TimerCallback), serializedTimerParams, TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3)); } public async Task TimerCallback(byte[] data) { var stateKey = "nowtime"; var content = JsonSerializer.Deserialize<string>(data); _logger.LogInformation(" ---------" + content); await this.StateManager.SetStateAsync<string>(stateKey, content); } public Task UnregisterTimer() { //从Actor中注销 return this.UnregisterTimerAsync("TestTimer"); } public async Task RegisterReminder() { await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); } public async Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period) { var stateKey = "nowtime"; var content = "now is " + DateTime.Now.ToString(); _logger.LogInformation(" reminder---------" + content); await this.StateManager.SetStateAsync<string>(stateKey, content); } public Task UnregisterReminder() { return this.UnregisterReminderAsync("TestReminder"); }
- 在ActorsClientController中新增以下接口
[HttpGet("timer/{orderId}")] public async Task<ActionResult> TimerAsync(string orderId) { var actorId = new ActorId("actorprifix-" + orderId); var proxy = ActorProxy.Create<IWorkflowActor>(actorId, "WorkflowActor"); await proxy.RegisterTimer(); return Ok("done"); } [HttpGet("unregist/timer/{orderId}")] public async Task<ActionResult> UnregistTimerAsync(string orderId) { var actorId = new ActorId("actorprifix-" + orderId); var proxy = ActorProxy.Create<IWorkflowActor>(actorId, "WorkflowActor"); await proxy.UnregisterTimer(); return Ok("done"); } [HttpGet("reminder/{orderId}")] public async Task<ActionResult> ReminderAsync(string orderId) { var actorId = new ActorId("actorprifix-" + orderId); var proxy = ActorProxy.Create<IWorkflowActor>(actorId, "WorkflowActor"); await proxy.RegisterReminder(); return Ok("done"); } [HttpGet("unregist/reminder/{orderId}")] public async Task<ActionResult> UnregistReminderAsync(string orderId) { var actorId = new ActorId("actorprifix-" + orderId); var proxy = ActorProxy.Create<IWorkflowActor>(actorId, "WorkflowActor"); await proxy.UnregisterReminder(); return Ok("done"); }
- 在IWorkflowActor中声明如下方法
启动并测试
- 启动BackEnd
dapr run --dapr-http-port 3511 --app-port 5000 --app-id backend dotnet .\BackEnd\bin\Debug\net6.0\BackEnd.dll --app-ssl
- 启动FrontEnd
dapr run --dapr-http-port 3501 --app-port 5001 --app-id frontend dotnet .\FrontEnd\bin\Debug\net6.0\FrontEnd.dll
测试timer:
http://localhost:5001/api/ActorsClient/timer/123
查看Redis中的值:
在此处,我们可以把BackEnd 重启一下,可以发现timer不会触发,也验证了文章开头所说的timer不会持久化
测试reminder:
http://localhost:5001/api/ActorsClient/reminder/123
查看Redis中的值:
在此处,我们同样把BackEnd 重启一下,可以发现reminder还是会触发,验证了reminder会进行数据持久化操作