Actors

通过 Dapr Actor 包,您可以与 JavaScript 应用程序中的 Dapr 虚拟 Actor 进行交互。 下面的示例讲演示如何使用 JavaScript SDK 与 Dapr 虚拟 Actor 进行交互。

有关 Dapr Actor 的更深入说明,请访问 概述页面

先决条件

场景

下面的代码示例大致描述了停车库点监控系统的场景,可以在这个视频中由Mark Russinovich提供。

停车库由数百个停车位组成,每个停车位都包括一个传感器,该传感器为集中监控系统提供更新。 停车位传感器(我们的 Actor)检测一个泊车位是否被占用,或是否可用。

要想自己运行这个例子,请克隆源代码,它可以在JavaScript SDK示例目录中找到。

Actor 接口

Actor接口定义了actor的实现和调用actor的客户端之间的约定。 在下面的例子中,我们为一个停车场的传感器创建了一个接口。 每个传感器有2种方法:carEntercarLeave,它定义了停车位的状态:

  1. export default interface ParkingSensorInterface {
  2. carEnter(): Promise<void>;
  3. carLeave(): Promise<void>;
  4. }

Actor 实现

一个 actor 实现通过扩展基本类型 AbstractActor 并实现 actor 接口(在这种情况下是 ParkingSensorInterface)来定义一个类。

下面的代码描述了一个演员实现以及几个辅助方法。

  1. import { AbstractActor } from "@dapr/dapr";
  2. import ParkingSensorInterface from "./ParkingSensorInterface";
  3. export default class ParkingSensorImpl extends AbstractActor implements ParkingSensorInterface {
  4. async carEnter(): Promise<void> {
  5. // Implementation that updates state that this parking spaces is occupied.
  6. }
  7. async carLeave(): Promise<void> {
  8. // Implementation that updates state that this parking spaces is available.
  9. }
  10. private async getInfo(): Promise<object> {
  11. // Implementation of requesting an update from the parking space sensor.
  12. }
  13. /**
  14. * @override
  15. */
  16. async onActivate(): Promise<void> {
  17. // Initialization logic called by AbstractActor.
  18. }
  19. }

配置Actor运行时

要配置 actor 运行时,请使用 DaprClientOptions。 各种参数及其默认值在操作方法:在Dapr中使用虚拟actor中有文档记录。

注意,超时和间隔应以time.ParseDuration字符串的格式进行格式化。

  1. import { CommunicationProtocolEnum, DaprClient, DaprServer } from "@dapr/dapr";
  2. // Configure the actor runtime with the DaprClientOptions.
  3. const clientOptions = {
  4. daprHost: daprHost,
  5. daprPort: daprPort,
  6. communicationProtocol: CommunicationProtocolEnum.HTTP,
  7. actor: {
  8. actorIdleTimeout: "1h",
  9. actorScanInterval: "30s",
  10. drainOngoingCallTimeout: "1m",
  11. drainRebalancedActors: true,
  12. reentrancy: {
  13. enabled: true,
  14. maxStackDepth: 32,
  15. },
  16. remindersStoragePartitions: 0,
  17. },
  18. };
  19. // Use the options when creating DaprServer and DaprClient.
  20. // Note, DaprServer creates a DaprClient internally, which needs to be configured with clientOptions.
  21. const server = new DaprServer({ serverHost, serverPort, clientOptions });
  22. const client = new DaprClient(clientOptions);

注册 Actor

通过使用DaprServer包初始化和注册您的Actor:

  1. import { DaprServer } from "@dapr/dapr";
  2. import ParkingSensorImpl from "./ParkingSensorImpl";
  3. const daprHost = "127.0.0.1";
  4. const daprPort = "50000";
  5. const serverHost = "127.0.0.1";
  6. const serverPort = "50001";
  7. const server = new DaprServer({
  8. serverHost,
  9. serverPort,
  10. clientOptions: {
  11. daprHost,
  12. daprPort,
  13. },
  14. });
  15. await server.actor.init(); // Let the server know we need actors
  16. server.actor.registerActor(ParkingSensorImpl); // Register the actor
  17. await server.start(); // Start the server
  18. // To get the registered actors, you can invoke `getRegisteredActors`:
  19. const resRegisteredActors = await server.actor.getRegisteredActors();
  20. console.log(`Registered Actors: ${JSON.stringify(resRegisteredActors)}`);

