Rerun URI 全面解析:rerun:// 协议设计与 re_uri 解析库实战指南
【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun
re_uri是 Rerun(多模态机器人数据可视化与查询平台)仓库中负责Rerun URI 解析与构造的核心 crate。Rerun 定义了自己的 URL scheme(rerun://、rerun+http://、rerun+https://),用于跨网络统一寻址 Catalog 服务器、数据集、录制段、文件夹以及本地 Viewer 的代理端点。本文将以 crates/store/re_uri/README.md 及其对应的 rustdoc 文档为主体,结合 crate 源码与测试,完整讲解 Rerun URI 的语法规范、解析流程、默认端口、查询参数与 Fragment 语义,读完你能够独立编写、解析与调试任何形式的 Rerun URI。
Rerun URI 是什么:跨网络统一寻址方案
Rerun 使用自己专属的 URL scheme 在网络上访问信息。正如 crates/store/re_uri/src/lib.rs 的 crate 级文档所述:
- 支持的 scheme 有三种:
rerun+http://、rerun+https://,以及作为rerun+https://别名的rerun://; - 解析时这些 scheme 会被即时转换为标准的
http://或https://; - 底层走的是gRPC 协议,URI 中的路径段(如
/catalog、/recording/12345)会被动态映射到对应的 gRPC 服务与方法上。
也就是说,Rerun URI 本质上是一层"面向用户、可读可分享"的寻址抽象:用户只需要记住rerun://这样的语义化 scheme,底层的传输协议与端点映射由re_uri与 gRPC 客户端/服务端负责完成。这也是该 crate 在 Cargo.toml 中声明的定位——"Parsing and constructing Rerun URIs"。
三种 Scheme 与协议转换规则
Scheme枚举定义在 crates/store/re_uri/src/scheme.rs,仅有两种变体,却覆盖三种书写形式:
| 书写形式 | 解析结果 | 规范显示 | 底层协议 |
|---|---|---|---|
rerun:// | RerunHttps | rerun(省略+https) | https |
rerun+https:// | RerunHttps | rerun(省略+https) | https |
rerun+http:// | RerunHttp | rerun+http | http |
关键实现细节(见 scheme.rs):
- 只要 URL 以
rerun+http://开头,就被识别为RerunHttp; - 以
rerun://或rerun+https://开头,则识别为RerunHttps; - 其它任何前缀都会返回
Error::InvalidScheme; RerunHttps在Display输出时统一呈现为rerun,因此rerun://与rerun+https://解析后完全等价(origin.rs 中的test_rerun_alias测试 也验证了这一点)。
Scheme::canonical_url负责把 Rerun scheme 重写为 http(s) 形式:rerun+http://→http://,rerun://或rerun+https://→https://。
安全警告:本地实例与 TLS
crates/store/re_uri/src/lib.rs 明确警告:大多数本地运行的 Rerun 实例没有配置完整的 TLS。因此:
- 本地开发、局域网内连接时应使用
rerun+http://; - 代价是底层连接不加密,请勿在不可信网络上传输敏感数据;
- 公网、生产环境默认使用
rerun://(等价于rerun+https://)。
默认端口:两个关键常量
re_uri在 lib.rs 定义了两个全局默认端口:
DEFAULT_REDAP_PORT = 51234:redap(Rerun Data Access Protocol)服务器的默认端口;DEFAULT_PROXY_PORT = 9876:Rerun gRPC 代理服务器的默认端口。
端口推断逻辑位于 redap_uri.rs 的FromStr实现:当 URL 路径中包含/proxy时,省略端口则默认9876,否则默认51234。而 origin.rs 的replace_and_parse则负责更通用的缺省端口推断:
- 目标是localhost/127.0.0.1/0.0.0.0时,使用调用方传入的默认端口(即上面的 51234 或 9876);
- 目标是公网主机时,
https://缺省补443,http://缺省补80。
这一点在test_parsing测试中体现得非常直观:rerun://example.com解析后端口为 443,rerun+http://example.com解析后端口为 80,而rerun://localhost/catalog解析后端口为 51234。
完整示例:合法 Rerun URI 一览
以下示例直接取自 lib.rs 的 rustdoc,它们是RedapUri::from_str的合法输入,每一行都可以通过uri.parse::<re_uri::RedapUri>()成功解析:
for uri in [ // 访问 catalog 服务器。 "rerun://rerun.io", "rerun://rerun.io:51234/catalog", "rerun+http://localhost:51234/catalog", "rerun+https://localhost:51234/catalog", // 代理:把消息发送给另一个 viewer。 "rerun+http://localhost:51234/proxy", // 链接到 catalog 服务器上的某个数据集。 "rerun://127.0.0.1:1234/dataset/1830B33B45B963E7774455beb91701ae", // 链接到某个数据集的资源(assets)。 "rerun://127.0.0.1:1234/dataset/1830B33B45B963E7774455beb91701ae/assets", // 链接到数据集中的某个录制段(可选时间选择)。 "rerun://127.0.0.1:1234/dataset/1830B33B45B963E7774455beb91701ae?segment_id=sid#time_selection=timeline@1.23s..72s", // 链接到 catalog 中的某个文件夹(数据集名称前缀)。 "rerun://rerun.io/folder/perception.detection", ] { assert!(uri.parse::<re_uri::RedapUri>().is_ok()); }可以看到 Rerun URI 的通用结构为:
scheme://host[:port]/endpoint[/参数路径][?查询参数][#fragment]五大端点:RedapUri 的变体体系
RedapUri是解析的顶层结果,定义于 redap_uri.rs,共五种变体,分别对应五类端点。路径段解析时最多取前 3 段(并过滤空段以兼容尾随斜杠),然后按下表匹配:
| 路径形式 | RedapUri 变体 | 承载结构 |
|---|---|---|
/catalog(或空路径) | Catalog | CatalogUri { origin } |
/entry/<entry_id> | Entry | EntryUri { origin, entry_id } |
/folder/<dotted.path> | Folder | FolderUri { origin, path } |
/dataset/<dataset_id>[/<resource>] | Dataset | DatasetUri { origin, dataset_id, resource, segment_id, fragment } |
/proxy | Proxy | ProxyUri { origin } |
其中Catalog 是默认端点:即使 URI 没有路径(如rerun://localhost:51234)或只有/,也会解析为Catalog(见test_catalog_default测试)。
Catalog:目录服务器
CatalogUri(endpoints/catalog.rs)只包含一个origin,用于定位 redap 服务器的根目录。rerun://rerun.io与rerun://rerun.io:51234/catalog都指向 catalog。
Entry:远程条目
EntryUri(endpoints/entry.rs)由origin加entry_id组成。entry_id是re_log_types::EntryId,底层是一个 TUID。解析时若 ID 非法,返回Error::InvalidTuid。值得注意的兼容性设计:带尾随路径段的 entry URL 仍可解析,保证旧版本 URL 依然能打开条目(见 redap_uri.rs 的test_entry_url_trailing_path)。
Folder:数据集名称前缀分组
FolderUri(endpoints/folder.rs)用点分路径表示目录层级,例如perception.detection。相关工具函数在 dataset_hierarchy.rs:
DATASET_HIERARCHY_SEPARATOR = '.':层级分隔符;split_dataset_hierarchy_path:把a.b.c拆成["a", "b", "c"],且尾随分隔符属于叶子名("a.b."→["a", "b."],"a."→["a."]);dataset_hierarchy_leaf_name:取叶子段。
Folder 路径的序列化会借助url::Url做percent-encoding,因此含/等特殊字符的文件夹名(如odd%2Fname→odd/name)可以安全往返(见test_folder_endpoint_percent_encoded)。空路径folder/会被拒绝。
Dataset:数据集、资源与录制段
DatasetUri(endpoints/dataset.rs)是结构最丰富的端点,支持以下形式:
<origin>/dataset/$DATASET_ID <origin>/dataset/$DATASET_ID?segment_id=$SEGMENT_ID&time_range=$TIME_RANGE <origin>/dataset/$DATASET_ID/assets <origin>/dataset/$DATASET_ID/assets?segment_id=$ASSET_IDdataset_id是re_tuid::Tuid,非法时返回Error::InvalidTuid;- 资源类型
DatasetResource:Segments(默认,指向数据集自身的录制段)与Assets(指向数据集的资源,即其隐藏资源数据集中的录制段);默认资源在序列化时会被省略,只有非默认的/assets才会写进 URL(见test_dataset_url_resource_roundtrip); - 未知资源名会回退到默认资源,保证新旧版本 URL 都能打开数据集(见
test_dataset_url_unknown_resource); - 查询参数
segment_id:None表示指向数据集整体(此时store_id()返回None),Some表示指向某个具体录制段; - 兼容旧参数
partition_id:旧的partition_id会被解析进segment_id,但两者同时出现会报Error::AmbiguousSegmentId(见test_dataset_data_url_legacy_partition_id与test_dataset_data_url_ambiguous_segment_id_partition_id); - 未知查询键会被静默忽略,以兼容其它版本产生的 URL。
DatasetUri::store_id()返回录制段加载进哪个 store,并体现了一个关键设计:
- 数据集的各录制段数据同构,共享同一个 application id(以及蓝图 blueprint);
- 数据集的各个资源(assets)彼此无关,每个资源独立分配 application id;
- 以上均有对应测试(
segments_of_a_dataset_share_an_application_id、assets_of_a_dataset_do_not_share_an_application_id)验证。
Proxy:指向本地 Viewer 的代理
ProxyUri(endpoints/proxy.rs)用于访问另一个本地 Viewer的代理端点,常用于把消息转发给正在运行的 viewer。示例:rerun+http://localhost:9876/proxy。由于路径含/proxy,缺省端口自动取 9876。
Fragment:定位实体与时间
URI 的#fragment部分用于指向具体实体或时间点,由 fragment.rs 中的Fragment结构承载,包含三个可选字段:
| 键 | 类型 | 含义 |
|---|---|---|
selection | DataPath | 选中某个实体路径,可带实例索引,如/entity/path[#42] |
when | (TimelineName, TimeCell) | 选定某条时间线和该时间线上的时间点 |
time_selection | TimeSelection | 选定某条时间线上的时间范围 |
官方 doc 示例:
selection=/entity/path selection=/entity/path[#42] selection=/entity/path[#42]&when=log_tick@32 selection=/entity/path&when=log_time@2022-01-01T00:00:03.123456789Z when=log_time@2022-01-01T00:00:03.123456789Z解析规则细节:
- Fragment 以
&分隔键值对,解析时跳过没有=的段; selection若重复出现,后出现的覆盖先前的;when若重复,则忽略后续的;- 未知键(如
focus=、foo=test)会直接导致解析失败(test_parse_fragment的 fail_cases); &与=支持反斜杠转义:split_on_unescaped_ampersand与split_at_first_unescaped_equals两个内部函数只把未被\转义的字符当作分隔符;Fragment::parse_forgiving在解析失败时返回Fragment::default(),供 URL 解析容错使用——例如test_dataset_data_url_with_broken_fragment中无法解析的 fragment 会被静默丢弃;- 只有
DatasetUri会保留 fragment,Catalog/Proxy/Entry/Folder 均不支持(RedapUri::fragment()返回None)。
TimeSelection:时间范围表达式
TimeSelection(time_selection.rs)由timeline与range(绝对时间范围)组成,字符串语法为TIMELINE@time..time,支持序列时间、持续时间和时间戳三种写法:
sequence@1096..2097 duration@+1.096s..+2.097s duration@-1.096s..+2.097s time_selection=timeline@1.23s..72s解析细节:
- 先按
@拆分出时间线名与范围,再按..拆分出 min/max; - min 与 max 的类型必须一致(都是序列或都是时长),否则返回
Error::InvalidTimeRange; - 负数时长甚至支持 Unicode 减号
−(U+2212),测试test_parse_format_time_selection专门覆盖了该情形; TimeSelection刻意不实现Display(用static_assertions::assert_not_impl_any!强制约束),因为其字符串表示存在歧义,必须通过format()(人类可读)或format_url()(URL 安全、避免禁用的特殊字符)显式格式化。
容错细节:解析时的宽容设计
crates/store/re_uri/src/redap_uri.rs 的解析入口有若干"防呆"设计,值得在实际对接时注意:
- 空格兼容:用户手动在地址栏粘贴
https://rerun.io/viewer?url=rerun+https://…时,+会被浏览器转成空格。解析器会把rerun http、rerun https还原为rerun+http、rerun+https(对应测试test_proxy_endpoint_with_space验证"rerun http://127.0.0.1:9876/proxy"可正常解析); - 无 scheme 输入:当输入不含
://时,解析器按主机名猜测 scheme——含localhost/127.0.0.1时假定rerun+http://,含rerun.io时假定rerun://(TLS),否则报InvalidScheme; - 0.0.0.0 归一化:
Origin::as_url与Display会把不可连接的0.0.0.0显示为127.0.0.1; +编码陷阱:url::Url::query_pairs()会把查询串中的+解码为空格(test_url专门验证了%2B才是+的正确编码),因此构造含+的查询参数时务必使用%2B;- 错误类型完备:error.rs 定义了
InvalidScheme、InvalidTimeRange、UnexpectedUri、UnexpectedOpaqueOrigin、UnexpectedBaseUrl、CannotLoadUrlAsRecording、AmbiguousSegmentId、InvalidTuid等错误变体,其中AmbiguousSegmentId专门用于拦截segment_id与partition_id同时出现的情况。
序列化与在仓库中的实际应用
字符串序列化
RedapUri及各类端点 URI 都实现了serde::Serialize/Deserialize,且以纯字符串形式序列化(Serialize输出to_string(),反序列化时再parse),因此 Rerun URI 可以方便地嵌入 JSON 配置、网络协议消息或持久化状态中。
在数据源识别中的角色
re_uri并不是孤立的解析库,它直接参与 Rerun 的数据源分发。在 crates/store/re_data_source/src/data_source.rs 中:
LogDataSource::RedapDatasetSegment { uri: re_uri::DatasetUri, open_behavior }:把rerun://URI 指向的录制段作为数据源打开;LogDataSource::RedapProxy(re_uri::ProxyUri):把rerun+http://代理作为数据源。
LogDataSource::from_uri会先把用户输入的 URL 分类成本地文件、stdin、HTTP、gRPC 等类型,其中 Rerun scheme 的 URL 就会落到RedapDatasetSegment/RedapProxy分支,例如:
rerun://127.0.0.1:1234/dataset/1830B33B45B963E7774455beb91701ae/data?segment_id=sid rerun+http://127.0.0.1:9876/proxy这印证了re_uri是"URL 文本 → 结构化地址 → 实际连接"链路中的第一环。
TableReference:跨录制表引用
table_reference.rs 中的TableReference枚举把"表"的引用抽象为三种:本地表、远程服务器的__entries表、远程条目表;后两者可通过url()转回对应的RedapUri(Catalog 或 Entry),实现了表引用与 URI 之间的双向转换。
小结
Rerun URI 是一套设计完整、容错友好的统一寻址协议:rerun:///rerun+https://走 TLS,rerun+http://用于本地无 TLS 场景;路径段/catalog、/entry/<id>、/folder/<path>、/dataset/<id>[/segments|assets]、/proxy分别映射五类端点;查询参数segment_id/旧版partition_id选择录制段;#fragment中的selection/when/time_selection让链接可以精确定位到某个实体、时间点或时间范围。
如果要继续深入:
- 完整解析与测试实现见 redap_uri.rs(含大量端到端用例);
- 端口与 origin 推断逻辑见 origin.rs;
- scheme 规范化见 scheme.rs;
- 各端点结构见 crates/store/re_uri/src/endpoints/ 目录;
- 实际消费 Rerun URI 的数据源分发逻辑见 data_source.rs。
【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考