Node.js开发人员工具。 用于Web套接字的Mqtt协议

Web套接字技术允许在Web应用程序或移动应用程序中将消息从服​​务器发送到客户端,而使用REST-API则无法完成。 要使用Web套接字,他们经常使用socket.io库,或者开发人员使用本机浏览器Web套接字对象。 在本文中,我将尝试证明这两种方法都不能解决所有问题,最好使用专用服务器,例如mqtt服务器(以前称为mqtt代理)来处理Web套接字。

为公平起见,为避免不必要的纠纷,我注意到除mqtt服务器外,还可以使用许多其他服务器,例如rabbitmq。

除非我们遇到经常发生连接中断的现实,否则使用Web套接字开发应用程序似乎非常简单。 必须解决的第一个任务是跟踪连接中断并恢复它们。 由于在断开连接和重新连接期间,客户端继续发送新消息,并且新的消息可以发送到客户端的可能性更大,因此事实使情况变得复杂。

在应用程序级别,有必要监视消息的接收,并实现消息的重复传递。 我特别想提请注意“在应用程序级别”一词(但我希望它在协议级别)。

一旦添加了用于跟踪消息传递的逻辑,所有消息就开始到达,但是很明显,由于有可能接收到该消息,因此存在重复消息,并且由于连接断开而丢失了对这一事实的确认。 并且您需要将程序代码数量加倍以排除重复的消息。

随着程序代码的复杂化,其效率也会降低,为此,他们经常批评socket.io库。 当然,它不如使用本机Web套接字有效,特别是由于存在重新连接逻辑和消息传递确认(我立即注意到在socket.io中未实现重新传递逻辑)。

一种更可靠和有效的方法是将这种逻辑带到协议级别。 并且存在这样的协议-它是mqtt。 mqtt协议的第一个版本由Andy Stanford-Clark(IBM)和Arlene Nipper(Arcom)于1999年开发。MQTT3.1.1规范由OASIS联盟于2014年标准化。

mqtt协议具有一个“服务质量”(qos)参数,该参数可以采用以下值:
0-如果可能,则传递消息;
1-保证邮件已传递,但可能有重复;
2-消息被传送一次,并且被保证一次。

也就是说,mqtt协议解决了保证邮件传递的问题,并将此问题从议程中删除。 但不仅是这个问题。

性能和扩展。

使用Web套接字时,即使没有真正的消息传递,所有连接的客户端也会保持与服务器的开放连接。 该负载与REST-API中的负载本质上是不同的,REST-API的负载由请求流确定。 在测试阶段很难模拟Web套接字上开放连接的负担。 因此,通常会根据发送和接收的消息数对应用程序的充分性能做出错误的假设,而没有考虑与客户端保持大量开放连接的负担。

如果我们将所有使用Web套接字的工作转移到专用的mqtt服务器,则我们的nodejs应用程序仅在使用mqtt服务器的Web套接字(或tcp,因为mqtt支持两种协议)上打开一个连接,我们可以通过将nodejs的多个实例连接到mqtt服务器来扩展我们的应用程序。

如果一台mqtt服务器的资源已用尽,则可以组织一堆mqtt服务器,而不会影响nodejs上的应用程序。

现在让我们继续一个例子。

如先前规范中所称的,mqtt服务器或代理根据发送消息/订阅消息的模型工作。 每个消息都发送到该主题。 收件人订阅消息线程。 发送者和接收者都有两个标识符:clientId(设备标识符)和userName(用户名)。

设备标识符很重要,因为它与订阅相关联,并且消息将发送给它。 与设备标识符相反,用户名在消息传递中没有决定性的作用,并且用于区分对主题的访问。

为了在客户端使用mqtt协议,使用了github.com/eclipse/paho.mqtt.javascript库。 有几种服务器实现,包括免费的。 在此示例中,我们将使用emqx服务器,该服务器通过docker -compose运行(请参阅github.com/apapacy/tut-mqtt )。

为了进行测试,请创建一个文档,在其中设置clientId,userName和消息文本:

<script src="/paho-mqtt.js"></script> <script src="/messages.js"></script> <form name="sender" onsubmit="return false"> <input type="text" name="user"> <input type="text" name="client"> <input type="text" name="message"> <input type="button" onclick="connect()" value="connect"> <input type="button" onclick="send()" value="send"> </form> 


发送消息是在message.js文件中实现的:

 var client; var connectOptions = { timeout: 30, reconnect: true, cleanSession: false, mqttVersion: 4, keepAliveInterval: 10, onSuccess: onConnect, onFailure: onFailure } function connect() { try { client = new Paho.Client('localhost', 8083, '/mqtt', document.forms.sender.client.value); connectOptions.userName = document.forms.sender.user.value; client.connect(connectOptions); } catch (ex) { console.log(ex); } } function onConnect() { console.log('on connect'); client.onMessageArrived = function(message) { console.log("onMessageArrived: " + message.payloadString); } client.subscribe("test", { qos: 2 }); } function onFailure(err) { console.log('on failure', JSON.stringify(err)); } function send() { var message = new Paho.Message(document.forms.sender.message.value); message.destinationName = "test"; message.qos = 2; client.send(message); } 


要进行检查,请在浏览器中打开index.html文件,指定clientId,userName,消息文本并发送几条消息(您可以在控制台中读取它们,因为客户端将消息发送到测试主题,并且已订阅该主题本身)。

现在打开另一个浏览器或另一个浏览器选项卡,并与另一个(重要的)clientId联接。 从第一个浏览器发送更多消息,并确保它们同时到达两个客户端,因为它们具有不同的clientId且都已订阅主题测试。

现在关闭第二个浏览器(或第二个浏览器选项卡),并发送更多消息。 之后,重新打开第二个浏览器,并使用相同的clientId加入。 确保在控制台日志中,您已经收到关闭第二个浏览器(第二个选项卡)期间发送的所有消息。 发生这种情况是因为:

  • 发送消息时,已设置质量等级qos = 2;
  • 通过设置qos = 2,您先前已使用相同的clientId加入了相同的主题。
  • 连接选项设置为cleanSession:false。

可以从资源库下载示例代码

apapacy@gmail.com
2019年9月29日

Source: https://habr.com/ru/post/zh-CN469315/


All Articles