做一个「手机语音同步到 PC」的小工具
最近我陆续把一个 1K DAU 的小产品——“咕咕同步”——从 Node.js 重写为 Go,并全线开源了。
问题
每天写长文或对话时,我要么坐在电脑前,要么靠在床上。真正难受的是这种场景:手机靠近嘴边,用任意输入法(讯飞、搜狗、iOS 原生)小声语音输入,文字需要立刻出现在电脑上。
已有的方案各有问题:
- 微信灌输入法太重,还带广告
- Google Docs / Notion 同步有延迟,打电话时不能用
- 要么是走 ASR 云服务——我并没有转写需求,输入法已经帮我做了
需要的是一个纯同步通道:手机发什么,PC 立刻收到什么。
做法
协议很简单:WebSocket 房间制,手机和 PC 扫同一个二维码进"房间",全量文本 + 光标位置(p)实时互发。不走 OT,不走 CRDT。
后端用 Go + gorilla/websocket。一开始我是用 Node.js 写的,在生产跑了一个月,发现:
- PM2 管理的 Node 进程内存泄漏不明显但有点烦
- goroutine 模型做连接管理比 Event Loop 清晰太多了
- 单二进制部署,
systemd直接管,零运行时依赖
关键决策:
- 消息是完整文本,不是 diff(文本本身就在手机端编辑器里,同步成本零)
- 心跳 3s ping / 6s pong——我从 30s 误设为 2s 最终调优到 3/6,太紧会把移动端 radio 吵醒耗电
- 房间 TTL 30 分钟,没人自动回收
- 限速 10 conn/min + 100 msg/min per IP,放公网基本够用
前端零构建。HTML + CSS + JS 分离,改样式读 CSS、改逻辑读 JS,比打包一大坨明白太多了。
架构图
手机 (IME 语音输入)
→ WebSocket 发送 {"t":"u","d":"完整文本","p":光标位置}
↓ (Go server: 单房间广播)
PC (浏览器 textarea)
→ 远端接收 → applyRemoteText + setSelectionRange(p, p)
无 Redis,无数据库,无消息队列。房间在内存,进程重启自然清空。
成果
- 单二进制约 9.7 MB,
systemd一键启停 htop看 RSS 稳定 ~50MB,没发现漂移- 高峰期 ~100 CCU 无压力
- 全链路延迟肉眼 < 100ms(从手机抬起到 PC 上出现文字)
开源
我把完整代码放在了 GitHub(https://github.com/NoelJudeNoel/sync-pad),欢迎有类似"需要轻量实时同步通道"场景的同学 fork,改完 WebSocket 后端做自己的协议层就行。
如果不是这个需求,我可能会选 Firebase / Supabase Realtime 更省力。但既然我对延迟和架构有洁癖,Go 这条路走下来是值得的。
项目地址、部署 Node.js → Go 的完整 diff 在 GitHub 仓库里,欢迎精读代码找 bug。