快速入门:Actors

开始使用 Dapr 的 Actors 构建块

让我们来看看Dapr的Actors构建块。 在这个快速入门中,您将运行一个智能设备微服务和一个简单的控制台客户端,以演示 Dapr Actors 中的有状态对象模式。

当前,您可以使用.NET SDK来体验 actor 的快速入门。

作为 .NET actors 快速入门的一个简要概述:

  1. 使用一个 SmartDevice.Service 微服务,您可以托管:
    • 两个 SmartDectectorActor 烟雾报警对象
    • 一个 ControllerActor 对象,用于命令和控制智能设备
  2. 使用一个 SmartDevice.Client 控制台应用程序,客户端应用程序与每个actor或控制器进行交互,以执行聚合操作。
  3. SmartDevice.Interfaces 包含服务和客户端应用使用的共享接口和数据类型。

Actors - 图1

先决条件

对于此示例,您将需要:

第1步:设置环境

克隆在Quickstarts存储库中提供的示例

  1. git clone https://github.com/dapr/quickstarts.git

第2步:运行服务应用

在一个新的终端窗口中,导航到 actors/csharp/sdk/service 目录并恢复依赖项:

  1. cd actors/csharp/sdk/service
  2. dotnet build

运行SmartDevice.Service,它将启动服务本身和Dapr sidecar:

  1. dapr run --app-id actorservice --app-port 5001 --dapr-http-port 3500 --resources-path ../../../resources -- dotnet run --urls=http://localhost:5001/

预期输出:

  1. == APP == info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
  2. == APP == Request starting HTTP/1.1 GET http://127.0.0.1:5001/healthz - -
  3. == APP == info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
  4. == APP == Executing endpoint 'Dapr Actors Health Check'
  5. == APP == info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
  6. == APP == Executed endpoint 'Dapr Actors Health Check'
  7. == APP == info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
  8. == APP == Request finished HTTP/1.1 GET http://127.0.0.1:5001/healthz - - - 200 - text/plain 5.2599ms

步骤 3:运行客户端应用程序

在新的终端窗口中,导航到 actors/csharp/sdk/client 目录并安装依赖项:

  1. cd ./actors/csharp/sdk/client
  2. dotnet build

运行 SmartDevice.Client 应用程序:

  1. dapr run --app-id actorclient -- dotnet run

预期输出:

  1. == APP == Startup up...
  2. == APP == Calling SetDataAsync on SmokeDetectorActor:1...
  3. == APP == Got response: Success
  4. == APP == Calling GetDataAsync on SmokeDetectorActor:1...
  5. == APP == Device 1 state: Location: First Floor, Status: Ready
  6. == APP == Calling SetDataAsync on SmokeDetectorActor:2...
  7. == APP == Got response: Success
  8. == APP == Calling GetDataAsync on SmokeDetectorActor:2...
  9. == APP == Device 2 state: Location: Second Floor, Status: Ready
  10. == APP == Registering the IDs of both Devices...
  11. == APP == Registered devices: 1, 2
  12. == APP == Detecting smoke on Device 1...
  13. == APP == Device 1 state: Location: First Floor, Status: Alarm
  14. == APP == Device 2 state: Location: Second Floor, Status: Alarm
  15. == APP == Sleeping for 16 seconds before checking status again to see reminders fire and clear alarms
  16. == APP == Device 1 state: Location: First Floor, Status: Ready
  17. == APP == Device 2 state: Location: Second Floor, Status: Ready

(可选)第4步:在Zipkin中查看

