← 返回项目列表

多VPS带宽聚合

stable
2026-06-15
VPSHysteria2GoRally-go带宽聚合

搞了几台廉价美西 VPS 后发现一个尴尬的事:单台出去带宽就 8-50Mbps,下个 YouTube 高清视频慢得不行。

如果能把几台 VPS 的带宽凑一起,聚合出一条 SOCKS5 代理,下载工具直接走这条线,理论上就能用多台便宜货的总带宽顶一台高价大带宽 VPS。

先试的方案:sing-box + HAProxy

第一版搭了两个容器:

sing-box 跑四路 Hysteria2 客户端,各占一个本地 SOCKS5 端口,HAProxy 在前面做 roundrobin 分发。

用了一阵发现几个烦人的地方:

  • 加一台 VPS 要改两套配置(sing-box 的 outbound + HAProxy 的 backend)
  • 想临时关掉某个节点?没有 toggle,得改配置重启
  • HAProxy 不支持热重载,改完必须重启整个链路
  • 没有 Web 界面,想看各节点状态只能看日志

本质问题:两个容器各管各的,生命周期不同步,运维起来像在同时维护两个系统。

干脆自己写一个:Rally-go

后来干脆用 Go 写了个单二进制,把 Hysteria2 客户端和 SOCKS5 负载均衡塞到一起:

和旧方案的核心区别:

  • 一个进程搞定所有事,不再伺候两个容器的编排
  • 负载均衡从 roundrobin 换成 leastconn(谁闲谁接活)
  • 加了个简陋的 Web UI,实时看各节点流量和延迟
  • 支持 SIGHUP 热重载,改完配置发个信号就行
  • 每条线路可以单独 toggle,不用重启

配置长这样

yaml
listen: ":1080"
web_listen: ":9090"

backends:
  - name: "节点A"
    addr: "node-a.example.com:443"
    auth: "your-auth-password"
    insecure: true
    down_mbps: 50
    up_mbps: 10
  - name: "节点B"
    addr: "node-b.example.com:443"
    auth: "your-auth-password"
    insecure: true
    down_mbps: 50
    up_mbps: 10

Hysteria2 的 bandwidth auto-detect 有个坑:它会严重低估可用带宽。一开始没设 down_mbps,某一台节点只能跑到 375KB/s,设了之后直接飙到 4MB/s。手上有的几台 VPS 最后都手动给了值。

信号处理踩了个坑

热重载实现的时候犯了个低级错误。一开始 ReloadConfig 里直接调了 r.Run(),这是个阻塞调用——结果 SIGHUP 信号处理协程被卡住了,第二次发信号根本没反应。

修起来很简单:go r.Run() 丢到独立协程里跑,别阻塞信号循环。同时给 Forwarder.Stop() 加了 sync.Once 保证幂等,避免重复关闭 panic。

go
go func() {
    r.Stop()
    if err := r.Run(); err != nil {
        log.Fatal(err)
    }
}()

Docker 部署

yaml
services:
  rally:
    image: ghcr.io/your-org/rally-go:latest
    container_name: rally
    network_mode: "host"
    restart: unless-stopped
    volumes:
      - ./rally.yaml:/etc/rally.yaml:ro

实测能跑多少

拿三台美西 VPS 试了一下 YouTube 视频下载:

| 节点 | 延迟 | 单节点带宽 | |------|------|-----------| | 节点A | 200ms | 111Mbps | | 节点B | 190ms | 119Mbps | | 节点C | 210ms | 123Mbps | | 聚合 | - | ~120Mbps |

leastconn 均分得还不错,三路误差不到 2%。

接入 MeTube 后,下载速度从单节点的 ~5MB/s 提到 ~15MB/s,一个 2GB 视频从 7 分钟缩到 2-3 分钟。

目前状态

代码放 GitHub 了(私有仓库,MIT),Docker 跑在 NAS 上,日常在用。遇到过某个节点因为 Cloudflare 封 HTTP 000 被踢出池子,其他三路一直稳定。