跳转到内容
搜索文档

使用 RpcTarget 类处理 Durable Object 元数据

使用 RpcTarget 从 Durable Object 内部访问名称。

最后更新 查看 MarkdownAgent 设置

使用 Durable Objects 时,您需要访问通过 idFromName() 创建 Durable Object 时使用的名称。此名称通常是有意义的标识符,表示 Durable Object 负责的内容(如用户 ID、房间名称或资源标识符)。

然而,当前实现存在限制:即使您可以使用 .idFromName(name) 创建 Durable Object,也无法通过 this.ctx.id.name 在 Durable Object 内部直接访问此名称。

下面展示的 RpcTarget 模式通过创建通信层提供解决方案,该层在每次方法调用时自动携带名称。这使您的 API 保持简洁,同时确保 Durable Object 能够访问自己的名称。

根据您的需求,您可以在 RpcTarget 类中临时存储元数据,或使用 Durable Object 存储在对象生命周期内持久化元数据。

此示例不持久化 Durable Object 元数据。它演示如何:

  1. 创建 RpcTarget
  2. RpcTarget 类中设置 Durable Object 元数据(此示例中的标识符)
  3. 将元数据传递给 Durable Object 方法
  4. 使用后清理 RpcTarget
import { DurableObject, RpcTarget } from "cloudflare:workers";

//  * Create an RpcDO class that extends RpcTarget
//  * Use this class to set the Durable Object metadata
//  * Pass the metadata in the Durable Object methods
//  * @param mainDo - The main Durable Object class
//  * @param doIdentifier - The identifier of the Durable Object

export class RpcDO extends RpcTarget {
	constructor(
		private mainDo: MyDurableObject,
		private doIdentifier: string,
	) {
		super();
	}

	//  * Pass the user's name to the Durable Object method
	//  * @param userName - The user's name to pass to the Durable Object method

	async computeMessage(userName: string): Promise<string> {
		// Call the Durable Object method and pass the user's name and the Durable Object identifier
		return this.mainDo.computeMessage(userName, this.doIdentifier);
	}

	//  * Call the Durable Object method without using the Durable Object identifier
	//  * @param userName - The user's name to pass to the Durable Object method

	async simpleGreeting(userName: string) {
		return this.mainDo.simpleGreeting(userName);
	}
}

//  * Create a Durable Object class
//  * You can use the RpcDO class to set the Durable Object metadata

export class MyDurableObject extends DurableObject<Env> {
	constructor(ctx: DurableObjectState, env: Env) {
		super(ctx, env);
	}

	//  * Initialize the RpcDO class
	//  * You can set the Durable Object metadata here
	//  * It returns an instance of the RpcDO class
	//  * @param doIdentifier - The identifier of the Durable Object

	async setMetaData(doIdentifier: string) {
		return new RpcDO(this, doIdentifier);
	}

	//  * Function that computes a greeting message using the user's name and DO identifier
	//  * @param userName - The user's name to include in the greeting
	//  * @param doIdentifier - The identifier of the Durable Object

	async computeMessage(
		userName: string,
		doIdentifier: string,
	): Promise<string> {
		console.log({
			userName: userName,
			durableObjectIdentifier: doIdentifier,
		});
		return `Hello, ${userName}! The identifier of this DO is ${doIdentifier}`;
	}

	//  * Function that is not in the RpcTarget
	//  * Not every function has to be in the RpcTarget

	private async notInRpcTarget() {
		return "This is not in the RpcTarget";
	}

	//  * Function that takes the user's name and does not use the Durable Object identifier
	//  * @param userName - The user's name to include in the greeting

	async simpleGreeting(userName: string) {
		// Call the private function that is not in the RpcTarget
		console.log(this.notInRpcTarget());

		return `Hello, ${userName}! This doesn't use the DO identifier.`;
	}
}

export default {
	async fetch(request, env, ctx): Promise<Response> {
		let id: DurableObjectId = env.MY_DURABLE_OBJECT.idFromName(
			new URL(request.url).pathname,
		);
		let stub = env.MY_DURABLE_OBJECT.get(id);

		//  * Set the Durable Object metadata using the RpcTarget
		//  * Notice that no await is needed here

		const rpcTarget = stub.setMetaData(id.name ?? "default");

		// Call the Durable Object method using the RpcTarget.
		// The DO identifier is passed in the RpcTarget
		const greeting = await rpcTarget.computeMessage("world");

		// Call the Durable Object method that does not use the Durable Object identifier
		const simpleGreeting = await rpcTarget.simpleGreeting("world");

		// Clean up the RpcTarget.
		try {
			(await rpcTarget)[Symbol.dispose]?.();
			console.log("RpcTarget cleaned up.");
		} catch (e) {
			console.error({
				message: "RpcTarget could not be cleaned up.",
				error: String(e),
				errorProperties: e,
			});
		}

		return new Response(greeting, { status: 200 });
	},
} satisfies ExportedHandler<Env>;

