之前做过一个即时通讯小程序, 用户要求能够实时收到消息通知。当时我选用了一种方案, 然而上线后的第一周就出现了问题, 用户反映消息接收不到, 存在有时连接不上的情况, 以及有时连接上了却又自动断开的状况。经过三天的排查, 发现根本原因并非网络方面的问题, 而是小程序自身的几个固有机制我没有处理妥善。
看起来跟浏览器有相似之处的小程序, 在行为方面存在着关键的差异点我所经历过的那些坑, 基本上全都集中在了生命周期配合以及重连策略这两个方面。
坑一:切后台后 会被销毁
许多人觉得切至后台又切回来, 连接仍处在——实则并非如此。小程序在切至后台超出一定时长后(iOS大概30秒, 大概10分钟), 会将连接切断。这并非网络方面的问题, 而是微信主动进行断开操作的。
这是小程序跟浏览器之间, 最为显著的差异之处: 于浏览器当中, 当你开启一个页面时, 连接在该页面尚未关闭以前会始终保持存在状态。而在小程序里面, 则是当你的页面遭遇切换、被实施销毁操作, 又或者 app 进入到后台运行的情况时——连接都存在着断开的可能性。
一套解决办法乃是于, 整个生命周期范围之内, 去核查连接的处于何种状态, 要是察觉到断开了, 便自行开展再次连接的操作。
// 页面显示时检查 WebSocket 状态 Page({ onShow() { if (socketTask && socketTask.readyState !== WebSocket.OPEN) { this.reconnect() } }, onHide() { // 不要在这里关闭连接——用户可能只是短暂切走 // 让心跳机制在后台检测 }, reconnect() { this.initWebSocket() } })要留意, 别于其中主动把连接关闭, 因为用户有可能仅仅是切换出来去回个消息, 过几秒就会回来, 而何时真正断开且需要重建是由心跳机制来决定的。
坑二:心跳机制——不是所有断开都有回调
某些断开情形下, 小程序的回调不会被触发。举例而言, 当手机从4G切换至WiFi网络时, TCP连接会断开, 然而小程序或许不会触发事件。我曾碰到这样的状况: 用户步入电梯, 信号中断, 走出时连接已然失效——可前端却显示“连接正常”。
解决方案是加一个应用层的心跳检测:
// 心跳检测——应用层保活 initWebSocket() { const ws = wx.connectSocket({ url: 'wss://api.example.com/ws' }) ws.onOpen(() => { this.heartbeatTimer = setInterval(() => { ws.send({ data: JSON.stringify({ type: 'ping' }) }) }, 30000) // 每 30 秒发一次心跳 }) ws.onMessage((res) => { const data = JSON.parse(res.data) if (data.type === 'pong') { this.lastPongTime = Date.now() // 记录最后一次 pong 时间 } }) // 单独检测:如果超过 60 秒没收到 pong,判为断开 this.checkTimer = setInterval(() => { if (Date.now() - this.lastPongTime > 60000) { ws.close() this.reconnect() // 触发重连 } }, 10000) }心跳的逻辑是: 网络客户端每隔半分钟发送一回ping, 网络服务端随后回应pong。要是超出一分钟未收到pong, 不论是否引发相关状况, 均判断为连接结束, 随后自动再次尝试重新连接。
坑三:wx. 的并发限制
这存在一个相对较为隐蔽的问题, 若是你于短时间阶段之内进行多次调用 wx., 像是快速且反复地进出页面这种情况, 唯有第一个进行连接才会成功, 后续的那些会被微信采取拦截措施。那小程序所具有的并发限制是, 一个小程序在同一时间之中, 最多只能够拥有 5 个连接。
但若你所写的重连逻辑欠缺严谨性, 即每次都会去创建一个全新的连接, 那么在同一个时间段里面, 便有可能同时存在着多个连接, 之中有一些“连接”, 其在服务端业已断开, 然而小程序端却还一无所知, 占据着连接数而不予以释放, 最终致使新连接无法创建出来。
解决方案是每次创建新连接前显式关闭旧连接:
// 重连前确保旧连接已关闭 reconnect() { if (this.socketTask) { this.socketTask.close() // 关闭旧连接 this.socketTask = null } // 等 1 秒再创建新连接——避免短时间内连续重连 setTimeout(() => this.initWebSocket(), 1000) }核心总结: 小程序, 存在三个关键点, 其一为页面切换时的连接保活, 其二是应用层心跳检测, 此检测不依赖回调, 其三为重连前关闭旧连接, 以此避免超过并发限制。做好这三点后, 我那个即时通讯小程序的连接成功率从百分之七十提到了百分之九十五以上。
觉得有用?点个赞,转发给需要的朋友。
评论区说说:你在小程序 上踩过什么坑?