一台服务器上运行 60 个容器
一个裸金属服务器运行数十到数百个 Hoody 容器。KSM 和 BTRFS 去重使边际成本接近零。
你在跑一次八小时的迁移。五个人想知道状态,但又不想占用接收槽或打断数据流。在 pipe URL 后追加 ?progress。任何打开它的人都能看到一个实时 HTML 仪表盘——已传输字节、当前速度、ETA、状态切换。无论多少双眼睛在看,迁移都以全速运行。
?progress 是对 pipe 路径的另一次独立读取,它监听传输状态而不占用接收槽。迁移察觉不到它。浏览器看到的是 HTML 仪表盘;curl 看到的是干净的 SSE 流。
她把带 ?progress 的 pipe URL 粘进手机浏览器。HTML 仪表盘瞬间出现——状态:waiting,0%——无需安装,无需登录,也不占用任何 receiver 名额。
SSE 推送一个 state: streaming 事件。进度条跳到 22%,字节数往上走,MB/s 稳定在 118。仪表盘每 250 ms 自动刷新,没有一次页面重载。
她关掉标签页。她的旁观连接断开。迁移毫无察觉——它从不在数据通路上。sender 和它唯一真正的 receiver 继续跑。
天亮时她重新打开 URL。仪表盘显示一个 done 事件:已传输 7.6 GB,耗时 8h 2m,无错误。服务端状态在刷新后依旧保留——晚到的人总能看到最后一行。
她把 URL 转发到团队 Slack。三名工程师打开后看到同样的 done 状态。没有要关闭的状态讨论串,没有要取消收藏的 Grafana 面板。一个 URL,五个见证者,零打扰。
# 1. Sender — eight-hour migration. Same as always.
tar czf - /var/lib/postgres | curl -T - "$PIPE/api/v1/pipe/migration"
# 2. Receiver — the only client that matters for backpressure.
curl "$PIPE/api/v1/pipe/migration" | tar xzf - -C /restore
# 3. Boss opens the URL on her phone. HTML dashboard. No setup.
# => https://pipe.hoody.com/api/v1/pipe/migration?progress
# 4. You want SSE for a Slack bot? Same URL, different Accept.
curl -N -H "Accept: text/event-stream" \
"$PIPE/api/v1/pipe/migration?progress" \
| grep -E '^event: (progress|state|done)'
# event: state \n data: ["state":"streaming","receivers":1]
# event: progress \n data: ["bytes":5046464512,"mbps":118,"etaSec":840]
# event: done \n data: ["bytes":8160000000,"durationSec":28800]三种 SSE 事件类型。state 用于状态切换(idle → waiting → streaming → complete),progress 在字节流动时每 250ms 推送一次(bytesTransferred、speed、ETA),done 在结束时推送一次最终统计。每条路径最多五十名旁观者,连接 TTL 三十分钟,传输后保留三十秒以便后到的人看到成功的那一行。
同一个 ?progress 端点服务于浏览器标签页、curl 管道和后台轮询的状态页。它们都不会拖慢传输。
在手机上收藏这个 URL。一天看两次。
HTML 仪表盘,无需登录把 SSE curl 进 Slack webhook。一行命令的状态机器人。
text/event-stream, same URL嵌入公开状态页。每 30 s 轮询一次。
0 个 receiver 名额,完整实时状态通过 done 事件接到 PagerDuty。完成时发出提醒。
event: done,一次性触发围观迁移本身就是一个 URL。迁移察觉不到。
每个团队都有一种回答“进度到哪了?”的方式。这些方式里,大部分都需要运行一个服务、接一个仪表盘、或照看一个聊天频道。在 pipe URL 上加一个查询参数则不需要其中任何一项。
发出 URL。停止发送状态更新。