主頁 > .NET開發 > Dapr + .NET Core實戰(五)Actor

Dapr + .NET Core實戰(五)Actor

2021-09-26 06:10:06 .NET開發

什么是Actor模式

Actors 為最低級別的“計算單元”

以上解釋來自官方檔案,看起來“晦澀難懂”,大白話就是說Actors模式是一段需要單執行緒執行的代碼塊,

實際開發中我們經常會有一些邏輯不能并發執行,我們常用的做法就是加鎖,例如:

lock(obj)
{
    //dosomething...
}

或者用Redis等中間件,為分布式應用加一些分布式鎖,遺憾的是,使用顯式鎖定機制容易出錯, 它們很容易導致死鎖,并可能對性能產生嚴重影響,Actors模式為單執行緒邏輯提供了一種更好的選擇,

什么時候用Actors

  • 需要單執行緒執行,比如需要加lock
  • 邏輯可以被劃分為小的執行單元

作業原理

Dapr啟動app時,Sidecar呼叫Actors獲取配置資訊,之后Sidecar將Actors的資訊發送到安置服務(Placement Service),安置服務會將不同的Actor型別根據其Id和Actor型別磁區,并將Actor資訊廣播到所有dapr實體

 在客戶端呼叫某個Actor時,安置服務會根據其Id和Actor型別,找到其所在的dapr實體,并執行其方法,

呼叫Actor方法

POST/GET/PUT/DELETE http://localhost:3500/v1.0/actors/<actorType>/<actorId>/method/<method>
  • <actorType>:執行組件型別,
  • <actorId>:要呼叫的特定參與者的 ID,
  • <method>:要呼叫的方法

計時器Timers和提醒器Reminders

Actor可以設定timer和reminder設定執行Actor的時間,有點像我們常用的定時任務,但是timer和reminder也存在不同,

  • timer只作用于激活狀態的Actor,一個Actor長期不被呼叫,其自己的空閑計時器會逐漸累積,到一定時間后會被Dapr銷毀,timer沒法作用于已銷毀的Actor
  • reminder則可以作用于所有狀態的Actor,主要方式是重置空閑計時器,使其處于活躍狀態

操作timer

POST/PUT http://localhost:3500/v1.0/actors/<actorType>/<actorId>/timers/<name>

到期時間(due time)表示注冊后 timer 將首次觸發的時間, period 表示timer在此之后觸發的頻率, 到期時間為0表示立即執行, 負 due times 和負 periods 都是無效,

下面的請求體配置了一個 timer, dueTime 9秒, period 3秒, 這意味著它將在9秒后首次觸發,然后每3秒觸發一次,

{
  "dueTime":"0h0m9s0ms",
  "period":"0h0m3s0ms"
}

下面的請求體配置了一個 timer, dueTime 0秒, period 3秒, 這意味著它將在注冊之后立即觸發,然后每3秒觸發一次,

{
  "dueTime":"0h0m0s0ms",
  "period":"0h0m3s0ms"
}

 

操作reminder

POST/PUT/GET/DELETE http://localhost:3500/v1.0/actors/<actorType>/<actorId>/reminders/<name>

 到期時間(due time)表示注冊后 reminders將首次觸發的時間, period 表示在此之后 reminders 將觸發的頻率, 到期時間為0表示立即執行, 負 due times 和負 periods 都是無效, 若要注冊僅觸發一次的 reminders ,請將 period 設定為空字串,

下面的請求體配置了一個 reminders, dueTime 9秒, period 3秒, 這意味著它將在9秒后首次觸發,然后每3秒觸發一次,

{
  "dueTime":"0h0m9s0ms",
  "period":"0h0m3s0ms"
}

下面的請求體配置了一個 reminders, dueTime 0秒, period 3秒, 這意味著它將在注冊之后立即觸發,然后每3秒觸發一次,

{
  "dueTime":"0h0m0s0ms",
  "period":"0h0m3s0ms"
}

下面的請求體配置了一個 reminders, dueTime 15秒, period 空字串, 這意味著它將在15秒后首次觸發,之后就不再被觸發,

{
  "dueTime":"0h0m15s0ms",
  "period":""
}

資料持久化

