Actors

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

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

先决条件

场景

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

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

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

Actor 接口

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

export default interface ParkingSensorInterface {
  carEnter(): Promise<void>;
  carLeave(): Promise<void>;
}

Actor 实现

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

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

import { AbstractActor } from "@dapr/dapr";
import ParkingSensorInterface from "./ParkingSensorInterface";

export default class ParkingSensorImpl extends AbstractActor implements ParkingSensorInterface {
  async carEnter(): Promise<void> {
    // Implementation that updates state that this parking spaces is occupied.
  }

  async carLeave(): Promise<void> {
    // Implementation that updates state that this parking spaces is available.
  }

  private async getInfo(): Promise<object> {
    // Implementation of requesting an update from the parking space sensor.
  }

  /**
   * @override
   */
  async onActivate(): Promise<void> {
    // Initialization logic called by AbstractActor.
  }
}

配置Actor运行时

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

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

import { CommunicationProtocolEnum, DaprClient, DaprServer } from "@dapr/dapr";

// Configure the actor runtime with the DaprClientOptions.
const clientOptions = {
  daprHost: daprHost,
  daprPort: daprPort,
  communicationProtocol: CommunicationProtocolEnum.HTTP,
  actor: {
    actorIdleTimeout: "1h",
    actorScanInterval: "30s",
    drainOngoingCallTimeout: "1m",
    drainRebalancedActors: true,
    reentrancy: {
      enabled: true,
      maxStackDepth: 32,
    },
    remindersStoragePartitions: 0,
  },
};

// Use the options when creating DaprServer and DaprClient.

// Note, DaprServer creates a DaprClient internally, which needs to be configured with clientOptions.
const server = new DaprServer({ serverHost, serverPort, clientOptions });

const client = new DaprClient(clientOptions);

注册 Actor

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

import { DaprServer } from "@dapr/dapr";
import ParkingSensorImpl from "./ParkingSensorImpl";

const daprHost = "127.0.0.1";
const daprPort = "50000";
const serverHost = "127.0.0.1";
const serverPort = "50001";

const server = new DaprServer({
  serverHost,
  serverPort,
  clientOptions: {
    daprHost,
    daprPort,
  },
});

await server.actor.init(); // Let the server know we need actors
server.actor.registerActor(ParkingSensorImpl); // Register the actor
await server.start(); // Start the server

// To get the registered actors, you can invoke `getRegisteredActors`:
const resRegisteredActors = await server.actor.getRegisteredActors();
console.log(`Registered Actors: ${JSON.stringify(resRegisteredActors)}`);

调用 Actor 的方法:

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

import { ActorId, DaprClient } from "@dapr/dapr";
import ParkingSensorImpl from "./ParkingSensorImpl";
import ParkingSensorInterface from "./ParkingSensorInterface";

const daprHost = "127.0.0.1";
const daprPort = "50000";

const client = new DaprClient({ daprHost, daprPort });

// Create a new actor builder. It can be used to create multiple actors of a type.
const builder = new ActorProxyBuilder<ParkingSensorInterface>(ParkingSensorImpl, client);

// Create a new actor instance.
const actor = builder.build(new ActorId("my-actor"));
// Or alternatively, use a random ID
// const actor = builder.build(ActorId.createRandomId());

// Invoke the method.
await actor.carEnter();

使用状态与Actor

import { AbstractActor } from "@dapr/dapr";
import ActorStateInterface from "./ActorStateInterface";

export default class ActorStateExample extends AbstractActor implements ActorStateInterface {
  async setState(key: string, value: any): Promise<void> {
    await this.getStateManager().setState(key, value);
    await this.getStateManager().saveState();
  }

  async removeState(key: string): Promise<void> {
    await this.getStateManager().removeState(key);
    await this.getStateManager().saveState();
  }

  // getState with a specific type
  async getState<T>(key: string): Promise<T | null> {
    return await this.getStateManager<T>().getState(key);
  }

  // getState without type as `any`
  async getState(key: string): Promise<any> {
    return await this.getStateManager().getState(key);
  }
}

Actor Timer 和 Reminder

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

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

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

Actor Timer

// ...

const actor = builder.build(new ActorId("my-actor"));

// Register a timer
await actor.registerActorTimer(
  "timer-id", // Unique name of the timer.
  "cb-method", // Callback method to execute when timer is fired.
  Temporal.Duration.from({ seconds: 2 }), // DueTime
  Temporal.Duration.from({ seconds: 1 }), // Period
  Temporal.Duration.from({ seconds: 1 }), // TTL
  50, // State to be sent to timer callback.
);

// Delete the timer
await actor.unregisterActorTimer("timer-id");

Actor Reminder

// ...

const actor = builder.build(new ActorId("my-actor"));

// Register a reminder, it has a default callback: `receiveReminder`
await actor.registerActorReminder(
  "reminder-id", // Unique name of the reminder.
  Temporal.Duration.from({ seconds: 2 }), // DueTime
  Temporal.Duration.from({ seconds: 1 }), // Period
  Temporal.Duration.from({ seconds: 1 }), // TTL
  100, // State to be sent to reminder callback.
);

// Delete the reminder
await actor.unregisterActorReminder("reminder-id");

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

export default class ParkingSensorImpl extends AbstractActor implements ParkingSensorInterface {
  // ...

  /**
   * @override
   */
  async receiveReminder(state: any): Promise<void> {
    // handle stuff here
  }

  // ...
}

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