← Cloudflare Durable Objects / durable-objects / examples
Используйте класс RpcTarget для обработки метаданных Durable Object
При работе с Durable Objects вам потребуется получить доступ к имени, использованному при создании Durable Object, через idFromName(). Обычно это осмысленный идентификатор, отражающий назначение Durable Object, например ID пользователя, название комнаты или идентификатор ресурса.
Однако у текущей реализации есть ограничение: хотя вы можете создать Durable Object с .idFromName(name), вы не можете напрямую получить доступ к этому имени внутри Durable Object через this.ctx.id.name.
RpcTarget паттерн, показанный ниже, предлагает решение: он создаёт слой связи, который автоматически передаёт имя при каждом вызове метода. Это позволяет сохранить чистый API и при этом гарантирует, что Durable Object имеет доступ к собственному имени.
В зависимости от ваших потребностей метаданные можно временно хранить в RpcTarget класса, либо используйте хранилище Durable Object, чтобы сохранить метаданные на весь срок жизни объекта.
В этом примере метаданные Durable Object не сохраняются. В нём показано, как:
- Создайте
RpcTargetкласс - Задайте метаданные Durable Object (в данном примере это идентификатор) в
RpcTargetкласс - Передайте метаданные в метод 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) {
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>;