ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

在 Elysia 中集成 Rivet Actors:有状态工作负载与 HTTP 框架的融合实战

在 Elysia 中集成 Rivet Actors:有状态工作负载与 HTTP 框架的融合实战 在 Elysia 中集成 Rivet Actors有状态工作负载与 HTTP 框架的融合实战【免费下载链接】actorsRivet Actors are the primitive for stateful workloads. Built for AI agents, collaborative apps, and durable execution.项目地址: https://gitcode.com/GitHub_Trending/riv/actors导读本篇文章基于仓库中的 examples/elysia 示例项目讲解如何将 Rivet ActorsRivet 的有状态工作负载原语与 Elysia Web 框架集成既用 Elysia 处理常规 HTTP 路由与请求解析又通过registry.handler把/api/rivet/*的流量交给 Rivet 运行时同时借助createClient的类型安全客户端在路由处理器中调用 Actor Action。读完本文你将掌握 actor 定义、registry 装配、Elysia 路由挂载、类型安全客户端调用以及 serverless 模式下 handler 的底层工作原理可直接照搬到自己的 Elysia 项目中。一、示例概览为什么把 Elysia 与 Rivet Actors 组合在一起examples/elysia是一个麻雀虽小五脏俱全的集成示例其 README 将它定位为 Elysia Integration演示 Elysia Web 框架与 Rivet Actors 的协同工作。示例整体只包含两个源文件src/index.ts定义 Actor 与注册表registry并调用registry.start()启动src/server.ts定义 Elysia 应用挂载 Rivet handler 并注册业务路由。README 中归纳的 Features 揭示了这套组合的价值Elysia 负责 HTTP 路由与请求处理Elysia 的声明式路由语法负责对外暴露 API与 Actor 层解耦高性能请求处理Elysia 构建于 Bun 之上适合追求低延迟的场景跨层类型安全TypeScript 类型从 Actor 定义一路推导到 HTTP 路由处理器Action 的参数与返回值在编译期即可校验Actor 集成在 Elysia 路由处理器内部直接调用 Actor Action。需要说明的是README 中引用的src/backend/registry.ts路径在当前仓库中已经不存在实际实现已演进为src/index.tssrc/server.ts两个文件本文以实际源码为准。二、快速开始安装、依赖与启动README 给出的启动流程如下本仓库为只读实际使用时请克隆到自己本地git clone https://gitcode.com/GitHub_Trending/riv/actors cd examples/elysia npm install npm run dev其中npm install会根据 package.json 安装两类关键依赖elysia^1.4.0Web 框架本体rivetkit^2.3.17Rivet 的 TypeScript SDK提供actor、setup、createClient等核心 API。package.json 还暴露了三个 npm 脚本覆盖开发、类型检查与生产启动脚本命令用途devtsx --watch src/index.ts开发模式文件变更自动重启check-typestsc --noEmit全量类型检查验证跨层类型安全starttsx src/index.ts生产模式直接启动注意dev与start的入口都是src/index.ts而src/index.ts会import { registry } from ./index.ts见src/server.ts的导入。也就是说启动后 Elysia 应用通过 registry 的内部机制被装载具体机制在 四、源码原理 中展开。项目启用了allowImportingTsExtensions见 tsconfig.json因此可以带.ts扩展名导入这在 ESM 环境下可直接被tsx运行。三、核心实现拆解Actor、注册表与 Elysia 路由3.1 定义 Actor有状态的计数器src/index.ts 用rivetkit的actor()函数定义了一个带状态的计数器 Actorimport { actor, setup } from rivetkit; export const counter actor({ state: { count: 0 }, actions: { increment: (c, x: number) { c.state.count x; return c.state.count; }, }, }); export const registry setup({ use: { counter }, }); registry.start();state声明 Actor 的持久化状态这里为{ count: 0 }。Rivet 会为每个 Actor 实例维护独立的状态副本actions定义可被远程调用的方法。每个 Action 的第一个参数是上下文对象c通过c.state读写状态本例的increment接收一个x: number参数将状态累加并返回最新计数setup({ use: { counter } })把所有 Actor 装配进一个 Registry。从源码看setup只是new Registry(input)的语法糖见 registry/index.tsuse记录中 key 就是 Actor 的名字客户端将用这个名字寻址registry.start()启动注册表。其实现会根据运行模式分派在 serverless 模式下等价于listen()绑定 HTTP 监听器在 serverfulenvoy模式下则启动 Actor Envoy 与 Engine 通信见 registry/index.ts。actor()的底层实现会通过ActorConfigSchema对配置做 Zod 校验再构造一个ActorDefinition实例见 actor/definition.ts这意味着state、actions的形态在启动前就会被严格校验非法配置会在开发期直接暴露。3.2 定义 Elysia 应用挂载 Rivet handler 并注册业务路由src/server.ts 是整篇集成的核心import { Elysia } from elysia; import { createClient } from rivetkit/client; import { registry } from ./index.ts; const client createClienttypeof registry(); const app new Elysia() .all(/api/rivet/*, (c) registry.handler(c.request)) .get(/increment/:name, async ({ params }) { const counter client.counter.getOrCreate(params.name); const newCount await counter.increment(1); return New Count: ${newCount}; }); export default app;这里有三个值得展开的要点要点一registry.handler(c.request)接管 Rivet 专属流量。Elysia 把所有落在/api/rivet/*前缀下的请求Rivet 的 serverless 协议流量包括 Actor 的启动、唤醒、元数据请求等直接透传给registry.handler。该 handler 由 rivetkit 的 Registry 类提供见 registry/index.ts因此业务路由与 Actor 协议路由在同一个 Elysia 实例中井水不犯河水。要点二createClienttypeof registry()建立类型安全的 Actor 客户端。客户端不需要传 endpoint因为在同一进程中运行时它会自动定位 Engine类型参数typeof registry让 TypeScript 从 Registry 推导出所有 Actor 的 Action 签名——如果increment的参数或返回值类型变化路由处理器中的调用点会立即报错。这正是 README 所称 Full TypeScript type safety across HTTP and actor layers 的来源与官方文档中 createClient () 开箱即用的类型安全 的描述一致见 docs/content/docs/actions.mdx。要点三client.counter.getOrCreate(params.name)实现按 Key 寻址。getOrCreate(name, key)会返回一个指向该 Actor 实例的句柄如果名为params.name的 Actor 不存在则先创建存在则直接复用见 client/client.ts。配合await counter.increment(1)每次请求都会把指定 key 的计数器 1并拿到最新的计数值。这是 Rivet 有状态语义最直接的体现——状态归属于 Actor 实例由 key 标识而非 HTTP 请求。3.3 类型安全如何穿越三层从 type-safety-index.ts 等官方示例可以看出这套类型安全链路是标准化的actor()的 Action 参数/返回值类型 →setup()聚合进 Registry 类型 →createClienttypeof registry()还原出每个 Actor 的类型化客户端。在server.ts中counter.increment(1)的参数1会被静态约束为number返回值则被推导为Promisenumber因此New Count: ${newCount}中的字符串插值是安全的。仓库为此在 package.json 中提供了check-types脚本CI 中运行npm run check-types即可守住这条类型链。四、源码原理Serverless Handler 与 Application 的装载链4.1 handler 如何工作registry.handler(request)是 Registry 类为 serverless 部署设计的 HTTP 入口见 registry/index.ts。它的工作流程可以概括为识别协议请求区分/api/rivet/startPOSTActor 冷启动请求与/api/rivet/metadataGET元数据请求。基础路径来自serveConfig.serverlessBasePath默认就是/api/rivet这正是示例中 Elysia 挂载该前缀的原因背压与流式响应handler 内部构造ReadableStream逐块透传运行时响应并通过desiredSize实现背压控制避免大响应打爆内存取消传播把 HTTP 请求的abort信号绑定到运行时的 cancellation token客户端断开时底层 Actor 调用也会被取消载荷限额对start请求的 body 做大小检查超限返回 413incoming_too_long启动保障isStartRequest时触发 serverless pool 配置失败则返回 503确保 Actor 能被放置到可用的计算池中。这些细节意味着挂载 handler 不只是 转发请求而是完整实现了 Rivet 引擎的 serverless 协议包括冷启动、配额、取消与背压语义。4.2 示例中的装载顺序src/index.ts先registry.start()而src/server.ts导出app。从 registry/index.ts 可以看到start()在 serverless 模式下实际会调用listen({ publicDir })绑定监听器在非 serverless 模式下则启动 Envoy。无论哪种模式示例都能正常工作这正是runtime: auto默认值带来的灵活运行模式适配见 registry/config/index.ts。另外Registry 的listen()还支持传入application选项见 registry/index.ts由createApplicationFetch包装用户应用后作为兜底路由见 registry/index.ts。这为 Rivet 自托管 HTTP 框架路由 提供了另一种集成思路如果希望让 Rivet 直接监听端口、把未匹配框架路由的请求交给 Elysia 处理可以改用await registry.listen({ application: app })的形式。五、运行模式与部署考量5.1 serverless 与 serverful 两种模式根据 registry/index.ts 的实现注释RivetKit 支持两种部署模式示例代码对两者都兼容Mode Bserverlesshandler(request)由外部框架Elysia、Hono 等托管 HTTP适合部署在 Worker、Vercel、Deno Deploy 等平台。示例中.all(/api/rivet/*, ...)就是标准写法Mode Aserverful/Envoystart()启动 Actor Envoy 直接与 Engine 通信适合独立服务器部署。通过环境变量可控制运行模式与端点RIVETKIT_RUNTIME_MODE如serverless、RIVET_PORT默认 3000、RIVET_ENDPOINT、RIVET_NAMESPACE等见 registry/config/index.ts 与 registry/index.ts。在本地没有 Engine 时rivetkit 也能以 local native 模式启动内置引擎端点方便开发调试。5.2 与 Hono 方案的对照本示例不是仓库中唯一的框架集成。官方文档的 calling-backend.ts 展示了完全同构的 Hono 版本const app new Hono(); app.all(/api/rivet/*, (c) registry.handler(c.req.raw)); app.get(/foo, async (c) { const counterActor client.counter.getOrCreate(); const result await counterActor.increment(42); return c.text(String(result)); });两者的集成范式完全一致registry.handler挂载协议前缀 createClient在业务路由中调用 Action。这意味着你在 Elysia、Hono 之间迁移甚至切换到其他支持Request/Response标准的框架时Actor 层代码零改动。README 中 Call actor actions from Elysia route handlers 正是对这一范式的概括。六、进阶指引与延伸阅读Actions 全貌本例的increment是 Actions 的入门形态。官方文档 docs/content/docs/actions.mdx 还覆盖了嵌套 Actions如handle.users.add(Ada)的路径寻址、UserError富错误返回、Zod 输入校验、通过c.abortSignal取消长任务等能力更多框架对照仓库中还有 examples/hono、examples/next-js、examples/trpc 等集成示例可横向对比不同框架下的同构写法深入 SDK 源码Registry、setup、handler的完整实现位于 rivetkit-typescript/packages/rivetkit/src/registry/index.tscreateClient与getOrCreate的实现位于 rivetkit-typescript/packages/rivetkit/src/client/client.ts多 Actor 与真实场景当应用有多个 Actor 时只需在setup({ use: { counter, otherActor } })中继续追加客户端会为每个 Actor 生成独立的类型化访问器。七、小结examples/elysia用约 20 行业务代码演示了 Elysia 与 Rivet Actors 的完整集成链路actor()定义有状态逻辑 →setup()装配注册表 → Elysia 的/api/rivet/*前缀交由registry.handler处理 →createClienttypeof registry()在路由中类型安全地调用 Action。其背后的handler实现了冷启动识别、背压、取消传播等完整的 serverless 协议语义而类型安全则贯通 Actor 定义、Registry 与 HTTP 路由三层。这套模式与仓库中 Hono、Next.js 等示例一脉相承是 Rivet Actors 与任意标准 HTTP 框架集成的通用范式。【免费下载链接】actorsRivet Actors are the primitive for stateful workloads. Built for AI agents, collaborative apps, and durable execution.项目地址: https://gitcode.com/GitHub_Trending/riv/actors创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表