跳转到内容
搜索文档

RPC (WorkerEntrypoint)

最后更新 查看 MarkdownAgent 设置

Service bindings 允许一个 Worker 调用另一个 Worker,而无需通过公开可访问的 URL。

您可以使用 Service bindings 创建 Worker 向其他 Worker 提供的内部 API。这可以通过扩展内置的 WorkerEntrypoint 类并添加自己的公共方法来实现。然后,您 Cloudflare 账户上声明了到此 Worker 的绑定(binding) 的其他 Worker 可以直接调用这些公共方法。

Workers 中的 RPC 系统 设计为尽可能类似于在同一 Worker 中调用 JavaScript 函数。在大多数情况下,您应该能够像在单个 Worker 中一样编写代码。

示例

例如,以下 Worker 实现了公共方法 add(a, b)

例如,如果 Worker B 实现公共方法 add(a, b)

{
	"$schema": "./node_modules/wrangler/config-schema.json",
	"name": "worker_b",
	"main": "./src/workerB.js"
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "worker_b"
main = "./src/workerB.js"
import { WorkerEntrypoint } from "cloudflare:workers";

export default class extends WorkerEntrypoint {
	async fetch() {
		return new Response("Hello from Worker B");
	}

	add(a, b) {
		return a + b;
	}
}
import { WorkerEntrypoint } from "cloudflare:workers";

export default class extends WorkerEntrypoint {
	async fetch() {
		return new Response("Hello from Worker B");
	}

	add(a: number, b: number) {
		return a + b;
	}
}
from workers import WorkerEntrypoint, Response

class Default(WorkerEntrypoint):
    async def fetch(self, request):
        return Response("Hello from Worker B")

    def add(self, a: int, b: int) -> int:
        return a + b

Worker A 可以声明对 Worker B 的绑定(binding)

{
	"$schema": "./node_modules/wrangler/config-schema.json",
	"name": "worker_a",
	"main": "./src/workerA.js",
	"services": [
		{
			"binding": "WORKER_B",
			"service": "worker_b"
		}
	]
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "worker_a"
main = "./src/workerA.js"

[[services]]
binding = "WORKER_B"
service = "worker_b"

这使得 Worker A 可以调用 Worker B 的 add() 方法:

export default {
	async fetch(request, env) {
		const result = await env.WORKER_B.add(1, 2);
		return new Response(result);
	},
};
export default {
	async fetch(request, env) {
		const result = await env.WORKER_B.add(1, 2);
		return new Response(result);
	},
};
from workers import WorkerEntrypoint, Response

class Default(WorkerEntrypoint):
    async def fetch(self, request):
        result = await self.env.WORKER_B.add(1, 2)
		return Response(f"Result: {result}")

您无需学习、实现或考虑特殊协议即可使用 RPC 系统。在此示例中,客户端 Worker A 调用 Worker B 并告诉它使用客户端提供的特定参数执行特定过程。这是通过标准 JavaScript 类实现的。

The WorkerEntrypoint Class

要从 Worker 提供 RPC 方法,您必须扩展 WorkerEntrypoint 类,如下例所示:

import { WorkerEntrypoint } from "cloudflare:workers";

export default class extends WorkerEntrypoint {
  async add(a, b) { return a + b; }
}
from workers import WorkerEntrypoint

class Default(WorkerEntrypoint):
    async def add(self, a, b):
        return a + b

每次调用 Worker 时都会创建类的新实例。请注意,尽管 Worker 实现为类,它仍然是无状态的——类实例仅在调用期间存在。如果您需要在 Worker 中持久化或协调状态,应使用 Durable Objects

Bindings (env)

env 对象作为 WorkerEntrypoint 类的类属性暴露。

例如,声明到环境变量 GREETING 的 binding 的 Worker:

{
	"$schema": "./node_modules/wrangler/config-schema.json",
	"name": "my-worker",
	"vars": {
		"GREETING": "Hello"
	}
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "my-worker"

[vars]
GREETING = "Hello"

可以通过调用 this.env.GREETING 访问它:

import { WorkerEntrypoint } from "cloudflare:workers";

export default class extends WorkerEntrypoint {
  fetch() { return new Response("Hello from my-worker"); }

  async greet(name) {
    return this.env.GREETING + name;
  }
}
from workers import WorkerEntrypoint, Response

class Default(WorkerEntrypoint):
    async def fetch(self, request):
        return Response("Hello from my-worker")

    async def greet(self, name):
        return self.env.GREETING + name

您可以通过这种方式使用任何类型的绑定(binding)

生命周期方法(ctx

ctx 对象作为 WorkerEntrypoint 类的类属性暴露。

例如,您可以通过调用 waitUntil() 方法延长调用上下文的生命周期:

import { WorkerEntrypoint } from "cloudflare:workers";

export default class extends WorkerEntrypoint {
  fetch() { return new Response("Hello from my-worker"); }

  async signup(email, name) {
    // sendEvent() will continue running, even after this method returns a value to the caller
    this.ctx.waitUntil(this.#sendEvent("signup", email))
    // Perform any other work
    return "Success";
  }

  async #sendEvent(eventName, email) {
    //...
  }
}
from workers import WorkerEntrypoint, Response

class Default(WorkerEntrypoint):
    async def fetch(self, request):
        return Response("Hello from my-worker")

    async def signup(self, email, name):
        # _send_event() will continue running, even after this method returns a value to the caller
        self.ctx.waitUntil(self._send_event("signup", email))
        # Perform any other work
        return "Success"

    async def _send_event(self, event_name, email):
        # ...
        pass

获取静态资源

如果您的 Worker 具有静态资源绑定(binding),您可以从 RPC 方法内调用 this.env.ASSETS.fetch()。由于 RPC 方法不接收 request 参数,请使用任何主机名构造 Request 或 URL——主机名会被 assets binding 忽略,只有路径名重要:

import { WorkerEntrypoint } from "cloudflare:workers";

export class ImageWorker extends WorkerEntrypoint {
	async getImage(path) {
		return this.env.ASSETS.fetch(new Request(`https://assets.local${path}`));
	}
}
import { WorkerEntrypoint } from "cloudflare:workers";

export class ImageWorker extends WorkerEntrypoint {
  async getImage(path: string): Promise<Response> {
    return this.env.ASSETS.fetch(
      new Request(`https://assets.local${path}`)
    );
  }
}

调用方随后可以通过 RPC 调用此方法:

const response = await env.IMAGE_SERVICE.getImage("/images/logo.png");
const response = await env.IMAGE_SERVICE.getImage("/images/logo.png");

命名入口点

除了默认导出外,您还可以从单个 Worker 内导出任意数量的命名 WorkerEntrypoint 类。然后,您可以声明到特定命名 entrypoint 的 Service binding。

您可以使用此功能将多个计算部分组合在一起。例如,您可能为应用程序中的每个权限角色创建不同的 WorkerEntrypoint,并使用它们提供特定于角色的 RPC 方法:

{
	"$schema": "./node_modules/wrangler/config-schema.json",
	"name": "todo-app",
	"d1_databases": [
		{
			"binding": "D1",
			"database_name": "todo-app-db",
			"database_id": "<unique-ID-for-your-database>"
		}
	]
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "todo-app"

[[d1_databases]]
binding = "D1"
database_name = "todo-app-db"
database_id = "<unique-ID-for-your-database>"
import { WorkerEntrypoint } from "cloudflare:workers";

export class AdminEntrypoint extends WorkerEntrypoint {
  async createUser(username) {
    await this.env.D1.prepare("INSERT INTO users (username) VALUES (?)")
      .bind(username)
      .run();
  }

  async deleteUser(username) {
    await this.env.D1.prepare("DELETE FROM users WHERE username = ?")
      .bind(username)
      .run();
  }
}

export class UserEntrypoint extends WorkerEntrypoint {
  async getTasks(userId) {
    return await this.env.D1.prepare(
      "SELECT title FROM tasks WHERE user_id = ?"
    )
      .bind(userId)
      .run();
  }

  async createTask(userId, title) {
    await this.env.D1.prepare(
      "INSERT INTO tasks (user_id, title) VALUES (?, ?)"
    )
      .bind(userId, title)
      .run();
  }
}

export default class extends WorkerEntrypoint {
  async fetch(request, env) {
    return new Response("Hello from my to do app");
  }
}
from workers import WorkerEntrypoint, Response

class AdminEntrypoint(WorkerEntrypoint):
    async def create_user(self, username):
        await self.env.D1.prepare("INSERT INTO users (username) VALUES (?)").bind(username).run()

    async def delete_user(self, username):
        await self.env.D1.prepare("DELETE FROM users WHERE username = ?").bind(username).run()

class UserEntrypoint(WorkerEntrypoint):
    async def get_tasks(self, user_id):
        return await self.env.D1.prepare("SELECT title FROM tasks WHERE user_id = ?").bind(user_id).run()

    async def create_task(self, user_id, title):
        await self.env.D1.prepare("INSERT INTO tasks (user_id, title) VALUES (?, ?)").bind(user_id, title).run()

class Default(WorkerEntrypoint):
    async def fetch(self, request):
        return Response("Hello from my to do app")

然后,您可以在另一个 Worker 中直接声明到 AdminEntrypoint 的 Service binding:

{
	"$schema": "./node_modules/wrangler/config-schema.json",
	"name": "admin-app",
	"services": [
		{
			"binding": "ADMIN",
			"service": "todo-app",
			"entrypoint": "AdminEntrypoint"
		}
	]
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "admin-app"

[[services]]
binding = "ADMIN"
service = "todo-app"
entrypoint = "AdminEntrypoint"
export default {
  async fetch(request, env) {
    await env.ADMIN.createUser("aNewUser");
    return new Response("Hello from admin app");
  },
};
from workers import WorkerEntrypoint, Response

class Default(WorkerEntrypoint):
    async def fetch(self, request):
        await self.env.ADMIN.create_user("aNewUser")
        return Response("Hello from admin app")

您可以在 D1 文档 中了解更多关于如何配置 D1 的信息。

您可以通过从 GitHub 克隆 cloudflare/js-rpc-and-entrypoints-demo 仓库 来尝试此待办事项应用程序的完整示例,以及使用命名 entrypoints 构建的 Discord bot。

延伸阅读

这篇文档对您有帮助吗?