created by snowlyg.
最初采用 WebRTC + WebSocket + Node.js 的架构。在复杂/不稳定的局域网环境中,WebSocket 这类长连接协议在程序长时间运行后,容易出现:连接断开后无法及时重连,甚至出现“已连接但无法通信”的假连接状态。
同时,网络侧变更(如防火墙规则调整、链路抖动、网络故障等)会进一步放大上述问题,往往需要人工介入才能恢复视频电话的正常使用。

原方案架构图
此种架构下,WebSocket 只能由客户端主动发起连接:客户端自下而上逐级向机房服务器的服务端建立连接;服务端无法从机房服务器侧“主动向客户端发起连接”。

websocket连接示意图
而且因为信息安全要求,机房服务器与客户端设备之间至少有一层以上的防火墙隔离。当防火墙上设置了某些不利于长连接协议的优化配置(或其他网络侧原因),会导致客户端与机房服务器之间异常断开通信。
如果客户端没有及时触发 WebSocket 重连,甚至无法自动重连,那么在没有人为干预的情况下,设备功能将无法自动恢复正常。

websocket 断开示意图
在局域网内所有设备 IP 信息固定(或可稳定发现)的前提下,在安卓应用中集成 gRPC 协议网关,采用可双向通信的 gRPC 来实现设备间的互联:
「客户端可以主动向服务端发起连接,服务端也可以主动向客户端发起连接」
从而避免出现 WebSocket 等长连接协议断连之后,必须等待客户端主动重连才能恢复的问题。
虽然 WebSocket 是实现聊天室的常用方案,但在需要主动双向通信的场景下,gRPC 往往是更好的选择。开发方面采用 gomobile 开发并生成 Android SDK:虽然性能上会有部分损失,但由于 Go 的跨平台特性,更便于开发配套的监控、测试和后期运维工具;同时更简洁的语法也更利于提升单元测试覆盖率,保证后续程序稳定、快速地更新迭代。

改进方案架构图
改进后的方案将 gRPC 服务集成到客户端 APP 中。这种架构下,所有在线设备都可以两两互联,由任意一方主动发起连接请求来建立通信,从而提升在复杂局域网环境中的可用性与可恢复性。

GRPC连接示意图
<aside> 💡
局域网内场景虽然并发并不高,但是每次拨打需要多次的信令交互来完成 WebRTC 的 PEER 连接。为了支持短时间多次拨打,或同一时间多个设备拨打同一个设备,主节点在每一次通话中都会维护“聊天室/会话”的映射关系(例如使用哈希表将房间与 UID 关联),以便信令更高效、可追踪地完成交互。
</aside>