
如何在无服务器平台实现实时推送同步NodeWarden SignalR Durable Objects 实现内幕【免费下载链接】nodewardenBitwarden-compatible server running on Cloudflare Workers项目地址: https://gitcode.com/gh_mirrors/no/nodewardenNodeWarden 是一个运行在 Cloudflare Workers 上的 Bitwarden 兼容自托管服务器它用SignalR 协议 Durable Objects在无服务器Serverless环境下实现了跨设备实时推送同步你在手机上改了密码电脑端的保险库毫秒级自动刷新。本文带你拆解这套实时同步架构的完整内幕看看没有服务器内存的 Worker如何无中生有地维持一条稳定 WebSocket 长连接。一、为什么实时同步在无服务器平台上很难传统做法很直接用 Redis 或内存 Map 存用户 → 连接的映射新事件来了就广播。但 Cloudflare Workers 是无状态、按请求计费的执行环境❌ 没有常驻内存两次请求之间状态可能丢失❌ 每次请求可能落在全球任意一台边缘机器上❌ WebSocket 是长连接天然需要有归属的实例NodeWarden 的解法只有一句话用 Durable ObjectsDO把每个用户的连接集合变成全局唯一、有状态、可持久化的单例对象。在 wrangler.toml 中只需三行绑定配置项值作用nameNOTIFICATIONS_HUB代码中的绑定名class_nameNotificationsHub通知中枢类实例命名规则按 userId 派生每个用户一个独立实例二、Durable Objects无服务器里的有状态单例 Durable Objects 是 Cloudflare 提供的有状态计算模型核心特性恰好击中痛点全局单例idFromName(userId)保证同一用户的所有请求都路由到同一个实例WebSocket 不会散到不同机器持久化存储DO 内置键值存储和事务连接票据的一次性消费靠事务保证不被并发重放定时唤醒alarm()机制可自动清理过期的未消费票据杜绝内存泄漏核心实现全部集中在 src/durable/notifications-hub.tsNotificationsHub类同时扮演 WebSocket 服务器、广播中枢和票据保险箱三个角色。三、SignalR 实时推送四步走 整个连接建立与消息推送分为 4 步前后端代码都能对应到源码。第 1 步协商Negotiate领取一次性票据浏览器先携带 Bearer Token 请求/notifications/hub/negotiate。服务端并不直接把 JWT 塞进 WebSocket URLURL 会被日志、代理、历史记录留存而是签发一张仅 60 秒有效的 HMAC-SHA256 签名票据并预先存入对应用户的 DO 中。票据的签发与校验逻辑在 src/utils/websocket-connection-token.ts。第 2 步WebSocket 升级事务性消费票据前端拿到票据后发起wss://.../notifications/hub?id票据升级请求见 webapp/src/App.tsx 中的连接逻辑。Worker 入口在 src/handlers/notifications.ts校验票据签名与 scope提取 userId通过stub.fetch(/internal/ws-token/consume)让 DO 在事务内读取即删除票据——同一张票永远无法通过两次并发升级校验通过后将原始 WebSocket 请求原样转发给该用户的NotificationsHub实例DO 侧随即acceptWebSocket接管连接并给连接打上device:设备ID标签用于后续只推给某台设备的精准定向。第 3 步SignalR 握手与保活连接建立后客户端发送{protocol:json,version:1}完成 SignalR 握手服务端回 ACK。之后客户端每 15 秒发一次{type:6}心跳帧PingDO 侧通过setWebSocketAutoResponse自动回 pong零额外请求成本。连接断开后客户端按 1s→2s→…→10s 的指数退避自动重连全程对用户无感。第 4 步业务写入即广播 这是整套架构最优雅的部分任何业务 handlerciphers.ts、folders.ts、sends.ts 等在数据落库后调用一行notifyUserCipherUpdate(...)消息就通过 DO stub 的fetch(/internal/notify)进入该用户的实例并遍历所有已握手的 WebSocket 按 SignalR 帧格式JSON 或 MessagePack 双协议广播出去业务写入 D1 → stub.fetch 通知 DO → 遍历 WebSocket → 各设备 250ms 防抖后静默刷新保险库注意通知走的是waitUntil不阻塞用户请求的响应返回——写操作依旧毫秒级完成推送是影子任务。四、两个进阶玩法 ✨手机离线时怎么办Bitwarden 推送中继WebSocket 只覆盖在线设备。src/services/push-relay.ts 在每次广播时还会检查该用户是否注册了移动推送设备若有则通过 Bitwarden 官方 Push Relay 下发系统级推送FCM/APNs手机即使 App 在后台也能收到保险库已更新通知打开即同步。未登录状态下的实时通道登录授权流程中还有anonymous-hub以 authRequestId 而非 userId 命名 DO 实例让请求登录的一方无需任何凭据就能实时收到已批准/已拒绝事件AuthRequestResponseRecieved。路由定义见 src/router-public.ts。五、架构启示清单问题NodeWarden 的解法无状态 Worker 如何挂 WebSocketDurable Objects 单例持有连接如何防止票据重放DO 存储事务内读取即删除如何让推送不拖慢主流程waitUntil影子任务如何精准推送到单台设备WebSocketdevice:标签过滤过期票据谁来清理DOalarm()定时自清理手机离线同步复用 Bitwarden Push Relay一句话总结在无服务器平台做实时推送关键不是把服务器做大而是把状态按用户切分并交给 Durable Objects 托管——SignalR 协议负责客户端生态兼容DO 负责服务端状态一致性两者组合让 NodeWarden 以纯边缘架构实现了与自托管 Bitwarden 服务器同等体验的跨设备实时同步。【免费下载链接】nodewardenBitwarden-compatible server running on Cloudflare Workers项目地址: https://gitcode.com/gh_mirrors/no/nodewarden创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考