此示例持久化 Durable Object 元数据。它演示与上一个示例类似的步骤,但使用 Durable Object 存储来存储标识符,无需通过 RpcTarget 传递。

import { DurableObject, RpcTarget } from "cloudflare:workers";

//  * Create an RpcDO class that extends RpcTarget
//  * Use this class to set the Durable Object metadata
//  * Pass the metadata in the Durable Object methods
//  * @param mainDo - The main Durable Object class
//  * @param doIdentifier - The identifier of the Durable Object

export class RpcDO extends RpcTarget {
	constructor(
		private mainDo: MyDurableObject,
		private doIdentifier: string,
	) {
		super();
	}

	//  * Pass the user's name to the Durable Object method
	//  * @param userName - The user's name to pass to the Durable Object method

	async computeMessage(userName: string): Promise<string> {
		// Call the Durable Object method and pass the user's name and the Durable Object identifier
		return this.mainDo.computeMessage(userName, this.doIdentifier);
	}

	//  * Call the Durable Object method without using the Durable Object identifier
	//  * @param userName - The user's name to pass to the Durable Object method

	async simpleGreeting(userName: string) {
		return this.mainDo.simpleGreeting(userName);
	}
}

//  * Create a Durable Object class
//  * You can use the RpcDO class to set the Durable Object metadata

export class MyDurableObject extends DurableObject<Env> {
	constructor(ctx: DurableObjectState, env: Env) {
		super(ctx, env);
	}

	//  * Initialize the RpcDO class
	//  * You can set the Durable Object metadata here
	//  * It returns an instance of the RpcDO class
	//  * @param doIdentifier - The identifier of the Durable Object

	async setMetaData(doIdentifier: string) {
		// Use DO storage to store the Durable Object identifier
		await this.ctx.storage.put("doIdentifier", doIdentifier);
		return new RpcDO(this, doIdentifier);
	}

	//  * Function that computes a greeting message using the user's name and DO identifier
	//  * @param userName - The user's name to include in the greeting

	async computeMessage(userName: string): Promise<string> {
		// Get the DO identifier from storage
		const doIdentifier = await this.ctx.storage.get("doIdentifier");
		console.log({
			userName: userName,
			durableObjectIdentifier: doIdentifier,
		});
		return `Hello, ${userName}! The identifier of this DO is ${doIdentifier}`;
	}

	//  * Function that is not in the RpcTarget
	//  * Not every function has to be in the RpcTarget

	private async notInRpcTarget() {
		return "This is not in the RpcTarget";
	}

	//  * Function that takes the user's name and does not use the Durable Object identifier
	//  * @param userName - The user's name to include in the greeting

	async simpleGreeting(userName: string) {
		// Call the private function that is not in the RpcTarget
		console.log(this.notInRpcTarget());

		return `Hello, ${userName}! This doesn't use the DO identifier.`;
	}
}

export default {
	async fetch(request, env, ctx): Promise<Response> {
		let id: DurableObjectId = env.MY_DURABLE_OBJECT.idFromName(
			new URL(request.url).pathname,
		);
		let stub = env.MY_DURABLE_OBJECT.get(id);

		//  * Set the Durable Object metadata using the RpcTarget
		//  * Notice that no await is needed here

		const rpcTarget = stub.setMetaData(id.name ?? "default");

		// Call the Durable Object method using the RpcTarget.
		// The DO identifier is stored in the Durable Object's storage
		const greeting = await rpcTarget.computeMessage("world");

		// Call the Durable Object method that does not use the Durable Object identifier
		const simpleGreeting = await rpcTarget.simpleGreeting("world");

		// Clean up the RpcTarget.
		try {
			(await rpcTarget)[Symbol.dispose]?.();
			console.log("RpcTarget cleaned up.");
		} catch (e) {
			console.error({
				message: "RpcTarget could not be cleaned up.",
				error: String(e),
				errorProperties: e,
			});
		}

		return new Response(greeting, { status: 200 });
	},
} satisfies ExportedHandler<Env>;

这篇文档对您有帮助吗?