# Realtime Gateway + Cloudflare Tunnel 本机联调说明
> 文档版本：v1.0
> 最后更新：2026-04-02 08:28:05


本文档说明如何在**不正式部署到线上服务器**的前提下，把本机的 `realtime-gateway` 暴露给外部设备或远程联调方。

适用场景：

- 真机调试
- 外网联调
- 家长端 / 场控端远程验证
- 演示环境

不适用场景：

- 正式生产实时链路
- 严格 SLA 场景
- 高稳定长时间压测

---

## 1. 推荐结论

当前阶段建议：

1. 网关继续运行在本机
2. 本机使用 Cloudflare Tunnel 暴露新网关
3. 旧模拟器继续本地运行
4. 旧模拟器通过桥接把数据发给本机新网关
5. 外部客户端只访问 Cloudflare Tunnel 暴露出来的 `wss` 地址

这样做的好处：

- 不需要先买独立服务器
- 不需要先做正式公网部署
- 不改变当前本机开发结构
- 新旧链路可以并行使用

---

## 2. 本机网关配置

推荐先使用：

[tunnel-dev.json](D:/dev/cmr-mini/realtime-gateway/config/tunnel-dev.json)

这个配置相比开发默认配置更适合 Tunnel 联调：

- 端口改为 `18080`
- 关闭匿名 consumer
- token 不再使用默认开发值

建议先把其中的 token 改成你自己的值。

启动方式：

```powershell
cd D:\dev\cmr-mini\realtime-gateway
go build -o .\bin\gateway.exe .\cmd\gateway
.\bin\gateway.exe -config .\config\tunnel-dev.json
```

本机地址：

- HTTP: `http://127.0.0.1:18080`
- WebSocket: `ws://127.0.0.1:18080/ws`

---

## 3. Quick Tunnel 方案

Quick Tunnel 最适合当前阶段。

Cloudflare 官方文档说明：

- Quick Tunnel 用于测试和开发，不建议生产使用
- 可以直接把本地 `http://localhost:8080` 之类的地址暴露出去
- 命令形式是 `cloudflared tunnel --url http://localhost:8080`

来源：

