本文檔描述如何在客戶端應用程序上建立Actor(MyActor)並調用其方法.git
MyActor --- MyActor.Interfaces | +- MyActorService | +- MyActorClient
接口項目(\MyActor\MyActor.Interfaces)。此項目包含參與者的接口定義。Actor接口能夠在任何名稱的項目中定義。接口定義了actor實現和調用actor的客戶機共享的actor契約。由於客戶機項目可能依賴於它,因此一般在與actor實現分離的程序集中定義它是有意義的.github
actor服務項目(\MyActor\MyActor service)。這個項目實現了ASP.Net核心web服務,該服務將承載參與者。它包含actor MyActor.cs的實現。actor實現是從基類型actor派生並實現MyActor.interfaces項目中定義的接口的類。actor類還必須實現一個構造函數,該構造函數接受ActorService實例和ActorId,並將它們傳遞給基本actor類.web
actor客戶端項目(\MyActor\MyActor client)此項目包含actor客戶端的實現,該客戶端調用actor接口中定義的MyActor方法.redis
Actor接口定義Actor實現和調用Actor的客戶機共享的Actor契約.api
Actor接口定義以下:app
Dapr.Actors.IActor
接口# Create Actor Interfaces dotnet new classlib -o MyActor.Interfaces cd MyActor.Interfaces # Add Dapr.Actors nuget package dotnet add package Dapr.Actors
更新csproj文件中的netcore到 .NET Core 3.0async
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp3.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Dapr.Actors" Version="0.1.0-preview01" /> </ItemGroup> </Project>
定義 IMyActor 接口和 MyData 數據對象.ide
using Dapr.Actors; using System.Threading.Tasks; namespace MyActor.Interfaces { public interface IMyActor : IActor { Task<string> SetDataAsync(MyData data); Task<MyData> GetDataAsync(); Task RegisterReminder(); Task UnregisterReminder(); Task RegisterTimer(); Task UnregisterTimer(); } public class MyData { public string PropertyA { get; set; } public string PropertyB { get; set; } public override string ToString() { var propAValue = this.PropertyA == null ? "null" : this.PropertyA; var propBValue = this.PropertyB == null ? "null" : this.PropertyB; return $"PropertyA: {propAValue}, PropertyB: {propBValue}"; } } }
Dapr使用ASP.NET web服務託管Actor服務. 本節將實現 IMyActor
接口以及註冊Actor到Dapr運行時.函數
# Create ASP.Net Web service to host Dapr actor dotnet new webapi -o MyActorService cd MyActorService # Add Dapr.Actors nuget package dotnet add package Dapr.Actors # Add Dapr.Actors.AspNetCore nuget package dotnet add package Dapr.Actors.AspNetCore # Add Actor Interface reference dotnet add reference ../MyActor.Interfaces/MyActor.Interfaces.csproj
實現IMyActor接口並從Dapr.Actors.Actor類派生。下面的示例還演示瞭如何使用Actor提醒。對於Actor來講,使用提醒,它必須來源於IRemindable。若是不打算使用提醒功能,能夠跳過實現下面代碼中顯示的IRemindable和提醒特定方法.ui
using Dapr.Actors; using Dapr.Actors.Runtime; using MyActor.Interfaces; using System; using System.Threading.Tasks; namespace MyActorService { internal class MyActor : Actor, IMyActor, IRemindable { /// <summary> /// Initializes a new instance of MyActor /// </summary> /// <param name="actorService">The Dapr.Actors.Runtime.ActorService that will host this actor instance.</param> /// <param name="actorId">The Dapr.Actors.ActorId for this actor instance.</param> public MyActor(ActorService actorService, ActorId actorId) : base(actorService, actorId) { } /// <summary> /// This method is called whenever an actor is activated. /// An actor is activated the first time any of its methods are invoked. /// </summary> protected override Task OnActivateAsync() { // Provides opportunity to perform some optional setup. Console.WriteLine($"Activating actor id: {this.Id}"); return Task.CompletedTask; } /// <summary> /// This method is called whenever an actor is deactivated after a period of inactivity. /// </summary> protected override Task OnDeactivateAsync() { // Provides Opporunity to perform optional cleanup. Console.WriteLine($"Deactivating actor id: {this.Id}"); return Task.CompletedTask; } /// <summary> /// Set MyData into actor's private state store /// </summary> /// <param name="data">the user-defined MyData which will be stored into state store as "my_data" state</param> public async Task<string> SetDataAsync(MyData data) { // Data is saved to configured state store implicitly after each method execution by Actor's runtime. // Data can also be saved explicitly by calling this.StateManager.SaveStateAsync(); // State to be saved must be DataContract serialziable. await this.StateManager.SetStateAsync<MyData>( "my_data", // state name data); // data saved for the named state "my_data" return "Success"; } /// <summary> /// Get MyData from actor's private state store /// </summary> /// <return>the user-defined MyData which is stored into state store as "my_data" state</return> public Task<MyData> GetDataAsync() { // Gets state from the state store. return this.StateManager.GetStateAsync<MyData>("my_data"); } /// <summary> /// Register MyReminder reminder with the actor /// </summary> public async Task RegisterReminder() { await this.RegisterReminderAsync( "MyReminder", // The name of the reminder null, // User state passed to IRemindable.ReceiveReminderAsync() TimeSpan.FromSeconds(5), // Time to delay before invoking the reminder for the first time TimeSpan.FromSeconds(5)); // Time interval between reminder invocations after the first invocation } /// <summary> /// Unregister MyReminder reminder with the actor /// </summary> public Task UnregisterReminder() { Console.WriteLine("Unregistering MyReminder..."); return this.UnregisterReminderAsync("MyReminder"); } // <summary> // Implement IRemindeable.ReceiveReminderAsync() which is call back invoked when an actor reminder is triggered. // </summary> public Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period) { Console.WriteLine("ReceiveReminderAsync is called!"); return Task.CompletedTask; } /// <summary> /// Register MyTimer timer with the actor /// </summary> public Task RegisterTimer() { return this.RegisterTimerAsync( "MyTimer", // The name of the timer this.OnTimerCallBack, // Timer callback null, // User state passed to OnTimerCallback() TimeSpan.FromSeconds(5), // Time to delay before the async callback is first invoked TimeSpan.FromSeconds(5)); // Time interval between invocations of the async callback } /// <summary> /// Unregister MyTimer timer with the actor /// </summary> public Task UnregisterTimer() { Console.WriteLine("Unregistering MyTimer..."); return this.UnregisterTimerAsync("MyTimer"); } /// <summary> /// Timer callback once timer is expired /// </summary> private Task OnTimerCallBack(object data) { Console.WriteLine("OnTimerCallBack is called!"); return Task.CompletedTask; } } }
默認狀況下,客戶端看到的actor的「類型」是從actor實現類的名稱派生的。若是須要,能夠經過將actor attribute屬性附加到actor實現類來指定顯式類型名.
[Actor(TypeName = "MyCustomActorTypeName")] internal class MyActor : Actor, IMyActor { // ... }
將 MyActor
註冊到 actor runtime並設置本地主機端口(https://localhost:3000
) , Dapr runtime能夠經過該端口調用actor.
private const int AppChannelHttpPort = 3000; public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .UseActors(actorRuntime => { // Register MyActor actor type actorRuntime.RegisterActor<MyActor>(); } ) .UseUrls($"http://localhost:{AppChannelHttpPort}/");
public class Startup { ... public void ConfigureServices(IServiceCollection services) { services.AddRouting(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseHsts(); } } }
建立一個簡單的控制檯應用程序來調用actor服務。Dapr SDK提供Actor代理客戶端來調用Actor接口中定義的Actor方法.
# Create Actor's Client dotnet new console -o MyActorClient cd MyActorClient # Add Dapr.Actors nuget package dotnet add package Dapr.Actors # Add Actor Interface reference dotnet add reference ../MyActor.Interfaces/MyActor.Interfaces.csproj
咱們建議使用本地代理到actor實例,由於ActorProxy.Create<IMyActor>(actorID,actorType)返回強類型actor實例來設置遠程過程調用.
namespace MyActorClient { using Dapr.Actors; using Dapr.Actors.Client; using MyActor.Interfaces; using System; using System.Threading.Tasks; ... static async Task InvokeActorMethodWithRemotingAsync() { var actorType = "MyActor"; // Registered Actor Type in Actor Service var actorID = new ActorId("1"); // Create the local proxy by using the same interface that the service implements // By using this proxy, you can call strongly typed methods on the interface using Remoting. var proxy = ActorProxy.Create<IMyActor>(actorID, actorType); var response = await proxy.SetDataAsync(new MyData() { PropertyA = "ValueA", PropertyB = "ValueB", }); Console.WriteLine(response); var savedData = await proxy.GetDataAsync(); Console.WriteLine(savedData); } ... }
若是Actor方法最多接受一個參數,則能夠調用Actor方法而無需遠程處理(直接經過http或使用ActorProxy中提供的helper方法)。Actor運行時將從客戶端反序列化傳入的請求體,並將其用做方法參數來調用Actor方法。當進行非遠程處理調用時,Actor方法參數和返回類型被序列化,反序列化爲JSON.
ActorProxy.Create(actorID, actorType)
返回 ActorProxy 實例並容許使用原始http客戶端調用IMyActor中定義的方法.
namespace MyActorClient { using Dapr.Actors; using Dapr.Actors.Client; using MyActor.Interfaces; using System; using System.Threading.Tasks; ... static async Task InvokeActorMethodWithoutRemotingAsync() { var actorType = "MyActor"; var actorID = new ActorId("1"); // Create Actor Proxy instance to invoke the methods defined in the interface var proxy = ActorProxy.Create(actorID, actorType); // Need to specify the method name and response type explicitly var response = await proxy.InvokeAsync<string>("SetMyDataAsync", new MyData() { PropertyA = "ValueA", PropertyB = "ValueB", }); Console.WriteLine(response); var savedData = await proxy.InvokeAsync<MyData>("GetMyDataAsync"); Console.WriteLine(savedData); } ... }
爲了驗證及調試 actor 服務及客戶端, 咱們首先須要經過Dapr CLI運行actor服務.
Run Dapr Runtime via Dapr cli
$ dapr run --app-id myapp --app-port 3000 dotnet MyActorService.dll
在經過Dapr運行時執行MyActorService以後,確保在端口3000上發現應用程序併成功創建actor鏈接.
INFO[0000] starting Dapr Runtime -- version -- commit INFO[0000] log level set to: info INFO[0000] standalone mode configured INFO[0000] dapr id: myapp INFO[0000] loaded component statestore (state.redis) INFO[0000] application protocol: http. waiting on port 3000 INFO[0000] application discovered on port 3000 INFO[0000] application configuration loaded 2019/08/27 14:42:06 redis: connecting to localhost:6379 2019/08/27 14:42:06 redis: connected to localhost:6379 (localAddr: [::1]:53155, remAddr: [::1]:6379) INFO[0000] actor runtime started. actor idle timeout: 1h0m0s. actor scan interval: 30s INFO[0000] actors: starting connection attempt to placement service at localhost:50005 INFO[0000] http server is running on port 3500 INFO[0000] gRPC server is running on port 50001 INFO[0000] dapr initialized. Status: Running. Init Elapsed 19.699438ms INFO[0000] actors: established connection to placement service at localhost:50005 INFO[0000] actors: placement order received: lock INFO[0000] actors: placement order received: update INFO[0000] actors: placement tables updated INFO[0000] actors: placement order received: unlock ...
運行 MyActorClient
若是MyActorClient成功調用託管在MyActorService中的actor,它將在控制檯輸出.
若是指定不一樣的Dapr運行時http端口(默認端口:3500),則須要在運行客戶端以前設置Dapr_http_port環境變量.
Success PropertyA: ValueA, PropertyB: ValueB
原文參考翻譯:https://github.com/dapr/dotnet-sdk/blob/master/docs/get-started-dapr-actor.md