赵 靖,王如武,周 皓
大连理工大学 软件学院,辽宁 大连 116024
随着“工业互联网”“工业4.0““中国制造2025”等战略的提出,制造业数字化转型已成为共识。打破不同工业系统间的信息壁垒,改善传统工业企业的“信息孤岛”现象,整合海量数据,挖掘更大的信息价值,优化流程,提升效率,赋能工业生产、管理、运营、销售、售后等整条生态链,是数字化转型的目标和作用。工业制造往往涉及的产品类型众多,工业系统之间千差万别,而且工业现场环境复杂,网络不稳定。尽管在降低时延方面,可以采用目前比较流行的边缘计算解决方案,在工厂内部或附近放置大量边缘云设备以减少延迟,但大量的专有传感器及边缘设备的信息交互也会增加通信成本,加大工业用电量,增加成本和运营费用。因此通讯协议的选取至关重要,而在物联网领域广泛流行的消息队列遥测传输(Message Queuing Telemetry Transport,MQTT)[1]协议就因为其消息体积小,通讯成本低,能量消耗少,适合在结构各异的系统间进行消息传递而备受关注。其独特的发布/订阅体系结构,使不同客户端之间的通信脱钩,从而允许不同的供应商在不同的系统中独立实现MQTT,摆脱原始系统本身的专有协议进行通信,从而在不同的专有系统中建立互操作性,非常适合作为工业生产中多种异构系统与部件间消息传递的标准,在智能制造中的应用越来越受欢迎。国内外的工业互联网平台,如华为FusionPlant平台、西门子mindsphere平台、海尔COSMOPlat平台等的解决方案中都采用了MQTT作为通讯协议,因此在传统工业企业中通过部署MQTT进行大量设备间的消息传递已经逐渐成为一种普遍的选择。
而在MQTT部署方案的制定中以及实际部署前,若先通过仿真模拟技术获取关键数据以供参考,并且快速得到直观的部署效果从而及时修正,则可以大大减小方案落地后的偏差,降低试错成本。这得益于仿真的简便灵活,可以快速获取所需数据(如网络设备吞吐量、网络带宽、时延等),而且成本极低,这对于工业企业来说是至关重要的。如果初期就通过实际部署进行评估测试会造成大量人力物力财力的浪费。
模拟部署MQTT是观察及研究其性能和表现的重要手段。目前在该方面主要分为小规模本地部署和仿真软件模拟两种方式。文献[3]中通过在两台机器上分别搭建单独的MQTT客户端和服务端进行了小规模的模拟实验,对比了MQTT和CoAP两种协议的端到端时延和带宽消耗情况,结果表明协议的性能取决于网络条件,在较低丢包率时MQTT会具有更低的延迟,但是该实验只设置了一个客户端,对于大量客户端的情况无法进行准确评估。文献[4]利用OPNET仿真器进行无线楼宇网络中MQTT的仿真以获取最佳部署节点数,然后在实际系统中部署进行下一步测试。虽然其能够较好地模拟MQTT,但是OPNET并不是免费的软件,主要面向商业用户,价格昂贵。文献[5]中使用OMNet++仿真器的INET框架评估了MQTT协议在工业环境下的性能,观察了在不同传感器数量下该协议的吞吐量和时延变化。但是文献[6-7]中的对比结果都表明,相比于OMNet++等其他模拟器,NS-3模拟器都有着更好的整体性能,比如需要更少的计算时间及更低的内存,同时NS-3是开源免费的,它拥有更加活跃的社区对其进行维护,每三到四个月定期发布具有新功能的版本[8]。另外,利用文献[9]提供的ns3-ai扩展模块还可以开展计算机网络和人工智能算法相结合的交叉研究,这些优势都促使更多的人愿意使用NS-3,并积极地完善和扩展它以便进行更广泛的研究。
本文针对NS-3中无法对MQTT协议提供简单易用的模块和接口支持问题,提出并实现了一个名为ns3-mqtt的框架来对NS-3进行扩展,详细描述了该框架的设计策略和实现细节,包括它的三个扩展组件(MQTT核心模块、MQTT应用程序、MQTT帮助程序),同时提供了一个工业场景仿真案例来说明集成后的NS-3如何模拟部署MQTT以获取必要数据,进而为实际部署MQTT提供有力的帮助。另外,还对数据包大小和客户端数量对MQTT协议性能的影响进行了研究,观察到了和OMNet++仿真器中相似的现象,证明了ns3-mqtt框架的可行性。
NS-3网络模拟器是一个开源的离散事件驱动的网络模拟器,主要用于研究和教育目的[10]。它可以在一台计算机上用C++和Python编写脚本来模拟现实世界的网络[11]。在一个网络系统的仿真中,每一个实体都会由一个称为节点的容器表示。网络设备、协议、应用程序都会依次安装到这个容器中。NS-3提供了多种网络协议(802.11、TCP、UDP等)及通讯技术(WIFI、LTE、5G等),作为一个开源项目,它完全由C++语言编写,主要运行于linux平台,任何组织和个人可对源码进行下载及修改,由NS-3社区维护。另外,它还可以与外部系统和实际应用进行交互,提供了与其他平台进行联合仿真并获取更真实数据的可能性。
MQTT协议是一种使用发布/订阅机制的消息传递协议,最初由Andy等设计[12],适用于资源受限设备以及低带宽、高延迟或不可靠的网络[13]。该协议位于OSI网络模型中的应用层,依赖于传输层的TCP协议以及网络层的IP协议,支持TLS/SSL加密。它独特的消息传递机制以及低带宽占用、可靠的特性,使其非常适合工业领域云边通信[14]的应用场景,在有限带宽的情况下也可为大量传感器等设备的连接提供实时可靠的消息服务。该协议基于一个称为broker的中心服务器来实现对所有消息的代理,形成发布订阅模式,其客户端只能够和中心服务器直接交互,而且消息的传递是基于主题的,客户端可向某一主题发布订阅请求或发布请求,中心服务器一旦接收到消息请求,就会根据消息主题在该主题的订阅列表下增加该客户端以满足订阅请求,或者分发消息内容给已订阅该主题的其他客户端以满足发布请求,可实现订阅方和发布方之间的解耦。
1.2.1 MQTT协议的消息格式
MQTT消息由2个字节的固定报头,2个字节的可变报头以及有效载荷三部分组成,固定报头的格式规范如图1所示。其中Retain字段用于设置该条消息是否需要服务端保留;Qos Level字段用于设置该条消息所需的服务等级;Dup Flag字段用于设置该条消息是否为重发消息;Message Type字段的值代表消息的类型(共有14种);Remaining Length字段的值则代表消息的可变报头和有效载荷大小。
图1 MQTT固定报头格式Fig.1 MQTT fixed header format
可变报头是否存在由消息类型(Message Type)决定,当消息类型为PUBLISH(Qos>0)、PUBACK、PUBREC、PUBREL、PUBCOMP、SUBSCRIBE、SUBACK、UNSUBSCRIBE、UNSUBACK类型时可变报头存储的是报文标识(MessageID),长度为2个字节,其他类型时可不添加可变报头。而有效载荷则承载了MQTT消息携带的实际数据,包括主题和内容。
1.2.2 MQTT协议的主题
MQTT协议的主题Topic起到过滤消息的作用,同时也是消息的一种标识,从而对消息进行分组。客户端只会收到其感兴趣的分组的内容。它是一个分层结构,在表示上类似restful风格。一个主题名可以由多个主题层级组成,每一层通过“/”斜杠分隔开,例如:“/layer1/layer2/layer3“。如果客户端对多个相似分组感兴趣,则可以在主题的某个层级使用通配符,包括“+”和“#”两种。“+”通配符属于单层通配符,只匹配某一个层级的全部内容,例如“/layer1/+”可匹配“/layer1/layer2a”和“/layer1/layer2b”。而“#”属于多层通配符,可匹配包括当前层级在内的后面的全部层级的子主题。需要注意的是“#”通配符也可代表0个层次,比如“/layer1/#”也能匹配单一的“/layer1”,此时“#”就代表0个层级。同时它在一个主题只能出现一次,且需在主题层级的末尾处,不可以出现在主题的中间和首部,也不可以在多个层级中同时出现。
1.2.3 MQTT协议的消息服务质量
MQTT协议通过控制Qos等级来决定服务质量[15],其Qos等级共有三个,分别是Qos0、Qos1、Qos2。不同的等级代表了不同的可靠性要求,具体描述如下。
(1)消息服务等级为Qos0时,代表着消息到达至多一次,由于消息发布是完全依赖于底层的TCP/IP协议的,因此会有消息丢失或重复的情况发生。这一等级的服务适用于偶尔丢失一次记录无影响的场景下,比如监测环境温度信息,它会在不久以后继续接收下一次消息。
(2)消息服务等级为Qos1时,代表着消息至少到达一次,但是可能会有消息重复现象发生。
(3)消息服务等级为Qos2时,代表着消息一定会到达且只到达一次。这一等级适用于对消息重复或者丢失比较敏感的场景。
ns3-mqtt框架由MQTT核心模块、MQTT应用程序、MQTT帮助程序三个部分组成。MQTT核心模块主要实现了协议头、客户端和代理服务端。代理服务端可对消息进行存储和转发、客户端可订阅和发布消息,二者均支持Qos0、Qos1、Qos2三种服务等级。考虑到未来对保留消息功能的潜在需求,例如测试新订阅客户端获取历史消息的性能等,还实现了对Retain类型消息的支持。而MQTT应用程序部分则是在MQTT核心模块基础上开发,分为mqtt-client和mqtt-broker两个应用程序,能够定时开启和关闭客户端以及服务端,并进行端到端的数据统计。同时能够通过设置客户端发包大小、间隔、Qos等进行流量生成。另外还编写了ns3::mqttbroker-helper和ns3::mqtt-client-helper类作为帮助程序,该部分的目的是提供便捷的高度封装的接口,并可以批量安装MQTT应用程序以及灵活设置流量生成参数,为安装和使用MQTT应用程序提供方便。图2展示了ns3-mqtt框架的结构以及和NS-3自身模块间的关系。
图2 ns3-mqtt框架结构Fig.2 ns3-mqtt frame structure
该模块参考MQTT-V3.1.1[1]协议规范对MQTT协议主要功能进行了实现。模块结构遵循NS-3项目风格。图3显示了模块中所有类之间的关系。其中ns3::Mqtt-Header类是基于ns3::Header类的扩展,ns3::util类则是用于对MQTT头部数据的存取操作进行简化。同时,服务端和客户端由ns3::broker类和ns3::client类实现,二者采用packet对象作为信息载体,利用TCP/IP协议提供的可编程接口socket[16]实现通讯,其中服务端类还要依赖ns3::mqtt_db类进行数据管理。以下各节将对实现细节进行详细解释。
图3 MQTT核心模块类关系图Fig.3 MQTT core module class diagram
2.1.1 MQTT消息头
MQTT协议消息头由名为MqttHeader的类表示。根据NS-3官方文档所述,该类必须从ns3::Header类派生,并实现ns3::Header类的纯虚方法。派生出的ns3::MqttHeader类需实现MQTT协议头部的序列化和反序列化,而序列化或反序列化中每次操作的字节数则需根据协议本身进行设置。为方便数据统计和重发数据包的辨别,将MQTT的可变头部统一设置为2个字节。因此MQTT协议报文头固定为4个字节。相对应的ns3::MqttHeader类里序列化和反序列化函数中每次操作的大小也为4个字节。需要注意的是,序列化和反序列化过程会调用Buffer函数对缓冲区进行写入和读出,二者的调用顺序不能颠倒,否则会造成缓冲区数据异常。另外,在ns3::packet中添加MQTT消息头需调用其中的AddHeader函数。由于ns3::MqttHeader中数值以二进制形式存储,因此需通过位运算将协议头部各字段值写入。而接收方获取数据包后则需利用ns3::packet中提供的PeekHeader函数,将数据先取出到ns3::MqttHeader对象中,然后调用该对象中GetData函数才能获取到数据。此数据为32位(4个字节)的二进制数,最后还需通过特定的位运算才能获取协议报文各字段的值,比如Message Type,Qos等。表1列出了协议头部二进制数据的写入和读出涉及的位运算公式。其中d表示当前头部的值。为了方便在程序中使用,已在辅助类中将其封装成函数,提供了对应接口。
表1 字段计算公式表Table 1 Field calculation formula table
2.1.2 MQTT协议的客户端
MQTT协议客户端由ns3::client类实现,主要完成对消息的发送,三种Qos服务等级的交互响应以及客户端参数的设置。消息的发送采用socket实现,Qos等级的交互响应采用回调函数和仿真调度器实现,具体的将在3.3.4小节进行介绍。ns3::client类提供的可设置参数有订阅的主题、发送消息的类型和内容、Qos服务等级以及是否为保留消息,该类中默认接收消息的监听端口号设置为1883。由于该类继承自ns3::Application类,其可作为一种应用程序在Node上运行。但为了简化仿真的复杂性,还提供了进一步封装的客户端应用程序mqtt-client,能够较为简便地对单一客户端进行参数设置,例如数据流量参数等。而mqtt-client-helper帮助程序则是提供对客户端批量安装以及参数批量设定的功能,以适应更大规模的仿真,免去了仿真中需逐一安装设定的麻烦。关于客户端应用程序和帮助程序会在3.2、3.3节进行更详细的介绍。
2.1.3 MQTT协议的服务端
MQTT服务端也称为broker,它由ns3::mqtt_db,ns3::broker类实现。ns3::mqtt_db类负责存储消息以及维护客户端和消息之间的对应关系,ns3::broker类则负责数据包的接收校验以及消息的转发。并且ns3::mqtt_db和ns3::broker是一一对应的,前者作为后者类中的一个私有成员变量。
ns3::mqtt_db中存储消息采用的数据结构是订阅树,它类似于多叉树。树的节点由Subtree_node对象表示,该对象由两个分别存储子节点指针和客户端指针的列表,以及代表该节点名称的字符串组成。其中客户端可存储在一个或多个树节点下,代表该客户端订阅了由根节点的儿子节点到该节点途径所有节点的名称构成的主题,一个典型的订阅树如图4所示,其中client2就订阅了“/layer1/layer2a”和“/sys”两个主题。ns3::mqtt_db在接收到经过ns3::broker校验后的数据包时,首先会调用getPacketHeadType函数得到数据包包头中的Message Type的值判断消息类型,若是PUBLISH型消息,则用字符分割函数将负载中主题和内容分开,利用find_topic_exits函数对订阅树进行深度优先遍历,判断该主题在订阅树中是否存在,若存在,将会立即调用pub_add_sub_client函数以及push_msg_to_client函数将消息存储在订阅了该主题的客户端中,这里的客户端是一个临时的Client对象,仅用作存储消息内容和维护实际客户端待接收的消息队列,此时消息还未开始向实际客户端发送。若是SUBSCRIBE型消息,则先调用find_child函数在订阅树中查找匹配的节点,此函数采用递归查找并返回可匹配到的最后一个节点。若能完全匹配则调用sub_add_sub_client函数将此订阅客户端存储到完全匹配的最后一个节点的客户端队列中,准备接收消息。否则会先调用add_subtree_topic函数在订阅树中增加相应节点以达到完全匹配。
图4 订阅树示例Fig.4 Example of subscription tree
ns3::broker类对接收到的数据包的包头和负载进行校验时,先通过位运算获取其头部各字段的值,若未出现非法值则校验后该消息交由mqtt_db处理,否则丢弃该数据包。当消息经mqtt_db处理后,ns3::broker会对mqtt_db中订阅树进行搜索。若发现某个节点中Client的实际客户端待接收消息队列大小不为0,就根据Client的地址(和实际客户端地址一一对应)取出消息队列中的每一条消息。取出后随即创建新的socket,根据Client的地址以及监听的端口号(1883)与对应客户端进行连接并转发对应消息。该转发功能同样支持Qos的三个服务等级,但服务等级是由消息发布时的Qos等级和客户端订阅时的Qos等级共同确定,取二者的较小值。
2.1.4 MQTT协议的服务等级
MQTT协议的三种Qos服务等级功能的实现主要依靠回调函数和Simulator调度器,NS-3中的回调函数和C++中回调函数类似,都需要订阅函数指针、赋值以及调用三个步骤。不过NS-3中对回调函数的步骤进行了简化,其使用步骤被封装在了对应的类和函数中。函数指针在Callback类模板中被定义,该模板可支持最多9个形参并可以定义函数签名的返回值。而Callback对象的创建和赋值由函数MakeCallback完成。通过回调函数接收到消息时可以通过socket回复消息,但只能回复一次。接收者如果想要再次发送消息,只能构造新的socket来发送,这种情况多出现在Qos2等级中,因为需要完成两次交互。Simulator调度器主要应用在控制何时去发送回复消息,该调度器是NS-3离散事件仿真引擎的入口,控制着整个仿真事件的调度,保证仿真不混乱。
在实际实现中只需设置MQTT数据包包头的Qos Level字段即可将Qos值跟随消息一起发送。其中Qos0等级由于只发送一次,故不存在交互行为。当服务等级为Qos1时,客户端发送消息后会调用等待接收ack函数sendRecvAck来接收服务端发回的ack报文。同时此函数中设有等待时间,若在规定时间内没有收到ack则重传消息。当服务等级为Qos2时,客户端发送消息后会调用sendRecvREC函数,等待接收PUBREC类型消息,若在规定时间无回应则重发该消息。若接收到PUBREC类型消息则开始进行第二次交互,也就是调用sendREL函数发送PUBREL消息,之后调用sendRecvCOMP函数等待服务端PUBCOMP类型的回应消息,若一段时间内收不到回应同样会重传消息,若接收到则代表着两次交互已完成。在交互的过程中服务端会一直启用socket监听,收到消息后立即调用回调函数对消息进行解析和逻辑处理,由此来完成和客户端的交互。而对于服务端转发消息给客户端的情况,同样涉及Qos服务等级。这里Qos等级的确定方法和客户端发送消息给服务端时相同,并且交互过程也相同,只是此时客户端和服务端角色互换了。
2.1.5 MQTT协议的Retain功能
MQTT协议的Retain功能旨在让新订阅者及时得到发布方最新的消息或状态信息。只要客户端一订阅带保留消息的主题,就会立即获得该主题下最新的保留消息。如果订阅的主题带有通配符,那么就会收到相匹配的每个主题下的保留消息。具体的设计思路为,对于服务端来说每个主题仅会保留唯一一个Retain消息,存储在该主题尾部对应的订阅树节点的retainmsg成员变量中,新的Retain消息会不断覆盖之前的。如果想删除某主题下的Retain消息,可以发布相同主题的,Retain字段为1并且内容为“-”的消息来删除。实际实现中,服务端收到消息后会先将其存储在订阅树中匹配的节点里,在节点处理待发数据时,遇到保留消息会存储到retainmsg中。节点数据发送完毕就会清空待发数据队列。待新的订阅者到来时,如果其订阅的主题对应的节点中retainmsg不为空,则将其中的保留消息发送给新的订阅者(客户端),使新订阅者立即获得该主题下最新的消息。
MQTT应用程序主要包括客户端应用程序以及服务端应用程序,分别由类ns3::MqttClient和类ns3::MqttBroker实现。客户端应用程序主要负责MQTT流量生成,可以设置的参数包括数据包大小、总数、发包间隔、发送速率、数据包中携带的主题和内容、数据包中携带的消息是否为保留消息以及发送所需的服务质量Qos(默认为Qos0)。而服务端应用程序可设置时间进行定时打开和关闭,在关闭后将调用Count函数进行数据统计并将结果输出到控制台或指定文件中。为方便进行延迟等数据的统计,在MQTT数据包中还添加了时间戳TimestampTag,但是NS-3中计算packet大小时会自动忽略TimestampTag对象的大小。表2、表3分别列出了服务端和客户端应用程序的主要函数及说明。
表2 服务端应用程序函数列表Table 2 Server application function list
表3 客户端应用程序函数列表Table 3 Client application function list
MQTT帮助程序主要目的在于提供更加简便的高级接口,表4列出了这些高级接口。它们为节点批量安装MQTT应用程序和设置属性提供方便。该部分包括MQTT客户端以及服务端两个帮助程序,分别由类ns3::MqttClientHelper和类ns3::MqttBrokerHelper实现。二者均提供了Install方法用于在节点中简便地安装应用程序,同时提供两种不同的安装方式(由传入的参数决定),分为单独安装和批量安装。单独安装可对某一节点的应用程序单独进行属性设置,而批量安装则会针对列表内所有节点统一安装属性相同的应用程序。
表4 MQTT帮助程序接口列表Table 4 MQTT helper interface list
本文选择ns-3.30版本的NS-3为例说明扩展的软件包如何安装到NS-3仿真平台中。NS-3的安装环境为ubuntu18.04 Linux操作系统,内核版本为5.4.0-73-generic。具体的集成方法如下。
(1)下载ns-3.30版本的NS-3软件。
(2)参照NS-3官方文档安装成功后,切换到ns-allinone-3.30/ns-3.30/src目录,在该目录下打开终端,输入sudo./create-module.py ns3-mqtt命令,利用create-module.py脚本创建一个新的ns3-mqtt模块。
(3)模块创建完成后,进入ns-allinone-3.30/ns-3.30/src/ns3-mqtt目录,也就是新创建模块的文件夹中,用所扩展的ns3-mqtt软件包里的文件替换这个文件夹下的所有文件。
(4)然后返回至ns-allinone-3.30/ns-3.30目录下执行sudo./waf configure命令,对NS-3进行重新配置,编译系统会检查NS-3依赖的软件包是否已经成功安装。
(5)最后在相同目录下执行sudo./waf命令进行编译,编译无错误则ns3-mqtt安装成功。
3.2.1 仿真设置
本文设计的仿真实验旨在给出一个示例作为参考,同时检验框架实现的效果。为了和文献[5]中使用OMNet++进行的仿真实验结果进行对比,本文中的仿真参数配置尽量与其接近。实验模拟的是工业场景下空气状况的监测,涉及温湿度传感器和红外气体传感器,分别用于空气温湿度的监测和空气污染物的检测。空间设定在100 m×100 m的范围内,节点的分布分为两个区域,左侧区域(60 m×100 m)代表了工业生产区域,右侧区域(20 m×100 m)代表了工业数据监测区域,MQTT服务端应用程序安装在坐标为(50,50)的中心位置节点(位于工业生产区域内),订阅方MQTT客户端应用程序安装在右侧工业数据监测区域的边缘位置,用于收集传感器发送的数据。其他发布方MQTT客户端应用程序都安装在左侧工业生产区域的节点中,工业生产区域的节点分布采用ns3::UniformDiscPositionAllocator模型以恒定的密度随机均匀地分布在以(30,50)坐标为圆心半径30 m的圆型区域内,移动模型选择的是随机圆盘静态分布模型,所有节点在圆盘范围内随机静态分布,通过使用NetAnim动画工具展示了该布局,图5给出了该布局的一个网络拓扑示例。另外仿真实验中发布方客户端设置为每隔5 s发送固定大小数据包以生成流量,同时所有模拟运行10次取平均值以增加可信度,每次模拟运行120 s。仿真实验的节点数量选为{1,30,60,90,120,150,180,210,240,270,300,330,360,390,420},和文献[5]中不同的原因在于为了观察当更大数量节点存在时,吞吐量及延迟的表现,这也是以前的研究所忽略的地方(注意这里的节点数量是指作为发布方的节点的数量)。同时考虑到仿真设定的空气监测场景不是延迟敏感型的,并且文献[5]中的实验也采用了无线通信,因此通信模型选择了wifi无线模型,Qos设置为默认值0。仿真中的数据包大小设置为12字节以更好地符合传感器等小型设备发包较小的特点,同时也增设了大小为120字节的数据包用于观察数据包大小对吞吐量和延迟的影响。
图5 网络拓扑示例Fig.5 Example of network topology
3.2.2 性能指标
MQTT的性能评估指标采用的是平均时延和最大吞吐量。该性能指标对于设备的采购和网络部署具有关键的指导作用。性能指标中没有选择平均吞吐量的原因在于平均吞吐量只能反映设备一般情况下的性能,而工业环境中有着更严格的要求,即使在流量最高峰时也需满足服务需求,因此在工业环境下最大吞吐量更有意义,能更精确地指导服务器等设备的参数选择。以下为两个指标的具体描述。
(1)平均时延:所有分组(数据包)从源节点到达目标节点所花费的时间的平均值,单位为s。
(2)最大吞吐量:单位时间内节点接收的最大数据量(分组大小之和),单位为bit/s。
3.2.3 仿真结果及分析
仿真实验总共分为两组。第一组仿真实验,数据包大小设置为12 Byte。观察了客户端数量对吞吐量和端到端延迟的影响,以获取该仿真环境中每个服务端应服务的最佳客户端数量。图6显示了随着客户端数量的变化吞吐量大小的改变。图7则显示了随着客户端数量的变化延迟的改变。仿真结果显示吞吐量在客户端数量增长的初期不断大幅度上升,而到达一定数量后上升幅度骤降,甚至开始下降,这与文献[5]中观察到的现象极其相似。
图6 吞吐量峰值随客户端数量的变化Fig.6 Peak throughput variation with number of clients
图7 延迟随客户端数量的变化Fig.7 Latency variation with number of clients
具体来看,客户端数量是达到210后吞吐量的增幅开始骤降,甚至出现了负增长。这说明初期在一定范围内随着节点数量的增多,数据流量增加,带宽利用率提高,同时网络中的冲突和丢包现象较少,导致吞吐量提高。但是随着节点越来越多,网络信道过度使用、数据包丢失、网络冲突等现象就会发生。这在工业环境中往往是很不利的。为此在实际部署前应充分考虑每个服务端能服务的客户端数量,在保证通讯质量和数据完整性前提下最大限度地提升其吞吐量,提高资源利用率,节约成本。同时,实验表明延迟虽然整体会随着节点数量的增加而不断增加,但是在客户端数量达到240后延迟的增幅会加大。这是由于网络数据包逐渐增多,在一定范围内只是轻微拥堵造成延迟小幅度增加,但随着客户端的数量越来越多,数据包也会越来越多,而网络信道的容量是有限的,会发生严重拥堵冲突,并且接收方客户端的接收数据的速率也是有上限的,会产生排队时延等问题,因此在当前仿真场景下,每个服务端服务的发布方客户端的最佳数量应为210左右。
第二组仿真实验,则是观察了不同数据包大小对订阅方客户端吞吐量和延迟变化的影响,以获取相关启发。针对吞吐量,实验观察的是其达到增量拐点时对应的客户端数量,增量拐点指在该点以后吞吐量增量骤降的点。实验设置两种数据包大小,分别是12 Byte和120 Byte。增加的120 Byte数据包用作对比,除单次发包大小不同外,其他仿真参数均不变。为方便对比效果的展示,将120 Byte时的吞吐量增量数据除以10。这样处理是考虑到数据包大小增加十倍后,吞吐量也增加十倍左右。图8显示了在不同数据包大小下吞吐量增量随着客户端数量增加的变化情况。
从图8中可以观察到,数据包大小为12 Byte时到达增量拐点对应的客户端数量为210,而120 Byte时为150,随着数据包的增大增量拐点提前了。这是因为数据包的增大会更容易导致网络拥塞。因此想要同时高质量地服务更多的客户端则需要更小的消息负载来降低数据包大小。
图8 吞吐量增量随客户端数量的变化对比Fig.8 Comparison of throughput increment with number of clients
另外该组实验也观察到了数据包增大对端到端的延迟的影响,图9显示了不同数据包大小下延迟随客户端数量增加的变化情况。
图9 延迟随客户端数量的变化对比Fig.9 Comparison of latency with number of clients
观察图9可以发现,在210个客户端内,不同的负载下二者的延迟相差很少,但是随着客户端数量的继续增加,更大的数据包就会导致更高的端到端的延迟。这是因为往往较大数据包被接收所需的时间更长,而在客户端较少时由于接收方的收包能力是冗余的,单位时间内接收的数据量一直未到达极限值,因此对延迟的影响极小。从以上吞吐量增量和延迟的对比来看,较小的数据包都会带来更优的性能。为此在实际部署MQTT时应尽量降低单次发送数据包的大小以获取更好的性能及更低的部署成本。
本文提出了ns3-mqtt扩展框架,并以软件包的形式集成到NS-3中,使得在NS-3中模拟部署MQTT成为可能,极大地方便了相关研究的展开。本文的框架设计思路以及协议的实现方法对NS-3中实现或改进其他应用层协议也具有借鉴意义,可供读者参考。同时,为验证框架实现的有效性,参照文献[5]中使用OMNet++仿真器的仿真参数,在集成后的NS-3中进行了相似的仿真实验。仿真结果表明该框架能够在NS-3中按照预期运行,且观察到了和文献[5]中相似的实验现象。下一步的工作主要集中在开发安全模块,继续丰富和扩展本文提出的框架,通过仿真分析一些新颖的MQTT安全增强方案,并探究进一步改进的方法。