MQTT版本升级过程及源码解析
MQTT版本升级过程及源码解析
首先说一下为什么要写这篇文章呢,在我发现网上对MQTT的文章介绍实在太少了,可能也是使用这个的频率比较低吧!还有对问题的定位以及解决的方式和办法也太少了,所以特意写这篇文章希望能作出一些贡献,帮助到一些需要的人。
主要记录一下MQTT在原先1.2.0版本使用过程中出现的问题,排查问题到升级1.2.1版本过程中出现的问题,通过源码一步步排查出最后的问题点,直到符合预期目标。
<!-- https://mvnrepository.com/artifact/org.eclipse.paho/org.eclipse.paho.client.mqttv3 -->
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.1</version>
</dependency>
MQTT的搭建及SSL认证可以参考这个博客:https://www.cnblogs.com/yueli/p/7490453.html 在这不仔细阐述
Qos介绍
在开头先了解一下Qos的一些含义,也是这个问题为导火线
Qos = 0 最多一次的传输
发布者PUBLISH消息到服务器(broker),发送即丢弃。没有确认消息,也不知道对方是否收到。网络层面,传输压力小
Qos = 1 至少一次的传输
发布者发布消息保存消息,服务器(broker)接收到消息,服务器(broker)PUBLISH到订阅者,服务器(broker)回一个PUBACK信息到发布者让删除消息,然后订阅者接收消息后PUBACK给服务器让删除消息。如果失败了,在一段时间确认信息没有收到,发送方都会将消息头的DUP设置为1,然后再次发送消息,消息最少一次到达服务。例如网络延迟等问题,发布者重复发送消息,订阅者多次订阅重复消息
Qos = 2 只有一次的传输
其实Qos = 2 只是在 1 的基础上做了改掉的赶脚,在发布者PUBLISH到服务器之后多了消息的确认以及多了消息msgID的缓存,重复信息的去重。在服务器PUBLISH到订阅者之后也多了消息的确认。
三种情况的区别
0 没有保存message,没有重发机制,啥事也不知道,1和2 的发布者和服务器有保存message,发布者有重发机制,服务器都有PUBLISH之后的PUBACK的确认机制,但是2的服务器多了缓存msgID的一项功能,提供了去重功能,防止了消息的重复发送,以及消息的接收的确认机制。订阅者这边不过多的介绍,感兴趣再去了解。
MQTT1.2.1版本出现的问题
问题的描述:因为项目中使用MQTT通信的地方比较多,一般都是以Qos = 0 的形式,这段时间发现会时常提醒报错Too many publishes in progress (32202),看了一下源码报错地方

从这里可以判定actualInFlight超出自己设置的maxInflight最大值导致的,尝试加大maxInflight也无用,只是延迟报错的时间而已
问题排查过程
首先上网搜一下是否有类似问题出现,果不其然有个哥们碰到了,博客地址:https://blog.csdn.net/lblblblblzdx/article/details/81159478 此文章给我很大帮助,感谢博主,但是最后的解决方案不是很好。
跟踪发布过程的源码逻辑
第一步:publish的过程
//
public IMqttDeliveryToken publish(String topic, MqttMessage message, Object userContext,IMqttActionListener callback)
//
public void sendNoWait(MqttWireMessage message, MqttToken token)
//
void internalSend(MqttWireMessage message, MqttToken token)
//
public void send(MqttWireMessage message, MqttToken token)
第二步:所有Qos类型,在publish消息的顶级父类中的构造器默认设置msgId = 0

第三步:消息在send方法中做了主要处理

第四步:根据上面的Qos的介绍,说明我们publish的消息在这个地方全部都缓存在tokens这里,其实就是放入Hashtable中,不管什么等级的Qos

在这几个步骤中,已经缓存好信息,准备异步发送,其中的lock机制就不多去解释
第五步:异步发送,其实主要是在客户端链接完成的时候就已经开始监听了,connect流程
//
public IMqttToken connect(MqttConnectOptions options) //2 异步链接
ConnectActionListener connectActionListener = new ConnectActionListener(this, persistence, comms, options,userToken,userContext, callback, reconnecting);
connectActionListener.connect(); //3 客户端的通信链接,包括发送和接收
ClientComms.connect(options, token); //4 个人理解是 将通信信息塞进线程池中,分别开启发送和接收的线程处理
ConnectBG conbg = new ConnectBG(this, token, connect, executorService);
conbg.start();
第六步:在ConnectBG的run中 new CommsSender. start线程run中while循环发送的信息流程,一直发送消息中

第七步:在notifySent的方法中判断Qos = 0 的作出了判断及操作

