当前位置:网站首页>记一次坎坷的调试|Mosquitto通过TLS连接EMQ时阻塞的问题
记一次坎坷的调试|Mosquitto通过TLS连接EMQ时阻塞的问题
2022-08-08 19:31:00 【奔跑的码仔】
最近两天在调试一个关于嵌入式Linux系统环境时,在系统开机之后,Mosquitto通过tls连接MQTT服务器(EMQ)时,创建MQTT连接总是阻塞的问题,现记录一下调试过程及解决问题的步骤。
先说下开发调试环境:
- 硬件平台:EXP imx.6ull
- 内核版本:4.1.15
- rootfs:基于buildroot创建
- mosquitto:2.0.11
- openssl:1.1.1
- MQTT服务器:支持TLS服务的EMQ
问题表象
linux系统开机之后,出现shell登录提示符之后,调用mosquitto_connect和EMQ建立基于TLS的连接,mosquitto_connect调用之后阻塞,大约90秒,该函数调用才会返回,并且报错。之后,mosquitto会触发重连机制,再次连接EMQ服务器,连接成功。这时,如果重新发起向EMQ的连接请求,mosquitto_connect不会阻塞。
起初怀疑的几点原因:
- 系统启动之后,网络还未初始化完成,就调用了mosquitto_connect,连接EMQ可能失败。尝试测试方法:完善网络初始化流程,优化mosquitto_connect调用时序,经测试,无效。
- 怀疑EMQ服务器问题,尝试测试方法:使用其他设备测试对同一EMQ发起TLS连接,没有问题。
- 开机时,系统时间未同步(1970-01-01 08:00:00),联网之后,ntpd服务才会同步网络时间到本地,所以中间会导致mqtt连接阻塞,经过思考,如果是系统时间的问题,mosquitto_connect应该返回TLS校验证书失效的错误提示,而不是阻塞。
- 怀疑应用程序其他部分干扰mosquitto_connect的调用流程,使用mosquitto_pub直接向EMQ发布消息,发现现象一样。
上述几轮怀疑和测试之后,调试陷入了僵局,我有些郁闷,经过思考之后,使出了杀手锏:就是最笨的,也是最有效的方法:向mosquitto_connect执行流程插入调试日志。其实,有些时候,看似最笨的方法,却是最为有效的调试方法,只要方向对,花些时间和精力也是值得的。
调试步骤
按照mosquitto_connect的调用流程,增加日志,确定阻塞位置,这里有一个调试的小技巧,就是先主干,再细节,即,先在需要调试的主路径上的关键位置添加日志,然后,编译调试,从而可以确定问题的大体位置,然后,在子流程上再次添加日志,再编译,在调试,这样一步一步递归下去,就会较为快速的定位问题位置。应用该方法,我最终确定了mosquitto_connect阻塞问题位置是:
SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_client_method());
简单介绍一下SSL_CTX_new,其主要是openssl创建用于TLS通信的控制块,为了确认问题,我编写了测试程序,再次确认是不是该函数导致的问题。代码如下:
#include <openssl/conf.h>
#include <openssl/engine.h>
#include <openssl/err.h>
#include <openssl/ui.h>
#include <openssl/ssl.h>
void ssl_test(void)
{
printf("ssl_test:TP0.\n");
SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_client_method());
(void)ssl_ctx;
printf("ssl_test:TP1.\n");
}
程序很简单,就是为了确认罪魁祸首是不是SSL_CTX_new。Linux开机后,立即执行改测试函数,首先打印"ssl_test:TP0",大约90秒之后,打印"ssl_test:TP1.”,问题位置确认。
发现问题
如果你是一个SSL/TLS机制小白的话,或者说,只了解SSL/TLS交互流程的话,分析SSL_CTX_new的实现原理有点强人所难,我属于这类人,所以,调试再次陷入困境。
就在一筹莫展之际,我发现了一个有趣的现象:发现终端只要打印一行:
random: nonblocking pool is initialized
SSL_CTX_new就会返回,多次测试之后,确认这两者的先后关系。可见,random的初始化和SSL_CTX_new的阻塞存在关系。之后的调试就比较顺利了,实在是山重水复疑无路,柳暗花明又一村。
通过查阅资料,发现关于random: nonblocking pool is initialized有以下几种主流的解决思路:
1. Linux随机数nonblocking pool快速初始化
2. random: nonblocking pool is initialized,只是开机不打印该提示,没有实际解决问题
3. Linux随机数nonblocking pool快速初始化
经过分析,1、3和该问题的现象基本一致:可以确定,4.1.15版本的内核random驱动存在问题,导致系统启动时,random初始较慢(90s),而SSL_CTX_new在初始化的时候,用到了random,其需要等待random初始化完成,才能继续执行,从而导致SSL_CTX_new阻塞,最终导致mosquitto_connect阻塞。
解决问题
修改random内核驱动代码:drivers/char/random.c,找到函数add_interrupt_randomness,修改如下的代码:
原始代码:
if ((fast_pool->count < 64) &&
!time_after(now, fast_pool->last + HZ))
return;
修改后的代码:
if ((fast_pool->count < 64) &&
!time_after(now, fast_pool->last + HZ) &&
nonblocking_pool.initialized)
return;
注意:本文内核版本是4.1.15,经过查阅内核代码,发现在较新的内核版本,该bug已解决!
编译,替换内核,经测试问题解决!
后记
反观这个问题的解决过程,不知道你发下了没有,一开始,问题的现象和引起问题的根源,可以用俗语“八竿子打不着”来形容,每次感觉问题快要解决了,最后,还是竹篮打水一场空,那种失落感、焦虑感会越来越浓。其实,事后分析解决问题的过程,不难得出解决问题的关键:首先,保持冷静,确定方向,然后,使用科学的方法,坚持不懈的向前推进,相信自己,最后,问题最终会被解决,只是时间的问题。
边栏推荐
猜你喜欢
随机推荐
Dry goods: design high concurrency architecture from scratch
数组!!!
ptorch
C语言初阶-结构体
5 IPOs, Internet home improvement is not as simple as Tubatu thinks
synApps -- Autosave
Securities account is better to choose which brokerage platform, which is more safe
shell九九乘法口诀表
Oracle存储修改以前的历史记录,怎么查找?
证券开户选哪个券商平台比较好,哪个更安全
Ability in general, but it can be large horizontal jump freely?Where is the better?
BP neural network
从 VLAN 到 IPVLAN: 聊聊虚拟网络设备及其在云原生中的应用
[MRCTF2020]你传你码呢
riscv-gnu-toolchain下载安装
odoo 登录布局调整
nyoj685 查找字符串(map)
如何在Firewalld中为特定IP地址开放端口
RADIUS服务器的演变过程
达梦数据库 DmAPservice服务,启停影响 DMSERVER库服务吗?