调用 Actor 的方法:

注册完 Actors 后,使用 ActorProxyBuilder 创建一个实现 ParkingSensorInterface 的代理对象。 您可以通过直接在代理对象上调用方法来调用 actor 的方法。 在内部,它会调用 Actor API 进行网络请求,并获取结果返回。

  1. import { ActorId, DaprClient } from "@dapr/dapr";
  2. import ParkingSensorImpl from "./ParkingSensorImpl";
  3. import ParkingSensorInterface from "./ParkingSensorInterface";
  4. const daprHost = "127.0.0.1";
  5. const daprPort = "50000";
  6. const client = new DaprClient({ daprHost, daprPort });
  7. // Create a new actor builder. It can be used to create multiple actors of a type.
  8. const builder = new ActorProxyBuilder<ParkingSensorInterface>(ParkingSensorImpl, client);
  9. // Create a new actor instance.
  10. const actor = builder.build(new ActorId("my-actor"));
  11. // Or alternatively, use a random ID
  12. // const actor = builder.build(ActorId.createRandomId());
  13. // Invoke the method.
  14. await actor.carEnter();

使用状态与Actor

  1. import { AbstractActor } from "@dapr/dapr";
  2. import ActorStateInterface from "./ActorStateInterface";
  3. export default class ActorStateExample extends AbstractActor implements ActorStateInterface {
  4. async setState(key: string, value: any): Promise<void> {
  5. await this.getStateManager().setState(key, value);
  6. await this.getStateManager().saveState();
  7. }
  8. async removeState(key: string): Promise<void> {
  9. await this.getStateManager().removeState(key);
  10. await this.getStateManager().saveState();
  11. }
  12. // getState with a specific type
  13. async getState<T>(key: string): Promise<T | null> {
  14. return await this.getStateManager<T>().getState(key);
  15. }
  16. // getState without type as `any`
  17. async getState(key: string): Promise<any> {
  18. return await this.getStateManager().getState(key);
  19. }
  20. }

Actor Timer 和 Reminder

Actor 可以通过注册 Timer 或 Reminder 来安排自己的周期性任务。 定时器和提醒的主要区别在于,Dapr actor 运行时在停用后不保留任何有关定时器的信息,而使用 Dapr actor 状态提供程序持久化提醒的信息。

这种区别允许用户在轻量级但无状态的timer和需要更多资源但有状态的reminder之间进行权衡。

Timer 和 Reminder 的调度接口定义是完全相同的。 如需更深入地了解调度配置,请参阅 Actor Timer 和 Reminder 文档

Actor Timer

  1. // ...
  2. const actor = builder.build(new ActorId("my-actor"));
  3. // Register a timer
  4. await actor.registerActorTimer(
  5. "timer-id", // Unique name of the timer.
  6. "cb-method", // Callback method to execute when timer is fired.
  7. Temporal.Duration.from({ seconds: 2 }), // DueTime
  8. Temporal.Duration.from({ seconds: 1 }), // Period
  9. Temporal.Duration.from({ seconds: 1 }), // TTL
  10. 50, // State to be sent to timer callback.
  11. );
  12. // Delete the timer
  13. await actor.unregisterActorTimer("timer-id");

Actor Reminder

  1. // ...
  2. const actor = builder.build(new ActorId("my-actor"));
  3. // Register a reminder, it has a default callback: `receiveReminder`
  4. await actor.registerActorReminder(
  5. "reminder-id", // Unique name of the reminder.
  6. Temporal.Duration.from({ seconds: 2 }), // DueTime
  7. Temporal.Duration.from({ seconds: 1 }), // Period
  8. Temporal.Duration.from({ seconds: 1 }), // TTL
  9. 100, // State to be sent to reminder callback.
  10. );
  11. // Delete the reminder
  12. await actor.unregisterActorReminder("reminder-id");

要处理回调,您需要在您的actor中重写默认的receiveReminder实现。 例如,从我们的原始actor实现:

  1. export default class ParkingSensorImpl extends AbstractActor implements ParkingSensorInterface {
  2. // ...
  3. /**
  4. * @override
  5. */
  6. async receiveReminder(state: any): Promise<void> {
  7. // handle stuff here
  8. }
  9. // ...
  10. }

有关virtual actors的完整指南,请访问操作方法:在Dapr中使用virtual actors