- [Quick Tunnels](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/trycloudflare/)
- [Set up Cloudflare Tunnel](https://developers.cloudflare.com/tunnel/setup/)

### 3.1 启动 Quick Tunnel

你的网关如果跑在 `18080`：

```powershell
cloudflared tunnel --url http://localhost:18080
```

启动后，`cloudflared` 会输出一个随机 `trycloudflare.com` 域名。

例如：

```text
https://random-name.trycloudflare.com
```

对应的 WebSocket 地址就是：

```text
wss://random-name.trycloudflare.com/ws
```

注意：

- 对外使用时，WebSocket 必须写成 `wss://`
- 本地 origin 仍然是 `http://localhost:18080`

### 3.2 Quick Tunnel 限制

Cloudflare 官方说明里，Quick Tunnel 当前有这些限制：

- 仅适合测试和开发
- 有并发请求上限
- 不支持 SSE

因此它适合：

- 临时分享联调地址
- 验证 WebSocket 接入
- 短期演示

不适合拿来当正式生产入口。

另外，官方文档提到：

- 如果本机 `.cloudflared` 目录里已有 `config.yaml`，Quick Tunnel 可能不能直接使用

来源：

- [Quick Tunnels](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/trycloudflare/)

---

## 4. Named Tunnel 方案

如果你后面不想每次拿随机域名，可以改用 Named Tunnel。

这时推荐的本地 `cloudflared` 配置示例在：

[config.example.yml](D:/dev/cmr-mini/realtime-gateway/deploy/cloudflared/config.example.yml)

示例内容：

```yaml
tunnel: YOUR_TUNNEL_ID
credentials-file: C:\Users\YOUR_USER\.cloudflared\YOUR_TUNNEL_ID.json

ingress:
  - hostname: gateway-dev.example.com
    service: http://localhost:18080
  - service: http_status:404
```

关键点：

- Tunnel 把 `gateway-dev.example.com` 映射到本机 `http://localhost:18080`
- 最后一条 `http_status:404` 是 catch-all

Cloudflare 官方文档对 published application 和 ingress 的说明见：

- [Set up Cloudflare Tunnel](https://developers.cloudflare.com/tunnel/setup/)
- [Protocols for published applications](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/protocols/)

启动后，对外 WebSocket 地址就是：

```text
wss://gateway-dev.example.com/ws
```

---

## 4.1 外部 consumer 小工具

如果你要在另一台机器上用仓库里的 `mock-consumer` 做联调，推荐复制：

[consumer-tunnel.example.json](D:/dev/cmr-mini/realtime-gateway/config/consumer-tunnel.example.json)
[consumer-gps-heart.example.json](D:/dev/cmr-mini/realtime-gateway/config/consumer-gps-heart.example.json)

填好实际的公网地址和 token，例如：

```json
{
  "url": "wss://gateway-dev.example.com/ws",
  "token": "your-consumer-token",
  "deviceId": "child-001",
  "topics": [
    "telemetry.location",
    "telemetry.heart_rate"
  ],
  "snapshot": true
}
```

然后运行：

```powershell
cd D:\dev\cmr-mini\realtime-gateway
go run .\cmd\mock-consumer -config .\config\consumer-tunnel.example.json
```

命令行参数会覆盖配置文件中的同名项，所以临时换 `deviceId` 时可以直接追加：

```powershell
go run .\cmd\mock-consumer -config .\config\consumer-tunnel.example.json -device-id child-002
```

如果只想临时直接从命令行看 GPS 和心率：

```powershell
go run .\cmd\mock-consumer -url wss://gateway-dev.example.com/ws -token your-consumer-token -device-id child-001 -topics telemetry.location,telemetry.heart_rate
```

---

## 5. 旧模拟器如何配合

旧模拟器仍然本地运行：

```powershell
cd D:\dev\cmr-mini
npm run mock-gps-sim
```

然后在旧模拟器页面的“新网关桥接”区域里填：

- 网关地址：`ws://127.0.0.1:18080/ws`
- Producer Token：与你 `tunnel-dev.json` 里配置一致
- 目标 Device ID：按你的联调对象填写
- Source ID：例如 `mock-gps-sim-a`

这里要注意：

- 旧模拟器桥接到的是**本机网关地址**
- 外部消费者连接的是**Cloudflare Tunnel 暴露的公网地址**

不要把旧模拟器桥接目标直接写成公网 `wss` 地址。  
旧模拟器和网关都在本机，直接走本地回环最稳。

---

## 6. 推荐联调拓扑

```text
旧模拟器页面
  -> 本机 mock-gps-sim
  -> 本机 realtime-gateway (ws://127.0.0.1:18080/ws)
  -> Cloudflare Tunnel
  -> 外部 consumer / 真机 / 调试端 (wss://<public-host>/ws)
```

这样职责最清晰：

- 旧模拟器只负责发模拟数据
- 新网关负责实时中转
- Cloudflare Tunnel 只负责把本机网关暴露到外部

---

## 7. 当前阶段的安全建议

即使只是联调，也建议至少做到：

- `allowAnonymousConsumers = false`
- Producer / Consumer / Controller token 全部替换
- 不把默认 token 长期暴露在公网链接中
- Tunnel 只暴露新网关，不一定要暴露旧模拟器 UI

如果只是你自己本机调试：

- 旧模拟器 UI 保持本地访问即可
- 只把 `realtime-gateway` 暴露出去

---

## 8. 结论

当前阶段最推荐的方案是：

1. 本机用 [tunnel-dev.json](D:/dev/cmr-mini/realtime-gateway/config/tunnel-dev.json) 启动新网关
2. 本机旧模拟器桥接到 `ws://127.0.0.1:18080/ws`
3. 用 `cloudflared tunnel --url http://localhost:18080` 先跑 Quick Tunnel
4. 外部客户端使用 `wss://<quick-tunnel-domain>/ws`
5. 等你需要固定域名或更稳定的入口时，再切换 Named Tunnel

这条路径最轻、最稳，也最符合你现在“先不正式上线”的目标。



