本章用于记录实现前端与设备的通讯功能所面对的问题和当前解决的思路。
PS:首先声明下。由于本人后端能力不强,因此这个设计肯定不是非常好的。可是我尽力能想到的办法了。望各位大佬还能够提供名贵的建议。
由于现在client
和server
代码是分隔编写的,意图是不想让MQTT和express的代码相互嵌套(2年前写的相互套什么app.post()
套一堆client.on
然后client.on
又套数据库的查询…,自己都不知道当时是怎么跑得起来的。而且用之前的想法测验,都失利了(利诱而奇特?!))可是现在面对的问题:router
的 res
需求带着数据回来。怎么将MQTT的message
带着的音讯放入 res
而且确保不会回来犯错。这个问题还需求优化。期望各位大佬能够点拨!
需求
在本系统中,针对用户发送操控设备或读取设备信息的GET/POST恳求后,服务端需求发送MQTT协议给硬件,同时也需求监听硬件回来的信息然后回来给用户。整体流程如图所示(画的有点笼统)
举个例子:当用户点击获取设备温度的按钮时,会产生GET/POST
恳求给咱们运用Express
建立的服务端(对应图中的1)。服务端接纳到这个恳求后,会发送特定主题的对应的MQTT音讯给broker
,由broker
转发根据主题转发给设备(对应图中的2与3)。当设备收到该主题的音讯时(假定主题是:我需求你告诉我现在你的温度),会发布一条音讯给broker
(假定主题是:这是设备现在的温度)。那么此时client
就需求提前订阅这个主题的音讯,以便能够接纳到。当该音讯经过broker
转发给client
后(对应5),咱们的服务端需求获取到这个音讯的内容,然后转发给前端(对应6)。
现在想到的思路
其实 1-6都好解决,可是问题的关键在于client
收到了MQTT音讯后,怎么传递给服务端?
在MQTT中,client
是经过client.on("messgae", (topic, message) => { })
办法监听收到的音讯。也便是说咱们只能在这个回调函数中拿到设备发送过来的音讯。且由于咱们的client.js
与 app.js
是两个独立的文件。因此我现在想并尝试了4种办法:
- 将
client
导出 (也便是整个逻辑仅有一个client一致接纳和发送音讯) - 在
app.js
中编写对应获取数据的函数,将函数放入client.on("message")
监听事情的回调函数中。也便是咱们服务端的client
始终监听(on 办法),当收到用户的恳求时,就发布一个音讯。 - 将
client
与服务端之间再弄一个http通讯。也便是user - server - client - broker - device
- 每次遇到这种恳求的时分,
Server
自动为用户创立一个client,顺次让client
上线、发布(对应图中的2)、订阅、监听(对应图中的5)。并在失利/成功的时分回来这个音讯(对应6),然后运用end()自动将其下线。(这也是现在选用的办法。)即咱们收到了用户需求操控设备的音讯,然后new 一个client
专门针对这个音讯来处理,然后将这个目标收到的音讯回来。让res = 这个音讯
即可。(这样的优点便是咱们的 app.post 恳求的整体逻辑和读取数据库相同。都是在回调函数中return res)
现在存在的问题
现在针对这4种计划或许存在的问题:
- 获取到MQTT回来的音讯或许会抵触(或者说会屡次调用监听函数,需求屡次定义
client.on("message")
办法,然后在不同的回调函数中处理 res 的回来)可是当多个用户同时进行这样的操作的时分,由于关于相同的client,其实本质上便是多订阅了更多的音讯,而无论在哪里的client.on("message")
都会监听到。然后或许导致发送给用户1的音讯过错发给了用户2。此外,后续存数据库的操作也都需求放在client.on办法
中。 - 这个函数按道理只会触发一次(用户发送恳求的时分触发)。但放到 on 里面会屡次履行,也便是每当收到音讯的时分,这个函数都会被触发。而且怎么去确定是哪一个 res 目标,这个如同不太能实现。
- 推迟或许会提高,多了一个Http恳求的时刻。且需求判断下回来给哪个恳求。
- 对broker的负载变高了,多个client一直上下线,发布订阅等。资源或许有点糟蹋
现在关于计划4的代码:
咱们把整个流程(2-6)能够一致封装成一个函数sendMQTT
:传入的参数包括:发布的主题和音讯(对应流程3),订阅的主题(对应流程5),以及当前恳求所要回来的res目标。咱们在这个函数里获取MQTT的音讯数据并回来。
const mqtt = require("mqtt");
function sendMQTT(pubTopic, subTopic, message, res) {
const client = mqtt.connect("mqtt://localhost:9000");
client.publish(pubTopic, message, (err) => {
if (err) {
res.sendStatus(500);
client.end();
}
});
client.subscribe(subTopic, (err) => {
if (err) {
res.sendStatus(502);
client.end();
}
});
client.on("message", (topic, message) => {
if (topic === subTopic) {
res.json(message);
}
client.end();
});
}
module.exports = sendMQTT;
然后是服务端获取前端恳求的路由:这里咱们设置前端传来的是id
// 用于测验客户端与服务端之间的通讯
const express = require("express");
const mqtt = require("mqtt");
const sendMQTT = require("../utils/sendMQTT");
const app = express();
app.get("/get/:id", (req, res) => {
const id = req.params.id;
sendMQTT(`pub${id}`, `sub${id}`, `getDeviceData`, res);
});
app.post("/post", (req, res) => {
console.log(req);
res.send("Hello, World!");
});
// app.use("/devices", deviceRouter);
app.listen(3000, () => {
console.log("Server is running on port 3000");
});
module.exports = app;
broker 还是和之前相同。 咱们先发动broker,然后发动 express 服务端。在 postman 中模仿。流程如下:
-
postman 创立一个 MQTT 衔接broker,并订阅所需求的主题 (
pubTopic
) -
运用 postman 发送对应的get恳求。
-
运用 postman 发布音讯(主题为
subTopic
) -
经过两个 postman 查看收到的音讯。