mqtt简介
MQTT(Message Queuing Telemetry Transport)是一种轻量级的、开放式的消息传输协议,专门设计用于受限设备之间的通信,特别适用于物联网(IoT)环境。以下是 MQTT 的一些关键特点和概念:
轻量级: MQTT 是一种轻量级协议,它的设计简单而高效,适用于资源受限的设备,如传感器、嵌入式设备等。
发布/订阅模式: MQTT 使用发布/订阅模式进行通信。发布者(Publisher)将消息发布到特定的主题(Topic),而订阅者(Subscriber)则通过订阅特定的主题来接收消息。这种模式使得设备可以解耦通信,不需要直接知道彼此的存在,从而提高了系统的灵活性和可扩展性。
QoS级别: MQTT 提供了三个不同的服务质量(Quality of Service,QoS)级别,用于控制消息传输的可靠性和顺序性。这些级别分别是:
QoS 0:最多一次交付,消息不会被确认,可能会丢失或重复。
QoS 1:至少一次交付,确保消息至少被传输一次,但可能会重复。
QoS 2:恰好一次交付,确保消息被准确地传输一次,不会重复。
保留消息: MQTT 支持保留消息功能,允许发布者发布带有保留标志的消息,这些消息将会被保留在代理服务器(Broker)中,并在订阅者订阅相应主题时立即发送给它们。这对于设备在启动时获取最新状态或配置信息非常有用。
遗嘱消息: 发布者可以设置遗嘱消息,当发布者与 MQTT 代理断开连接时,代理将会发布这条遗嘱消息到指定的主题,通知其他订阅者发布者的离线状态。
可靠性和安全性: MQTT 提供了一些机制来确保通信的可靠性和安全性,如用户名/密码认证、TLS/SSL 加密通信等。
多种实现和广泛支持: MQTT 有多种开源实现,如 Eclipse Paho、Mosquitto 等,并且得到了广泛的支持和应用,包括在工业自动化、智能家居、物联网、传感器网络等领域。
总之,MQTT 是一种简单、轻量级、可靠的消息传输协议,适用于连接各种设备和系统的通信,并已成为物联网领域中的重要技术之一。
mqtt通配符
通配符+
+符号表示匹配一个级别内的任意字符,不能匹配多级别的主题。
通配符#
#符号表示匹配任意级别内的任意字符,可以匹配多级别的主题。
websocket实现mqtt效果
https://blog.csdn.net/u012643122/article/details/127539130
前端
package.json
stomp
"dependencies": {
"stompjs": "^2.3.3",
},
mqtt
"dependencies": {
"mqtt": "^3.0.0",
},
前端vue2使用stomp的方式:
// mq websocket协议
import Cookies from "js-cookie";
import Stomp from 'stompjs';
import {MQTT_URL,MQTT_topic,MQTT_USERNAME,MQTT_PASSWORD} from '@/utils/mqConfig'
export default {
components: { SidebarItem, Logo },
data() {
return {
msgList: [],
// 连接
ws: null,
count: 0,
timer: null,
inCon: false,
timeout: null,
client: Stomp.client(MQTT_URL),
time: new Date().getTime()
}
},
methods: {
connect:function(){
const headers = {
login: MQTT_USERNAME,
password: MQTT_PASSWORD
};
this.inCon = true
this.client.connect(MQTT_USERNAME, MQTT_PASSWORD,this.onConnected,this.onFailed,'/');
this.client.debug=false;
},
onConnected:function(){
let user = this.$store.state.user.name;
console.log('mq连接成功'+user)
if (user) {
// user = JSON.parse(user)
//订阅的频道
const topic = MQTT_topic+"-" + user;
console.log("MQ topic:" + topic);
this.client.subscribe(topic,this.responseCallback,this.onFailed);
console.log('mq订阅成功')
}
if (this.timer) {
clearInterval(this.timer)
this.timer = null
this.count = 0
}
this.inCon = false
},
onFailed:function(msg){
console.log("MQ Failed:" + msg);
this.inCon = false
this.reconnectWSConnection()
},
reconnectWSConnection () {
let that = this
if (this.timer === null) {
this.timer = setInterval(() => {
if (this.count === 60) {
console.log('ws重连30分钟未成功,请刷新页面')
clearInterval(that.timer)
return
}
console.log('正在重连mqtt')
if (!that.inCon) {
that.inCon = false
that.ws = new WebSocket(MQTT_URL)
that.client = Stomp.over(that.ws)
that.client.connect(MQTT_USERNAME, MQTT_PASSWORD,this.onConnected,this.onFailed,'/');
}
that.count++
}, 5000)
}
},
handleDownload(row) {
this.download('system/exportRecord/download/'+row.exportRecordId, {}, row.fileName);
},
//成功时的回调函数
responseCallback:function(resp){
try {
debugger
//接收消息的处理
if (resp.body.indexOf('{') >= 0) {
console.log("MQ msg=>" + resp.body);
let res = JSON.parse(resp.body)
let type = res.type;
let code = res.code;
let time = res.time;
let msg = res.msg;
let title = res.title;
let data = res.data;
if(type=="importResult"){
this.$notify({
type: code==200?'success':'error',
title: this.$t("common.import")+title+this.$t(code==200?"common.success":"common.fail"),
dangerouslyUseHTMLString: true,
message: '<div style="width=120px;word-break:break-all;word-wrap:break-word;">'+'"'+data.fileName+'"'+msg+'</div>',
position: 'bottom-right',
duration: 0
});
}else if(type=="importProgress"){
}else if(type=="repSourceCodeResult"){
let me=this;
let downloadUrl='/system/exportRecord/download/'+data.exportRecordId;
let notifyObj=this.$notify({
type: code==200?'success':'error',
title: title,
dangerouslyUseHTMLString: true,
message: '<div style="width=120px;word-break:break-all;word-wrap:break-word;">'+'"'+data.fileName+'"'+msg+'</div>'+'<a href="javascript:void(0);">下载</a>',
position: 'bottom-right',
duration: 0,
onClick:function(){
me.$router.push('/exim/exportRecord?id='+data.exportRecordId);
me.handleDownload(data);
notifyObj.close();
},
});
}else if(type=="repSourceCodeProgress"){
}
}
} catch (error) {
debugger
console.error(error);
}
},
},
computed: {
...mapState(["settings"]),
...mapGetters(["sidebarRouters", "sidebar"]),
activeMenu() {
const route = this.$route;
const { meta, path } = route;
// if set path, the sidebar will highlight the path you set
if (meta.activeMenu) {
return meta.activeMenu;
}
return path;
},
showLogo() {
return this.$store.state.settings.sidebarLogo;
},
variables() {
return variables;
},
isCollapse() {
return !this.sidebar.opened;
}
},
mounted() {
this.connect();
},
};
前端vue2使用mqtt的方式:
// mq websocket协议
import mqtt from 'mqtt';
import {MQTT_URL,MQTT_topic,MQTT_USERNAME,MQTT_PASSWORD} from '@/utils/mqConfig'
export default {
components: { SidebarItem, Logo },
data() {
return {
}
},
methods: {
connect:function(){
const clientId = 'mqttjs_' + Math.random().toString(16).substr(2, 8)
const host = MQTT_URL
const options = {
keepalive: 60,
username: MQTT_USERNAME,
password: MQTT_PASSWORD,
clientId: clientId,
protocolId: 'MQTT',
protocolVersion: 4,
clean: true,
reconnectPeriod: 1000,
connectTimeout: 30 * 1000,
will: {
topic: 'WillMsg',
payload: 'Connection Closed abnormally..!',
qos: 0,
retain: false
},
}
console.log('连接中')
const client = mqtt.connect(host, options)
client.on('error', (err) => {
console.log('连接错误: ', err)
client.end()
})
client.on('reconnect', () => {
console.log('重连...')
})
client.on('connect', () => {
console.log('已连接:' + clientId)
// Subscribe
let user = this.$store.state.user.name;
if (user) {
const topic = MQTT_topic+"-" + user;
console.log("MQ topic:" + topic);
client.subscribe(topic, { qos: 0 })
console.log('mq订阅成功')
client.on('message', (topic, message, packet) => {
console.log(topic+">>>mq接收到消息:"+(typeof message)+">>>"+message)
this.responseCallback(message);
})
}
})
},
handleDownload(row) {
this.download('system/exportRecord/download/'+row.exportRecordId, {}, row.fileName);
},
//成功时的回调函数
responseCallback:function(resp){
try {
debugger
//接收消息的处理
if (resp.indexOf('{') >= 0) {
console.log("MQ msg=>" + resp);
let res = JSON.parse(resp)
let type = res.type;
let code = res.code;
let time = res.time;
let msg = res.msg;
let title = res.title;
let data = res.data;
if(type=="importResult"){
this.$notify({
type: code==200?'success':'error',
title: this.$t("common.import")+title+this.$t(code==200?"common.success":"common.fail"),
dangerouslyUseHTMLString: true,
message: '<div style="width=120px;word-break:break-all;word-wrap:break-word;">'+'"'+data.fileName+'"'+msg+'</div>',
position: 'bottom-right',
duration: 0
});
}else if(type=="importProgress"){
}else if(type=="repSourceCodeResult"){
let me=this;
let downloadUrl='/system/exportRecord/download/'+data.exportRecordId;
let notifyObj=this.$notify({
type: code==200?'success':'error',
title: title,
dangerouslyUseHTMLString: true,
message: '<div style="width=120px;word-break:break-all;word-wrap:break-word;">'+'"'+data.fileName+'"'+msg+'</div>'+'<a href="javascript:void(0);">下载</a>',
position: 'bottom-right',
duration: 0,
onClick:function(){
me.$router.push('/exim/exportRecord?id='+data.exportRecordId);
me.handleDownload(data);
notifyObj.close();
},
});
}else if(type=="repSourceCodeProgress"){
}
}
} catch (error) {
debugger
console.error(error);
}
},
},
computed: {
...mapState(["settings"]),
...mapGetters(["sidebarRouters", "sidebar"]),
activeMenu() {
const route = this.$route;
const { meta, path } = route;
// if set path, the sidebar will highlight the path you set
if (meta.activeMenu) {
return meta.activeMenu;
}
return path;
},
showLogo() {
return this.$store.state.settings.sidebarLogo;
},
variables() {
return variables;
},
isCollapse() {
return !this.sidebar.opened;
}
},
mounted() {
this.connect();
},
};
前端微信小程序使用mqtt的方式:
import {MQTT_URL,MQTT_USERNAME,MQTT_PASSWORD} from '@/utils/mqConfig'
let client = null
// 引入MQTT(也是要npm install mqtt的)
import mqttInfo from 'mqtt/dist/mqtt.js'
// 连接方法
export default {
/**
*订阅多个subscribe传入字符串数组
*/
init(userId, subscribe, fun){
let options = {
username: MQTT_USERNAME,
password: MQTT_PASSWORD,
clientId: 'WX-' + userId,
connectTimeout: 600000,
clean: false,
reconnectPeriod: 5000,
}
if(client){
// 多个页面都有使用mqtt时,不能重复连接, 需关闭后再连接
client.end()
client = null
setTimeout(() => {
this.connect(options, subscribe, fun)
}, 500)
} else {
this.connect(options, subscribe, fun)
}
},
connect(options, subscribe, fun) {
// 主要是ws和wx
// #ifdef H5
//h5的连接是 'ws://' + url。
client = mqttInfo.connect('wss://' + MQTT_URL, options)
// #endif
// #ifdef MP-WEIXIN || MP-ALIPAY || APP-PLUS
//app的连接是 'wx://' + url。
client = mqttInfo.connect('wxs://' + MQTT_URL, options)
// #endif
client.on('connect', (res)=>{
if (client) {
client.subscribe(subscribe , (err)=>{
console.log('订阅主题:' + subscribe)
if (!err) {
console.log('订阅主题成功')
}
})
} else {
console.log('client 对象为空,无法订阅主题。')
}
})
// mqtt离线
client.on('offline', function() {
console.log('offline')
})
//mqtt接收到信息
client.on('message', function(topic, message) {
console.log('mqtt接收到数据:' + message)
let msg = JSON.parse(message.toString())
if (fun) {
fun(msg)
}
})
//mqtt连接失败
client.on('error', (err) => {
console.log('error', err)
//重新连接
client.reconnect()
})
//mqtt断开连接回调
client.on('close', (err) => {
console.log('close', err)
})
},
close() {
if(client) {
console.log('关闭订阅')
client.end()
client = null
}
}
}
前端原生js使用mqtt的方式:
function connect() {
const clientId = 'mqttjs_' + Math.random().toString(16).substr(2, 8)
const host = 'wss://'+domain+'/mqtt'
const options = {
keepalive: 60,
username: "xxx",
password: "xxx",
clientId: clientId,
protocolId: 'MQTT',
protocolVersion: 4,
clean: true,
reconnectPeriod: 1000,
connectTimeout: 30 * 1000,
will: {
topic: 'WillMsg',
payload: 'Connection Closed abnormally..!',
qos: 0,
retain: false
},
}
console.log('连接中')
const client = mqtt.connect(host, options)
client.on('error', (err) => {
console.log('连接错误: ', err)
client.end()
})
client.on('reconnect', () => {
console.log('重连...')
})
client.on('connect', () => {
console.log('已连接:' + clientId)
// Subscribe
client.subscribe('your/topic/#', { qos: 0 })
client.on('message', (topic, message, packet) => {
console.log('收到消息:' + message.toString() + '\nOn topic: ' + topic)
var result = JSON.parse(message.toString());
})
})
}
后台
pom.xml
rabbitmq
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.3.12.RELEASE</version>
</dependency>
spring-mqtt
spring-integration-mqtt已包含了org.eclipse.paho.client.mqttv3
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mqtt</artifactId>
<version>5.5.19</version>
</dependency>
paho-mqtt
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
</dependency>
代码
后台使用rabbitmq+mqtt插件的方式1:
import com.alibaba.fastjson.JSONObject;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
public class TcpClientSendMQ {
@Autowired
private AmqpTemplate rabbitTemplate;
public void sendDataToWeb(TaskType taskType, String username, String title, String code,String msg,Object data) {
JSONObject json = new JSONObject();
json.put("type",taskType.getValue());
json.put("code",code);
json.put("time",System.currentTimeMillis());
json.put("msg",msg);
json.put("title",title);
json.put("data",data);
rabbitTemplate.convertAndSend(RabbitConfig.QUEUE_CMS_WEB_MSG +"-"+ username, JSONObject.toJSONString(json));
}
public void sendMsgToWeb(TaskType taskType, String username, String fileName, String title, String code, String msg) {
JSONObject data = new JSONObject();
data.put("fileName",fileName);
sendDataToWeb(taskType,username,title,code,msg,data);
}
}
后台使用rabbitmq+mqtt的方式2:
import com.alibaba.fastjson.JSONObject;
import com.omni.common.config.MqttSender;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class TcpClientSendMQ {
@Autowired
private MqttSender mqttSender;
public void sendDataToWeb(TaskType taskType, String username, String title, String code,String msg,Object data) {
JSONObject json = new JSONObject();
json.put("type",taskType.getValue());
json.put("code",code);
json.put("time",System.currentTimeMillis());
json.put("msg",msg);
json.put("title",title);
json.put("data",data);
mqttSender.sendWithTopicAndQos(RabbitConfig.QUEUE_CMS_WEB_MSG +"-"+ username, 0, JSONObject.toJSONString(json));
}
public void sendMsgToWeb(TaskType taskType, String username, String fileName, String title, String code, String msg) {
JSONObject data = new JSONObject();
data.put("fileName",fileName);
sendDataToWeb(taskType,username,title,code,msg,data);
}
}
后台使用mqtt工具类(脱离spring,实现连接多mqtt地址):
https://blog.csdn.net/u012643122/article/details/132326165
nginx配置rabbitmq+mqtt插件
location /mqtt {
root /usr/share/nginx/html;
proxy_pass http://127.0.0.1:15674; # 代理rabbitmq
proxy_http_version 1.1;
proxy_connect_timeout 500;
proxy_read_timeout 500;
proxy_send_timeout 500;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
nginx配置rabbitmq+emqx
location /mqtt {
root /usr/share/nginx/html;
proxy_pass http://127.0.0.1:8083/mqtt; # 代理emqx
proxy_http_version 1.1;
proxy_connect_timeout 500;
proxy_read_timeout 500;
proxy_send_timeout 500;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
emqx开启微信mqtt支持
docker exec -it -uroot emqx bash
echo "listeners.ws.default.websocket.fail_if_no_subprotocol = false" >> ./etc/emqx.conf
rabbitmq和emqx的安装
https://blog.csdn.net/u012643122/article/details/133869648