❝脚步不停,终达卓越!更多优质文章及代码资源详见公众号 《开源519》
前段时间,项目上出现一个严重的设备掉线问题,导致客户投诉,简单记录一下前因后果。
每次遇到这种概率性问题,解决起来都比较困难,主要原因有下:
1. 测试同事,好不容易协调好设备上线,远程取了日志后,立马呈给开发大佬排查。
2. 像这种问题,首先过滤socket状态日志。从日志上看,似乎是远程服务器断开了客户端的连接:
recv error: Connection reset by peer
3. 于是,压力给到服务器侧研发。服务器侧同事拿到问题后,给出结论:是因为存在相同的终端信息登入,导致设备被踢。 4. 问题甩到了终端侧研发,看到此结论,首先怀疑是否有不同的终端误刷了相同的设备ID。 5. 测试同事坚决否认:货设备ID管控,不可能误刷相同信息。 6. 终端研发无奈只能结合日志,逐行排查控制业务相关的代码。
7. 星光不负赶路人,经过苦思冥想和 "地中海" 加成,终于在代码实现上,找到一些蛛丝马迹。
问题代码大致如下:
void DeviceConnection::reconnect() {
...
if (m_worker.joinable()) {
... // 省略 触发socket关闭
m_worker.detach();
}
m_worker = std::thread([this] {
... // 省略 socket连接
});
}
最终定位到此处,从代码上分析:
socket,然后回收线程。detach是非阻塞的,并不会等待socket完全关闭。这就导致了在新线程创建新的socket连接时,上一个socket可能还没关闭。socket关闭的够快,就不会存在这种问题。而本地复现时,CPU处理的业务往往没有实际场景复杂,故难以复现。找到问题了,修复起来也就快了。主要有两种方式:
detach()替换成join(),可快速解决问题。鉴于当前需快速、稳妥地修复问题,选择了第一种方案。虽然从长远看第二种方案更优,但受限于项目进度,第一种方案是当下最优解。后续类似功能的开发,可参考第二种方案。
回头再看这个紧急问题,根源在于误用了线程detach()。后续开发中,回收线程尽量不要使用detach()。
用心感悟,认真记录,写好每一篇文章,分享每一框干货。
更多文章内容包括但不限于C/C++、Linux、开发常用神器等,可进入“开源519公众号”聊天界面输入“文章目录” 或者 菜单栏选择“文章目录”查看。公众号后台聊天框输入本文标题,在线查看源码。 在聊天框输入“开源519资料” 获取Linux C/C++ 学习资料书籍。