前言
最近两个项目中都采用了MQTT作为实时消息传输协议,在开发中遇到了不少问题,在这里简单的总结一下,以便记录和帮助大家少走弯路。这篇文章的内容主要来自于https://github.com/emqtt/emqttd 以及http://emqtt.com/。
MQTT是什么
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分。该协议支持所有平台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和制动器(比如通过Twitter让房屋联网)的通信协议。
如何使用MQTT
服务端搭建
本部分将以EMQ为主要介绍对象,EMQ是一个百万级分布式开源物联网MQTT消息服务器。
如下图所示。
主要特点如下。
- 完全开放源码
- 百万级并发连接
- 完整MQTT协议支持
- 简便安装部署
- 分布式集群或桥接
扩展模块与插件
EMQ的部署确实相当简单,特别对于windows用户,
1.http://emqtt.com/downloads 下载windows版本,
2.解压
3.在bin目录下运行 emqttd start即可
4.访问localhost:18083可以知道是否部署成功
tips:
1. EMQ的默认端口:8083 webSocket的端口,18083 工作台端口,1883 后台的通讯端口,8080 API端口
2. EMQ的开源版本是没有数据存储功能的,商业版本是具备数据存储的能力,商业版不便宜,最低的并发版本至少要3W/年。
3. EMQ的工作台是好东西,要充分利用,简单的测试都可以用它
4. EMQ的百万链接并非是并发,这个要注意。
5. MQTT是基于TCP,而MQTT-SN是基于UDP的。
关于数据存储是一个单独的话题,要是并发需求不高可以考虑自己搞一个,原理其实很简单。设定一个管理topic比如tempTopic,若某个topic的消息需要存储则将这个topic发送到tempTopic。再订阅这个topic并将收到的消息持久化即可。
简单流程如下所示
1.订阅tempTopic消息,若收到消息执行下面逻辑
2.订阅收到的消息内容的topic,并将消息内容持久化到数据库中,若收到消息执行下面逻辑
3.将消息内容持久化到数据库中。
4.系统启动时自动监听数据库中tempTopic的消息内容。
Web客户端使用
Web客户端其实就是JS客户端,主要可以用的是Eclipse Paho HTML5 JavaScript over WebSocket和MQTT.js
下面代码以MQTT.js和ES6为例。
import { connect } from 'mqtt';
var client = connect('ws://localhost:8083/mqtt');
client.on('connect', function () {
client.subscribe('presence')
client.publish('presence', 'Hello mqtt')
})
client.on('message', function (topic, message) {
// message is Buffer
console.log(message.toString())
client.end()
})
Java客户端使用
Java客户端则以Eclipse Paho Java为主。
示例
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.cert.CertificateException;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
public class MqttPublishSample {
public static void main(String[] args) throws KeyManagementException, CertificateException, FileNotFoundException, IOException, KeyStoreException {
String topic = "MQTT Examples";
String content = "Message from MqttPublishSample";
int qos = 2;
String broker = "ssl://localhost:8883";
String clientId = "JavaSample";
MemoryPersistence persistence = new MemoryPersistence();
try {
MqttClient sampleClient = new MqttClient(broker, clientId, persistence);
MqttConnectOptions connOpts = new MqttConnectOptions();
connOpts.setCleanSession(true);
System.out.println("Connecting to broker: "+broker);
sampleClient.connect(connOpts);
System.out.println("Connected");
System.out.println("Publishing message: "+content);
MqttMessage message = new MqttMessage(content.getBytes());
message.setQos(qos);
sampleClient.publish(topic, message);
System.out.println("Message published");
client.setCallback(new MqttCallback() {
public void connectionLost(Throwable throwable) {
}
public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
System.out.println("Message Arrived");
System.out.println(new String(mqttMessage.getPayload(),"utf-8"));
}
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
});
sampleClient.disconnect();
System.out.println("Disconnected");
System.exit(0);
} catch(MqttException me) {
System.out.println("reason "+me.getReasonCode());
System.out.println("msg "+me.getMessage());
System.out.println("loc "+me.getLocalizedMessage());
System.out.println("cause "+me.getCause());
System.out.println("excep "+me);
me.printStackTrace();
}
}
}
总结
1.clientId不能重复
2.需要数据存储功能的需要结合实际情况是自研还是购买服务
3.需要进行充分的测试,以避免Last Message引起的异常问题
4.对于是否能够满足性能需求一定要实际测试