Ursula:基于线程每核心、多Raft架构的HTTP事件流Rust运行时
摘要
Ursula是一个开源、自托管的分布式服务器,用于可重放、仅追加的事件时间线,运行于HTTP和SSE之上,采用线程每核心、多Raft架构,并搭配S3存储以实现低延迟和持久性。
查看缓存全文
缓存时间: 2026/05/21 16:16
tonbo-io/ursula 源代码:https://github.com/tonbo-io/ursula
Ursula
Crates.io(https://crates.io/crates/ursula)许可证:Apache-2.0 文档:ursula.tonbo.io(https://ursula.tonbo.io)
Ursula 是一个自托管、分布式的服务器,用于文档编辑、代理运行、工作流和聊天背后可重放、仅追加的事件时间线。它通过普通 HTTP 和 SSE 实现耐久流协议(https://github.com/durable-streams/durable-streams)。
Ursula 的特性
事件流存在于代理网络之外。文档编辑器、代理和耐久工作流需要时间线,这些时间线能让浏览器、移动应用和无服务器函数通过公共互联网读取、写入和追踪。这需要原生于 HTTP、分布式、基于 S3 的基础设施,而不是 Kafka 风格为单一网络设计的、锁定 SDK 的架构。
耐久流协议(https://github.com/durable-streams/durable-streams)完美定义了这种有线格式,但其参考服务器是一个单进程:节点丢失即数据丢失。我们评估的其他每个服务器都迫使你放弃这个原语本应保留的四个特性之一:
- 开源自托管。
- 低写入延迟(P99 追加低于 50 毫秒,无需批处理窗口)。
- 纯 S3 经济性(冷层使用标准 S3,无需 S3 Express 层,无需每 GB SaaS 加价)。
- 法定复制耐久性(确认的写入能够承受单节点故障)。
Ursula 保留了所有四个特性。完整设计意图:为什么选择 Ursula(https://ursula.tonbo.io/docs/why-ursula)· Ursula 对比(https://ursula.tonbo.io/docs/competitive-comparison)。
快速开始
目前,Ursula 从 Rust 源代码构建。预构建的发布二进制文件即将推出。
运行单个内存节点(无持久化,适合快速体验):
cargo run --bin ursula
它会绑定到 127.0.0.1:4437,根据 CPU 选择核心数,并使用内存引擎。可以通过 --listen、--core-count、--raft-group-count 覆盖,或者使用 --wal-dir / --raft-log-dir 选择持久化后端。
创建存储桶和流,追加字节,然后读取回来:
curl -X PUT http://127.0.0.1:4437/demo
curl -X PUT http://127.0.0.1:4437/demo/hello
curl -X POST http://127.0.0.1:4437/demo/hello \
-H 'Content-Type: application/octet-stream' \
--data-binary 'hello world'
curl 'http://127.0.0.1:4437/demo/hello?offset=-1'
通过 SSE 实时追踪流,新追加的内容会立即以 event: data 行到达:
curl -N 'http://127.0.0.1:4437/demo/hello?offset=-1&live=sse'
教程:快速入门(https://ursula.tonbo.io/docs/quick-start)· 部署集群(https://ursula.tonbo.io/docs/deploy-cluster)· 配置 S3(https://ursula.tonbo.io/docs/configure-s3)。
架构
三个或五个 Ursula 进程作为一个耐久流服务器运行。一个流通过哈希映射到一个 Raft 组,该组在每个投票节点上有一个副本,并且同一个组 ID 在每个节点上由一个确定的核心拥有。组之间独立复制;没有跨组的事务路径。
HTTP / SSE 客户端
|
|
v
+-----------+ +-----------+ +-----------+
| 节点 1 |<->| 节点 2 |<->| 节点 3 |
| HTTP/gRPC | | HTTP/gRPC | | HTTP/gRPC |
| | | | | |
| 核心 0 | | 核心 0 | | 核心 0 |
| 组 0* |<->| 组 0 |<->| 组 0 |
| 组 3 |<->| 组 3* |<->| 组 3 |
| | | | | |
| 核心 1 | | 核心 1 | | 核心 1 |
| 组 1 |<->| 组 1* |<->| 组 1 |
| 组 4* |<->| 组 4 |<->| 组 4 |
| | | | | |
| 核心 2 | | 核心 2 | | 核心 2 |
| 组 2 |<->| 组 2 |<->| 组 2* |
| 组 5 |<->| 组 5 |<->| 组 5* |
+-----+-----+ +-----+-----+ +-----+-----+
| | |
+---------------+---------------+
|
后台刷新
v
+--------------+
| S3 冷层 |
+--------------+
* 该 Raft 组的领导者,领导权可因组而异。
- 每核心一线程(https://seastar.io/shared-nothing/),多 Raft(https://tikv.org/deep-dive/scalability/multi-raft/)。 每个流通过哈希映射到一个 Raft 组和所属核心,因此核心拥有不相交的组,在热路径上没有共享的可变状态。
- 每组的节点间 Raft。 每个节点为相同配置的组托管副本,这些副本交换 gRPC Raft RPC,而非领导者的 HTTP 写入会转发给当前组领导者。
- 写入路径上的热环。 追加提交到内存环和 Raft 日志中,同时后台刷新器将较旧的已提交块移动到 S3。
- 独立的 Raft 组。 每个组都有自己的 Raft 实例、日志、状态机、热环、监视器和冷刷新预算,没有跨组提交协议。
- 无状态 HTTP 前端。 axum(https://github.com/tokio-rs/axum)解析、路由并渲染协议,而流所有权和可变状态保留在所属组参与者内部。在节点之间,写入在单个组内由领导者序列化,并在该组的大多数副本持久化并应用命令后得到确认。
完整设计:架构概述(https://ursula.tonbo.io/docs/architecture/overview)。
基准测试
在 EC2(3 × c7g.4xlarge,Raft 法定人数)上,Ursula 在 500 个流下维持 35,200 次追加/秒(是单节点耐久流的 5.9 倍,是 S2 Lite 的 5.2 倍,两者均在 1 × c7g.4xlarge 上),并将 SSE 扇出到 1000 个订阅者,P99 延迟为 6.1 毫秒(比耐久流快 160 倍,比 S2 Lite 快 18 倍)。苹果对苹果的方法论、完整图表、回放和延迟分析:ursula.tonbo.io/benchmark(https://ursula.tonbo.io/benchmark)。
路线图
v0.1.x 系列是一个工作原型。下一步计划:
-
if-match条件追加。 在追加路径上实现乐观并发控制。一个if-match: <etag>头部允许写入者仅在流顶端未移动时提交,这样并发写入者无需外部锁即可协调。该语义需要落地到 Ursula 的 HTTP 适配器和 Raft 状态机中。 -
对流的无状态 WASM 计算。 一个计划中的 Ursula 扩展:将确定性的 WASM 模块绑定到流上,使得服务器能够物化每个流的状态,从而实现自动压缩和
410 Gone启动恢复,无需应用程序端检查点。 - 动态成员管理。 在线投票者/学习者重新配置以及协调的滚动成员变更(目前的集群是静态的)。
- 备份和恢复工具。 一种受支持的从 S3 冷层恢复整个集群丢失的恢复路径(目前不存在)。
- 客户端 SDK。 基于 HTTP API 的易用 Rust 和 TypeScript 客户端。
致谢
- ElectricSQL(https://electric-sql.com/) 感谢其原始的耐久流协议,Ursula 实现了该协议。
- Loro(https://loro.dev/) 感谢其快照和重放扩展设计,Ursula 在基础协议之上采用了该设计。
许可证
Apache 2.0。见 LICENSE。
由 Tonbo(https://tonbo.io/)构建,一个开源存储团队。
相似文章
我同时运行 Claude Code 和 Codex,却总是丢失它们之间的线索——于是构建了一个位于 S3 存储桶中的无服务器协调层
tracecraft 是一个 CLI 工具,使用兼容 S3 的存储桶作为多个编码代理(如 Claude Code 和 Codex)的无状态协调层,支持原子任务认领、邮箱、共享内存和跨工具框架会话镜像。
Ü 编程语言
Ü 是一种静态类型的编译型编程语言,专为可靠性和速度而设计,具有安全/不安全代码分离、RAII 和 LLVM 后端。它的目标是优于 C++ 且比 Rust 更易用。
我们如何(及为何)将生产环境的C++前端基础设施重写为Rust
NearlyFreeSpeech.NET 将其生产环境的C++前端基础设施(nfsncore)重写为Rust,该系统负责所有传入请求的路由、缓存和访问控制。迁移的动机是Rust的安全性保证、性能、生态系统优势以及老化的C++代码库的局限性。
Narwhal v0.6.0 – 面向边缘应用的消息代理,现已支持频道持久化
Narwhal 0.6.0 发布,新增频道持久化功能,为边缘场景提供轻量级 Rust 消息代理,将认证与校验逻辑外包给外部“调制器”。
通过HTTP提供文件的三种方式:同步、epoll和io_uring
一篇技术文章,比较了通过HTTP提供文件的三种方法:同步的每个请求一个线程、基于epoll的异步I/O和io_uring,并附有C语言代码示例。