dapr微服务.net sdk入门

Actors入门

先决条件

  • .Net Core SDK 3.0

  • Dapr CLI

  • Dapr DotNet SDK

概述

本文档描述如何在客户端应用程序上创建Actor(MyActor)并调用其方法.

MyActor --- MyActor.Interfaces
         |
         +- MyActorService
         |
         +- MyActorClient
  • 接口项目(\MyActor\MyActor.Interfaces)。此项目包含参与者的接口定义。Actor接口可以在任何名称的项目中定义。接口定义了actor实现和调用actor的客户机共享的actor契约。因为客户机项目可能依赖于它,所以通常在与actor实现分离的程序集中定义它是有意义的.

  • actor服务项目(\MyActor\MyActor service)。这个项目实现了ASP.Net核心web服务,该服务将承载参与者。它包含actor MyActor.cs的实现。actor实现是从基类型actor派生并实现MyActor.interfaces项目中定义的接口的类。actor类还必须实现一个构造函数,该构造函数接受ActorService实例和ActorId,并将它们传递给基本actor类.

  • actor客户端项目(\MyActor\MyActor client)此项目包含actor客户端的实现,该客户端调用actor接口中定义的MyActor方法.

第1步 - 创建Actor接口

Actor接口定义Actor实现和调用Actor的客户机共享的Actor契约.

Actor接口定义如下:

  • Actor接口必须继承 Dapr.Actors.IActor 接口

  • Actor方法的返回类型必须是Task或Task

  • 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.0

    
    
      
        netcoreapp3.0
      
    
      
        
      
    

    实现 IMyActor 接口

    定义 IMyActor 接口和 MyData 数据对象.

    using Dapr.Actors;
    using System.Threading.Tasks;
    
    namespace MyActor.Interfaces
    {
        public interface IMyActor : IActor
        {
            Task SetDataAsync(MyData data);
            Task 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和提醒特定方法.

    using Dapr.Actors;
    using Dapr.Actors.Runtime;
    using MyActor.Interfaces;
    using System;
    using System.Threading.Tasks;
    
    namespace MyActorService
    {
        internal class MyActor : Actor, IMyActor, IRemindable
        {
            /// 
            /// Initializes a new instance of MyActor
            /// 
            /// The Dapr.Actors.Runtime.ActorService that will host this actor instance.
            /// The Dapr.Actors.ActorId for this actor instance.
            public MyActor(ActorService actorService, ActorId actorId)
                : base(actorService, actorId)
            {
            }
    
            /// 
            /// This method is called whenever an actor is activated.
            /// An actor is activated the first time any of its methods are invoked.
            /// 
            protected override Task OnActivateAsync()
            {
                // Provides opportunity to perform some optional setup.
                Console.WriteLine($"Activating actor id: {this.Id}");
                return Task.CompletedTask;
            }
    
            /// 
            /// This method is called whenever an actor is deactivated after a period of inactivity.
            /// 
            protected override Task OnDeactivateAsync()
            {
                // Provides Opporunity to perform optional cleanup.
                Console.WriteLine($"Deactivating actor id: {this.Id}");
                return Task.CompletedTask;
            }
    
            /// 
            /// Set MyData into actor's private state store
            /// 
            /// the user-defined MyData which will be stored into state store as "my_data" state
            public async Task 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(
                    "my_data",  // state name
                    data);      // data saved for the named state "my_data"
    
                return "Success";
            }
    
            /// 
            /// Get MyData from actor's private state store
            /// 
            /// the user-defined MyData which is stored into state store as "my_data" state
            public Task GetDataAsync()
            {
                // Gets state from the state store.
                return this.StateManager.GetStateAsync("my_data");
            }
    
            /// 
            /// Register MyReminder reminder with the actor
            /// 
            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
            }
    
            /// 
            /// Unregister MyReminder reminder with the actor
            /// 
            public Task UnregisterReminder()
            {
                Console.WriteLine("Unregistering MyReminder...");
                return this.UnregisterReminderAsync("MyReminder");
            }
    
            // 
            // Implement IRemindeable.ReceiveReminderAsync() which is call back invoked when an actor reminder is triggered.
            // 
            public Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period)
            {
                Console.WriteLine("ReceiveReminderAsync is called!");
                return Task.CompletedTask;
            }
    
            /// 
            /// Register MyTimer timer with the actor
            /// 
            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
            }
    
            /// 
            /// Unregister MyTimer timer with the actor
            /// 
            public Task UnregisterTimer()
            {
                Console.WriteLine("Unregistering MyTimer...");
                return this.UnregisterTimerAsync("MyTimer");
            }
    
            /// 
            /// Timer callback once timer is expired
            /// 
            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()
                    .UseActors(actorRuntime =>
                    {
                        // Register MyActor actor type
                        actorRuntime.RegisterActor();
                    }
                    )
                    .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(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(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("SetMyDataAsync", new MyData()
                {
                    PropertyA = "ValueA",
                    PropertyB = "ValueB",
                });
                Console.WriteLine(response);
    
                var savedData = await proxy.InvokeAsync("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连接.

    1. 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

    你可能感兴趣的:(dapr微服务.net sdk入门)