注册

MQTT客户端学习路线总结


本篇仅仅是记录一下MQTT学习的过程和感想,文字偏多



前言


总结一下MQTT协议的学习过程, 大概分为9步。


MQTT学习过程.jpg


在IoT最热门时,有过一些了解,仅限名词解释。这次在为工厂装修设计时,涉及到了一些智能设备,也因此近距离接触到了MQTT协议相关的系统。虽然直接采购的是成品(包含施工方案),但出于好奇和喜欢动手的本能,我想学习一下MQTT通信应用协议


使用


直接体验采购的智能设备,也算是使用,但总感觉少了点什么-感觉没有真正体验到MQTT协议通信。基于此种想法,翻阅MQTT环境搭建的指导文章,开始在自己的电脑上捣鼓安装MQTT客户端软件和MQTT服务器软件,一番折腾后,成功安装了MQTTXmosquitto


在MQTTX客户端上,看到可以成功的接收消息和发送消息,瞬间有种傻瓜式的成就感(我会用MQTT协议啦)。


体验完MQTTX,按照mosquitto官方指导,竟然发现它既可用作服务器同时还可用做客户端,在mac终端中急切的敲着发送主题的命令,在MQTTX上看到收到的消息,就一个感觉:呗爽。


对于一个新手,MQTTX和mosquitto的成就感促使我想继续阅读MQTT相关的文章,也因此从MQTT官网找到了Steve's Internet Guide博客


体验了MQTT软件和阅读了Steve's Internet Guide后,对运行一个客户端源码工程十分渴望,如此优秀的博客,配上程序运行时的日志,简直是学习MQTT的“下饭菜”。决定了就开干,从MQTT官网选型合适的MQTT协议实现库,最终我选定了Elipse paho


实践


Elipse paho共包含了17种版本,涉及多种语言(C语言,python, javascript, C++, golang, ruby, rust), 我实践学习用的是paho.mqtt.java版本。


用IntelliJ IDEA(社区版)创建一个属于自己的命令式应用MQTTHarvey, 将“org.eclipse.paho.mqttv5.client” 源码引入自己的应用。然后创建自己实践用入口代码(包括场景模拟和日志打印)。


最佳体验的方式:上手直接敲代码,调用核心功能API,看效果。如果API不熟,记住功能名称,疯狂的在掘金中搜索相关的介绍文章,比如:MQTT如何发送主题, MQTT如何订阅等等。


在首次成功运行已集成好源码的工程后,针对PUBLISH,SUBCRIBE需要先实践一遍(当然,最初我对实践这两个消息的称呼为:发送消息,接收消息).


消息的发送和接收源码在哪里?带着这个疑问,“胡乱一通”断点,终于找到了地方,接收消息的文件是MqttInputStream.java, 发送消息的文件是MqttOutputStream.java。


协议流到底是什么样子?能不能输出这些字节流,以观其全貌,知其结构。


协议初探


既然是协议,那必然有数据格式规范,编码后的数据按照字节流的方式进行传输,那么将字节流按照字节一个一个打印日志输出出来,就是协议的样子。我应该最先了解哪个协议样子呢?依据基础常识,MQTT客户端开始运行时,与服务端建立连接肯定是第一次联网操作,既然是这个样子,MQTT有没有关于联机的协议呢?经过一顿搜索,CONNECT 是关于客户端连接服务器的协议。那如何将其打印出来呢?因为都是字节流,又不了解协议规范,想要打印出来真的比较难。


正向研究协议在刚开始阶段,硬杠还是比较浪费时间的,甚至会打击继续学习的信心。后来我才用了一个比较讨巧的方式, 写一段只包含连接服务器的代码,就可以解决难以仅仅打印CONNECT协议的问题。


在二进制流打印完成后,在IDE控制台看到的日志,其实都是一个字节一个字节的字符串信息,很难以看懂。这个时候就需要拿出MQTT规范文档找到CONNECT协议的定义,然后手动尝试去解析每个字节,直到可以把字节都能和规范对上号,才可以证明对这个协议稍微搞懂了。


在手动完成CONNECT协议后,我就迫不及待的想看看消息发送协议,后来想了一下,没必要那么急,毕竟发送消息协议是如此的重要,肯定是比较复杂的。调整一下探究方向,一个新手,想要学习协议分析,从简单的协议入手,比如:断开连接,心跳。因此,我第二个分析的协议就是DISCONNECT,带着好奇的激情,最终完成了这个协议分析,那一天晚上睡觉时,感觉都是无比的开心。


解码