使用 Dapr 狀態管理構建塊保存執行組件狀態, 由于執行組件可以一輪執行多個狀態操作,因此狀態存盤組件必須支持多項事務, 撰寫本文時,以下狀態存盤支持多項事務:

  • Azure Cosmos DB
  • MongoDB
  • MySQL
  • PostgreSQL
  • Redis
  • RethinkDB
  • SQL Server

若要配置要與執行組件一起使用的狀態存盤組件,需要將以下元資料附加到狀態存盤配置

- name: actorStateStore
  value: "true"

win10自承載模式下已默認設定此項 C:\Users\<username>\.dapr\components\statestore.yaml

專案實體

Actor操作

下面將通過一個審核流程的例子來演示,

還是用前面的FrontEnd專案,引入nuget包Dapr.Actors和Dapr.Actors.AspNetCore

 定義IOrderStatusActor介面,需要繼承自IActor

using Dapr.Actors;

using System.Threading.Tasks;

namespace FrontEnd.ActorDefine
{
    public interface IOrderStatusActor : IActor
    {
        Task<string> Paid(string orderId);
        Task<string> GetStatus(string orderId);
    }
}

定義OrderStatusActor實作IOrderStatusActor,并繼承自Actor

using Dapr.Actors.Runtime;

using System.Threading.Tasks;

namespace FrontEnd.ActorDefine
{
    public class OrderStatusActor : Actor, IOrderStatusActor
    {
        public OrderStatusActor(ActorHost host) : base(host)
        {
        }


        public async Task<string> Paid(string orderId)
        {
            // change order status to paid
            await StateManager.AddOrUpdateStateAsync(orderId, "init", (key, currentStatus) => "paid");
            return orderId;
        }

        public async Task<string> GetStatus(string orderId)
        {
            return await StateManager.GetStateAsync<string>(orderId);
        }

    }
}

需要注意的是,執行組件方法的回傳型別必須為 Task 或 Task<T> , 此外,執行組件方法最多只能有一個引數, 回傳型別和引數都必須可 System.Text.Json 序列化

Actor的api是必需的,因為 Dapr 挎斗呼叫應用程式來承載和與執行組件實體進行互動,所以在Startup的Configure中配置

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapActorsHandlers();
               // .......
            });

Startup類也是用于注冊特定執行組件型別的位置, 在ConfigureServices ScoreActor 使用注冊 services.AddActors :

            services.AddActors(options =>
            {
                options.Actors.RegisterActor<OrderStatusActor>();
            });

為測驗這個Actor,需要定義一個介面呼叫,新增ActorController

using Dapr.Actors;
using Dapr.Actors.Client;

using FrontEnd.ActorDefine;

using Microsoft.AspNetCore.Mvc;

using System.Threading.Tasks;

namespace FrontEnd.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class ActorController : ControllerBase
    {
        [HttpGet("paid/{orderId}")]
        public async Task<ActionResult> PaidAsync(string orderId)
        {
            var actorId = new ActorId(orderId);
            var proxy = ActorProxy.Create<IOrderStatusActor>(actorId, "OrderStatusActor");
            var result = await proxy.Paid(orderId);
            return Ok(result);
        }
    }
}

ActorProxy.Create 為創建代理實體, Create方法采用兩個引數:標識特定執行組件和執行組件 ActorId 型別, 它還具有一個泛型型別引數,用于指定執行組件型別所實作的執行組件介面, 由于服務器和客戶端應用程式都需要使用執行組件介面,它們通常存盤在單獨的共享專案中,

下面通過postman測驗下,呼叫成功

 查看redis中的資料

127.0.0.1:6379> keys *
 1) "test_topic"
 2) "frontend||guid"
 3) "frontend||name"
 5) "newOrder"
 6) "frontend||OrderStatusActor||myid-123||123"
 7) "myapp2||key2"
 8) "myapp2||key1"
 9) "deathStarStatus"
10) "myapp||name"
127.0.0.1:6379> hgetall frontend||OrderStatusActor||myid-123||123
1) "data"
2) "\"init\""
3) "version"
4) "1"

可以發現actor資料的命名規則是appName||ActorName||ActorId||key

同樣可以使用注入的方式創建proxy,ActorController中注入IActorProxyFactory

        private readonly IActorProxyFactory _actorProxyFactory;

        public ActorController(IActorProxyFactory actorProxyFactory)
        {
            _actorProxyFactory = actorProxyFactory;
        }