这些差不多就是发布过程的主题流程结构,了解这些才会知道让你解决问题更加的容易
问题点
在上面的第六步的图中黄色箭头指出了问题出现的点,主要是在大数据量高并发的时候,因为在Qos = 0 的时候,在tokens(Hashtable)中的key一直是0,默认初始化。后面的流程中并没有改变过,在黄色那块tokenStore.getToken 在发送之后才remove数据,但是多条数据高并发的时候,在remove数据之后,后一条在get的时候会出现空的状况,不发送信息,导致actualInFlight没有减,一直增加,一定时间后就会超出最大值。Qos =1 、2 是不会发送这样的情况,因为他们的messageId是唯一的。
解决办法
- 将Qos设置为1 (这是网上主流的解决办法,但是这个太耗费资源问题,在我看来只是规避问题点而已)
- 升级MQTT版本1.2.1 (这个版本解决了刚才说的bug)
- 也可以想想在不升级版本的情况下如何去改善这个问题,重写那些类可以实现···
解决过程
既然说了第一种解决办法不是很赞同,那就直接进去第二种办法吧。升级到1.2.1版,时间:Feb, 2019,但是在升级版本的时候又出现了一些问题,因为改动还是有点多的。
接下来说说1.2.1版本的改动了什么呢
首先主要改动的是在Qos = 0 的不放入tokens中了,首先想到的是不会get到了,也不用担心重复了,直接从集合pendingMessages中拿

然后从信息的自身获取数据的token,获取不到再去tokens中拿

以上两个就是主要解决这个高并发冲突的原因
跳入另一个坑中···
为什么这么说呢,在我们开发的意识中,升级版本怎么也要向下兼容吧,那就顺其自然的换个版本就完事了,结果一跑起来,一堆红色的出现,那心情···我太南了。以我的性子,就是不能惯着她,继续深挖为什么,既然坑已经有了,就不怕有多深。
另一个坑的过程
第一时间也是上网搜一些为啥,大家的解决也差不多,都是SSL验证出现问题,但是解决方案也是五花八门
解决方案
第一种:设置系统属性 类似这样的文章 https://blog.csdn.net/hxpjava1/article/details/77937026
第二种:有看了一些代码了,就是重写X509TrustManagerImpl,绕过SSL的验证,试过有用,类似这样的文章 https://blog.csdn.net/iverson_AL/article/details/100669777
第三种:再深入看一些源码,你会看到会什么会报错,为什么会验证,主要是你的属性没有设置好,采用了默认验证导致
解决过程
这里主要说一下解决的过程,如何从这些网络文章种找出适合自己的出路。
首先 第一种我就不咋喜欢,动不动就设置了系统全局属性,第二种方案,有两种可能性,一是这个api真的有问题或是不符合自己的项目需要重写代码逻辑结构,二是在不了解的情况下直接绕过验证。我在测试第二个的时候就是如此只是稍微看了一下源码,没有深入看进去,试了一下,果真可以实现我想要目的。
但是过后又想了一下,不该如此,既然开源出来的东西,不可能如此**,应该会有什么地方可以简单设置一下的。既然有这个想法就一直深入探究下去,果不其然,真相出来了。
在1.2.1版本中MqttConnectOptions 的httpsHostnameVerificationEnabled属性默认true,导致不是Https的被验证不通过导致的,也可能MQTT开发人员安全意识很强,在1.2.0版本中没有这个概念存在,所以在版本升级的时候需要加上MqttConnectOptions.setHttpsHostnameVerificationEnabled(false);
以下源码查看的过程




若是有Https证书是不会有问题的。
总结一下
其实很简单的问题,居然整的时候那么复杂,原因是我们不够强,面对源码的时候还是比较害怕的,还有比较懒吧。
整个过程比较繁琐,啰嗦吧,耐心看下来,应该有收获。
操作总结:
- 升级MQTT版本1.2.1
- 若不是https的需要设置为false
- 不要走,跑起来
扩展点及疑惑地点可以供参考