如果您在计算机上本地为 Dapr 配置了 Zipkin,您可以在 Zipkin Web UI 中查看 actor 与客户端的交互(通常在 http://localhost:9411/zipkin/)。

Actors - 图2

发生了什么?

当您运行客户端应用程序时,会发生几件事情:

  1. 两个 SmartDetectorActor actors 在客户端应用程序中被创建,并且使用对象状态进行初始化:

    • ActorProxy.Create<ISmartDevice>(actorId, actorType)
    • proxySmartDevice.SetDataAsync(data)

    这些对象是可重入的并且保存状态,正如 proxySmartDevice.GetDataAsync()所示。

    1. // Actor Ids and types
    2. var deviceId1 = "1";
    3. var deviceId2 = "2";
    4. var smokeDetectorActorType = "SmokeDetectorActor";
    5. var controllerActorType = "ControllerActor";
    6. Console.WriteLine("Startup up...");
    7. // An ActorId uniquely identifies the first actor instance for the first device
    8. var deviceActorId1 = new ActorId(deviceId1);
    9. // Create a new instance of the data class that will be stored in the first actor
    10. var deviceData1 = new SmartDeviceData(){
    11. Location = "First Floor",
    12. Status = "Ready",
    13. };
    14. // Create the local proxy by using the same interface that the service implements.
    15. var proxySmartDevice1 = ActorProxy.Create<ISmartDevice>(deviceActorId1, smokeDetectorActorType);
    16. // Now you can use the actor interface to call the actor's methods.
    17. Console.WriteLine($"Calling SetDataAsync on {smokeDetectorActorType}:{deviceActorId1}...");
    18. var setDataResponse1 = await proxySmartDevice1.SetDataAsync(deviceData1);
    19. Console.WriteLine($"Got response: {setDataResponse1}");
    20. Console.WriteLine($"Calling GetDataAsync on {smokeDetectorActorType}:{deviceActorId1}...");
    21. var storedDeviceData1 = await proxySmartDevice1.GetDataAsync();
    22. Console.WriteLine($"Device 1 state: {storedDeviceData1}");
    23. // Create a second actor for second device
    24. var deviceActorId2 = new ActorId(deviceId2);
    25. // Create a new instance of the data class that will be stored in the first actor
    26. var deviceData2 = new SmartDeviceData(){
    27. Location = "Second Floor",
    28. Status = "Ready",
    29. };
    30. // Create the local proxy by using the same interface that the service implements.
    31. var proxySmartDevice2 = ActorProxy.Create<ISmartDevice>(deviceActorId2, smokeDetectorActorType);
    32. // Now you can use the actor interface to call the second actor's methods.
    33. Console.WriteLine($"Calling SetDataAsync on {smokeDetectorActorType}:{deviceActorId2}...");
    34. var setDataResponse2 = await proxySmartDevice2.SetDataAsync(deviceData2);
    35. Console.WriteLine($"Got response: {setDataResponse2}");
    36. Console.WriteLine($"Calling GetDataAsync on {smokeDetectorActorType}:{deviceActorId2}...");
    37. var storedDeviceData2 = await proxySmartDevice2.GetDataAsync();
    38. Console.WriteLine($"Device 2 state: {storedDeviceData2}");
  2. 调用SmartDetectorActor 1的DetectSmokeAsync方法

    1. public async Task DetectSmokeAsync()
    2. {
    3. var controllerActorId = new ActorId("controller");
    4. var controllerActorType = "ControllerActor";
    5. var controllerProxy = ProxyFactory.CreateActorProxy<IController>(controllerActorId, controllerActorType);
    6. await controllerProxy.TriggerAlarmForAllDetectors();
    7. }
  3. 调用ControllerActor的TriggerAlarmForAllDetectors方法。 当检测到烟雾时,ControllerActor会内部触发所有警报

    1. public async Task TriggerAlarmForAllDetectors()
    2. {
    3. var deviceIds = await ListRegisteredDeviceIdsAsync();
    4. foreach (var deviceId in deviceIds)
    5. {
    6. var actorId = new ActorId(deviceId);
    7. var proxySmartDevice = ProxyFactory.CreateActorProxy<ISmartDevice>(actorId, "SmokeDetectorActor");
    8. await proxySmartDevice.SoundAlarm();
    9. }
    10. // Register a reminder to refresh and clear alarm state every 15 seconds
    11. await this.RegisterReminderAsync("AlarmRefreshReminder", null, TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15));
    12. }

    控制台打印一条消息,指示检测到烟雾

    1. // Smoke is detected on device 1 that triggers an alarm on all devices.
    2. Console.WriteLine($"Detecting smoke on Device 1...");
    3. proxySmartDevice1 = ActorProxy.Create<ISmartDevice>(deviceActorId1, smokeDetectorActorType);
    4. await proxySmartDevice1.DetectSmokeAsync();
  4. 调用 SmartDetectorActor 12SoundAlarm 方法

    1. storedDeviceData1 = await proxySmartDevice1.GetDataAsync();
    2. Console.WriteLine($"Device 1 state: {storedDeviceData1}");
    3. storedDeviceData2 = await proxySmartDevice2.GetDataAsync();
    4. Console.WriteLine($"Device 2 state: {storedDeviceData2}");
  5. ControllerActor还会使用RegisterReminderAsync创建一个持久的提醒,在15秒后调用ClearAlarm

    1. // Register a reminder to refresh and clear alarm state every 15 seconds
    2. await this.RegisterReminderAsync("AlarmRefreshReminder", null, TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15));

对于示例的完整上下文,请查看以下代码:

告诉我们您的想法

我们一直在努力改进我们的快速入门示例,并重视您的反馈。 您觉得此快速入门有帮助吗? 您有改进的建议吗?

加入我们的discord频道参与讨论。

下一步

了解更多关于Actor构建块

探索 Dapr 教程 >>