dapr微服務.netcore sdk入門

Actors入門

先決條件

概述

本文檔描述如何在客戶端應用程序上建立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

第1步 - 建立Actor接口

Actor接口定義Actor實現和調用Actor的客戶機共享的Actor契約.api

Actor接口定義以下:app

  • Actor接口必須繼承 Dapr.Actors.IActor 接口
  • Actor方法的返回類型必須是Task或Task<object>
  • Actor方法最多能夠有一個參數

建立項目和添加依賴

# Create Actor Interfaces
dotnet new classlib -o MyActor.Interfaces

cd MyActor.Interfaces

# Add Dapr.Actors nuget package
dotnet add package Dapr.Actors

升級項目到 .NET Core 3.0

更新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 接口

定義 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}";
        }
    }
}

第2步 - 建立Actor服務

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

添加Actor實現

實現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實現類的名稱派生的。若是須要,能夠經過將actor attribute屬性附加到actor實現類來指定顯式類型名.

[Actor(TypeName = "MyCustomActorTypeName")]
internal class MyActor : Actor, IMyActor
{
    // ...
}

註冊 Actor 到 Dapr 運行時

將 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}/");

更新Startup.cs

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();
            }
        }
    }

第3步 - 添加客戶端

建立一個簡單的控制檯應用程序來調用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遠程服務調用Actor方法

咱們建議使用本地代理到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方法最多接受一個參數,則能夠調用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

爲了驗證及調試 actor 服務及客戶端, 咱們首先須要經過Dapr CLI運行actor服務.

  1. 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
     ...
    
  2. 運行 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

相關文章
相關標籤/搜索