Ursula:基于线程每核心、多Raft架构的HTTP事件流Rust运行时

Lobsters Hottest 工具

摘要

Ursula是一个开源、自托管的分布式服务器,用于可重放、仅追加的事件时间线,运行于HTTP和SSE之上,采用线程每核心、多Raft架构,并搭配S3存储以实现低延迟和持久性。

<p>大家好,我刚刚开源了Ursula,这是一个基于ElectricSQL的Durable Streams协议构建的自托管多节点HTTP事件流服务器。</p> <p>架构概要:</p> <ul> <li>线程每核心 x 多Raft</li> <li>写入路径上的内存热环,S3作为冷存储层</li> <li>写入在Raft法定确认中提交,耗时小于50毫秒</li> </ul> <p>架构:<a href="https://ursula.tonbo.io/docs/architecture/overview" rel="ugc">https://ursula.tonbo.io/docs/architecture/overview</a></p> <p>Raft法定确认下的基准测试:</p> <ul> <li>写入性能是官方Node.js Durable Streams的5.9倍,是S2 Lite的5.2倍</li> <li>向1000个订阅者分发的p99 SSE延迟为6.1毫秒(比DS参考低160倍,比S2 Lite低18倍)</li> <li>基于多Raft法定确认模式的复制与S3相比,持久性损失极小</li> </ul> <p><em>Ursula运行在3台c7g.4xlarge上,基线运行在1台c7g.4xlarge上。</em></p> <p>完整方法:<a href="https://ursula.tonbo.io/benchmark" rel="ugc">https://ursula.tonbo.io/benchmark</a></p> <p><a href="https://lobste.rs/s/fzce4h/ursula_thread_per_core_multi_raft_rust">评论</a></p>
查看原文
查看缓存全文

缓存时间: 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/)构建,一个开源存储团队。

相似文章

Ü 编程语言

Hacker News Top

Ü 是一种静态类型的编译型编程语言,专为可靠性和速度而设计,具有安全/不安全代码分离、RAII 和 LLVM 后端。它的目标是优于 C++ 且比 Rust 更易用。