新增獲取資料介面

        [HttpGet("get/{orderId}")]
        public async Task<ActionResult> GetAsync(string orderId)
        {
            var proxy = _actorProxyFactory.CreateActorProxy<IOrderStatusActor>(
                new ActorId("myid-" + orderId),
                "OrderStatusActor");

            return Ok(await proxy.GetStatus(orderId));
        }

postman測驗

 Timer操作

使用Actor基類的 RegisterTimerAsync 方法計劃計時器,在OrderStatusActor類中新增方法

        public Task StartTimerAsync(string name, string text)
        {
            return RegisterTimerAsync(
                name,
                nameof(TimerCallbackAsync),
                Encoding.UTF8.GetBytes(text),
                TimeSpan.Zero,
                TimeSpan.FromSeconds(3));
        }

        public Task TimerCallbackAsync(byte[] state)
        {
            var text = Encoding.UTF8.GetString(state);

            _logger.LogInformation($"Timer fired: {text}");

            return Task.CompletedTask;
        }

StartTimerAsync方法呼叫 RegisterTimerAsync 來計劃計時器, RegisterTimerAsync 采用五個引數:

  1. 計時器的名稱,
  2. 觸發計時器時要呼叫的方法的名稱,
  3. 要傳遞給回呼方法的狀態,
  4. 首次呼叫回呼方法之前要等待的時間,
  5. 回呼方法呼叫之間的時間間隔, 可以指定 以 TimeSpan.FromMilliseconds(-1) 禁用定期信號,

在OrderStatusActor構造方法中呼叫StartTimerAsync

StartTimerAsync("test-timer", "this is a test timer").ConfigureAwait(false).GetAwaiter().GetResult();

通過呼叫paid介面實體化一個Actor,即可開啟timer

 查看控制臺,timer觸發成功

== APP == info: FrontEnd.ActorDefine.OrderStatusActor[0]
== APP ==       Timer fired: this is a test timer

TimerCallbackAsync方法以二進制形式接收用戶狀態, 在示例中,回呼在將狀態寫入日志之前將狀態 string 解碼回 ,

可以通過呼叫 來停止計時器 UnregisterTimerAsync 

    public Task StopTimerAsync(string name)
    {
        return UnregisterTimerAsync(name);
    }

Reminder操作

使用Actor基類的 RegisterReminderAsync 方法計劃計時器,在OrderStatusActor類中新增方法

        public Task SetReminderAsync(string text)
        {
            return RegisterReminderAsync(
                "test-reminder",
                Encoding.UTF8.GetBytes(text),
                TimeSpan.Zero,
                TimeSpan.FromSeconds(1));
        }

        public Task ReceiveReminderAsync(
            string reminderName, byte[] state,
            TimeSpan dueTime, TimeSpan period)
        {
            if (reminderName == "test-reminder")
            {
                var text = Encoding.UTF8.GetString(state);

                Logger.LogWarning($"reminder fired: {text}");
            }

            return Task.CompletedTask;
        }

RegisterReminderAsync方法類似于 RegisterTimerAsync ,但不必顯式指定回呼方法, 如上面的示例所示,實作 IRemindable.ReceiveReminderAsync 以處理觸發的提醒,

    public class OrderStatusActor : Actor, IOrderStatusActor, IRemindable

ReceiveReminderAsync觸發提醒時呼叫 方法, 它采用 4 個引數:

  1. 提醒的名稱,
  2. 注冊期間提供的用戶狀態,
  3. 注冊期間提供的呼叫到期時間,
  4. 注冊期間提供的呼叫周期,

在OrderStatusActor構造方法中呼叫SetReminderAsync

            SetReminderAsync("this is a test reminder").ConfigureAwait(false).GetAwaiter().GetResult();

通過呼叫paid介面實體化一個Actor,即可開啟reminder

 查看控制臺,reminder觸發成功

== APP == warn: FrontEnd.ActorDefine.OrderStatusActor[0]
== APP ==       reminder fired: this is a test reminder

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/302909.html

標籤:.NET技术

上一篇:Dapr + .NET Core實戰(五)Actor

下一篇:NetCore5實作https請求

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more