其实软件的职责就是能自动解决规范化的一些流程问题,数据格式问题。在协议初探时,还停留在手动分析字节流阶段,毕竟MQTT客户端如果应用在真实场景,自动解码字节流这样的功能,肯定是必备的。


既然在研究MQTT协议,那就需要拿出点诚意,自己写程序来解码字节流,然后将其拆解为规范中可描述的文字。


依然从简单的协议分析,然后再去分析复杂的。通过MQTT规范文档了解到,CONNECT,PING,DISCONNECT都是相对比较简单的,也容易写测试代码来完成解码实验场景。


在真正通过代码来解码CONNECT协议时,才发现固定头,可变头真的在用代码一步一步解码时,非常容易出错,因为是新手,再加上急于求成(毕竟已经会手动分析了),程序要么执行到一半就发生异常,要么在测试代码中稍微调整一下参数,程序就会崩溃。冷静下来后,发现还是要按照规范文档,一个属性一个属性往下研究,不能急躁。并且在这个过程中,发现自己对待MQTT规范不够重视,连数据类型都直接给忽略了,比如规范中的 “1.5 Data representation” 内容。因为没有重视它,导致在按照规范实现解码时,时常囫囵吞枣,自己认为是代表的是什么意思,就赶紧敲代码实现。


在刚开始对协议解码时,因为懒,所以只针对数据包的头部做了解码,完成了控制包类型1到14(控制包类型:“2.1.2 MQTT Control Packet type”)。完成之后,回看代码发现重复代码很多,分析完重复部分后,才发现像Reason Code,UTF-8 Encoded, Variable Byte Integer这些都是具有全局性的,即:可以把他们的解码分别做成公共部分。为了证实自己的这一点理解,赶紧翻看客户端源码是不是这样的,果不其然,理解正确。


工程分析


因为已经完成了一部分的解码工作,并且对MQTT规范阅读也已经上道,所以就想歇一歇,安静的学习一下客户端源码,它到底是如何实现发送接收消息的,它的代码是如何实现了每一条MQTT规范的。带着这些疑问,先确定程序执行每个控制包的方法调用流程,然后确定运行时的线程数,最终再分析代码之间的依赖关系。


代码跟踪是枯燥的,看别人将协议实现的如此漂亮,深深的受了打击。


每天偶尔看看源码,想象是自己在维护它,熟练的记住每个API,让自己的信心慢慢恢复。


术语理解


有了阅读MQTT规范的技能,自己解码的能力和分析源码之后,有种放空茫然的感觉。MQTT到底是什么,我如何描述它,它在布道时,是如何宣传的,等等?


可能要解决上述疑惑,需要找MQTT官方资料认真学习一下,然后再看看中文是如何教授的。


因此,我从MQTT官网找到HIVEMQ发布的MQTT基础文章,整个系列有十部分内容,然后再逐字逐句翻译,通过翻译,让自己产生疑问,然后再带着疑问去阅读MQTT规范,如果还是无法理解,再通过代码做实验。总之,硬着头皮也要将MQTT规范中的术语或者别人对MQTT的描述理解清楚。


场景模拟


在完成HIVEMQ基础文章的翻译之后,算得上对MQTT有点感觉了,也因此想要将自己的实践测试代码,能不能按照使用场景,分别实现一遍。



场景



  • 连接 -> 断开 -> 重连
  • 连接 -> 订阅 -> 解除订阅
  • 连接 -> 心跳
  • 连接 -> 恢复会话
  • 等等


为什么做这样的事情,我是基于2个原因:1. 我对代码库API及参数使用不熟 2. 提前模拟这些场景,对实战中发生的故障分析,肯定有指导作用


MQTT协议实现


通过一系列的学习和实践,如果真的已经搞懂了MQTT,那么我应该自己可以实现一个简化版的MQTT客户端


比如:实现最简化的功能,连接broker服务器


graph TD
建立Socket --> 发送CONNECT协议 --> 解析CONNACK

当然,自研一个MQTT客户端,从个人来讲,确实对技术能力提升有很大帮助,从公司来讲,那就是Money(比如:杭州映云科技有限公司)。


扩展


短时间内是否真的可以搞懂MQTT?难。协议规范纯理论学习是可以慢慢搞的十分清楚的,但MQTT最终是为了解决生活中实际的通信问题的,那就意味网络原因,数据安全也好,都可能让一个开发人员耗费大量的时间去排查定位问题。基于此种考虑,想要提高技能,增加增经验,可能需要建立问题库,及查看其他人或者公司在使用MQTT过程中的问题列表,并且尝试思考是否能给出解决方案。


作者:harvey_fly
来源:juejin.cn/post/7278953365224734774

0 个评论

要回复文章请先登录注册