转载请注明出处 https://www.cnblogs.com/zhouguanglin/p/11986446.html
MQTT版本升级过程及源码解析的更多相关文章
- QT源码解析(一) QT创建窗口程序、消息循环和WinMain函数
QT源码解析(一) QT创建窗口程序.消息循环和WinMain函数 分类: QT2009-10-28 13:33 17695人阅读 评论(13) 收藏 举报 qtapplicationwindowse ...
- 解析jQuery中extend方法--源码解析以及递归的过程《二》
源码解析 在解析代码之前,首先要了解extend函数要解决什么问题,以及传入不同的参数,会达到怎样的效果.extend函数内部处理传入的不同参数,返回处理后的对象. extend函数用来扩展对象,增加 ...
- QT源码解析(七)Qt创建窗体的过程,作者“ tingsking18 ”(真正的创建QPushButton是在show()方法中,show()方法又调用了setVisible方法)
前言:分析Qt的代码也有一段时间了,以前在进行QT源码解析的时候总是使用ue,一个函数名在QTDIR/src目录下反复的查找,然后分析函数之间的调用关系,效率实在是太低了,最近总结出一个更简便的方法, ...
- 分布式事务_02_2PC框架raincat源码解析-启动过程
一.前言 上一节已经将raincat demo工程运行起来了,这一节来分析下raincat启动过程的源码 主要包括: 事务协调者启动过程 事务参与者启动过程 二.协调者启动过程 主要就是在启动类中通过 ...
- Tensorflow版Faster RCNN源码解析(TFFRCNN) (2)推断(测试)过程不使用RPN时代码运行流程
本blog为github上CharlesShang/TFFRCNN版源码解析系列代码笔记第二篇 推断(测试)过程不使用RPN时代码运行流程 作者:Jiang Wu 原文见:https://hom ...
- Fabric1.4源码解析: 链码容器启动过程
想写点东西记录一下最近看的一些Fabric源码,本文使用的是fabric1.4的版本,所以对于其他版本的fabric,内容可能会有所不同. 本文想针对Fabric中链码容器的启动过程进行源码的解析.这 ...
- Fabric1.4源码解析:链码实例化过程
之前说完了链码的安装过程,接下来说一下链码的实例化过程好了,再然后是链码的调用过程.其实这几个过程内容已经很相似了,都是涉及到Proposal,不过整体流程还是要说一下的. 同样,切入点仍然是fabr ...
- Flink 源码解析 —— JobManager 处理 SubmitJob 的过程
JobManager 处理 SubmitJob https://t.zsxq.com/3JQJMzZ 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1 ...
- Flink 源码解析 —— TaskManager 处理 SubmitJob 的过程
TaskManager 处理 SubmitJob 的过程 https://t.zsxq.com/eu7mQZj 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink ...
随机推荐
- VS环境下基于C++的单链表实现
------------恢复内容开始------------ #include<iostream> using namespace::std; typedef int ElemType; ...
- 我对C++开发人员有偏见
前言 我确实对C++开发人员有一些偏见,我也知道对一类人有偏见是不正确的行为:但,在我所处的三线城市的环境中,我对C++开发有偏见并非是一件不正确的事,因为C++开发都是变态这件事,根本就是客观事实. ...
- nginx篇最初级用法之访问认证
1打开conf下的配置文件 在server 之下 location 之上加入 auth_basic "Input Password:"; 弹出的提示信息 auth_basic ...
- [BZOJ] DZY Loves Math 系列 I && II
为了让自己看起来有点事干 ,做个套题吧..不然老是东翻翻西翻翻也不知道在干嘛... \(\bf 3309: DZY \ Loves \ Math\) 令 \(h=f*\mu\) 很明显题目要求的就是\ ...
- [考试反思]0907NOIP模拟测试39:角落
题比较简单,但是做的非常烂. T1是个愚蠢的找规律组合数快速幂,数组开小了(看错数据范围) T2题目保证联通没看见,hack掉了正解. T3也挺蠢的,但是打乱了,思路不是很清晰导致丢了50分. 只能说 ...
- 我把数表A了
真kx 之前打一棵沙雕主席树,复杂度和正解一毛一样,结果常数爆炸了! 我一直天真的认为卡卡常这题就能AC,然后卡常卡到死也没A掉,极限数据大概跑了1.1s 今天终于打正解了,离线处理,可以用常数更小的 ...
- Android Studio接谷歌原生登录
目录 前言 AndroidStudio server_client_id @ 前言 准备 近日,公司要求上线海外市场,需要接入海外SDK,首先上架的是GooglePlay,需要先接入GooglePla ...
- 009-2010网络最热的 嵌入式学习|ARM|Linux|wince|ucos|经典资料与实例分析
前段时间做了一个关于ARM9 2440资料的汇总帖,很高兴看到21ic和CSDN等论坛朋友们的支持和鼓励.当年学单片机的时候datasheet和学习资料基本都是在论坛上找到的,也遇到很多好心的高手朋友 ...
- 2019年10月13日 linux习题 wangqingchao
1. GUN的含义是: GNU's Not UNIX . 2. Linux一般有3个主要部分:内核.命令解释层.实用工具. 3.POSIX是可携式操作系统接口的缩写,重点在规范核心与应用程序之间的接口 ...
- PHP 导出微信公众号粉丝的方法
PHP 导出微信公众号粉丝的方法 先 user/get 获取关注者列表 然后user/info 根据openid读取信息 以上方法认证的订阅号支持
