物联网架构成长之路(32)-SpringBoot集成MQTT客户端
一、前言
这里虽然是说MQTT客户端。其实对于服务器来说,这里的一个具有超级权限的MQTT客户端,就可以做很多事情。比如手机APP或者网页或者第三方服务需要发送数据到设备,但是这些又不是设备,又不能让他们连到MQTT。那么就可以通过HTTP请求业务服务器。然后由业务服务器利用这个MQTT客户端进行发送数据。
还有,之前好多人问我,怎么保存这些物联网数据,真的要像前面的博客那样,要自己写插件吗?特别麻烦的啊。这里给出的结论是不需要。保存数据,除了写EMQ插件,还可以在EMQ的规则引擎上进行配置Web消息转发【EMQ 3.x 版本】,还有就是这种通过业务服务器订阅根Topic来保存物联网原始数据。
这篇博客这讨论如何把MQTT客户端集成到业务服务器上(基于SpringBoot 2.0)。下一篇博客会讲到数据保存到InfluxDB,然后如何通过Grafana进行可视化Dashboard看板模式展示。
二、配置pom.xml,引入第三方库
<!-- MQTT -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mqtt</artifactId>
</dependency>
三、MQTT客户端代码(Java)
MqttDemoApplication.java
package com.wunaozai.mqtt; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import com.wunaozai.mqtt.tools.MqttPushClient; @SpringBootApplication
public class MqttDemoApplication { public static void main(String[] args) {
SpringApplication.run(MqttDemoApplication.class, args); test();
} private static void test(){
MqttPushClient.MQTT_HOST = "tcp://mqtt.com:1883";
MqttPushClient.MQTT_CLIENTID = "client";
MqttPushClient.MQTT_USERNAME = "username";
MqttPushClient.MQTT_PASSWORD = "password";
MqttPushClient client = MqttPushClient.getInstance();
client.subscribe("/#");
}
}
MqttPushCallback.java
package com.wunaozai.mqtt.tools; import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; /**
* MQTT 推送回调
* @author wunaozai
* @date 2018-08-22
*/
public class MqttPushCallback implements MqttCallback { private static final Logger log = LoggerFactory.getLogger(MqttPushCallback.class); @Override
public void connectionLost(Throwable cause) {
log.info("断开连接,建议重连" + this);
//断开连接,建议重连
} @Override
public void deliveryComplete(IMqttDeliveryToken token) {
//log.info(token.isComplete() + "");
} @Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
log.info("Topic: " + topic);
log.info("Message: " + new String(message.getPayload()));
} }
MqttPushClient.java
package com.wunaozai.mqtt.tools; import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttTopic;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; /**
* 创建一个MQTT客户端
* @author wunaozai
* @date 2018-08-22
*/
public class MqttPushClient { private static final Logger log = LoggerFactory.getLogger(MqttPushClient.class);
public static String MQTT_HOST = "";
public static String MQTT_CLIENTID = "";
public static String MQTT_USERNAME = "";
public static String MQTT_PASSWORD = "";
public static int MQTT_TIMEOUT = 10;
public static int MQTT_KEEPALIVE = 10; private MqttClient client;
private static volatile MqttPushClient mqttClient = null;
public static MqttPushClient getInstance() {
if(mqttClient == null) {
synchronized (MqttPushClient.class) {
if(mqttClient == null) {
mqttClient = new MqttPushClient();
}
}
}
return mqttClient;
} private MqttPushClient() {
log.info("Connect MQTT: " + this);
connect();
} private void connect() {
try {
client = new MqttClient(MQTT_HOST, MQTT_CLIENTID, new MemoryPersistence());
MqttConnectOptions option = new MqttConnectOptions();
option.setCleanSession(true);
option.setUserName(MQTT_USERNAME);
option.setPassword(MQTT_PASSWORD.toCharArray());
option.setConnectionTimeout(MQTT_TIMEOUT);
option.setKeepAliveInterval(MQTT_KEEPALIVE);
option.setAutomaticReconnect(true);
try {
client.setCallback(new MqttPushCallback());
client.connect(option);
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 发布主题,用于通知<br>
* 默认qos为1 非持久化
* @param topic
* @param data
*/
public void publish(String topic, String data) {
publish(topic, data, 1, false);
}
/**
* 发布
* @param topic
* @param data
* @param qos
* @param retained
*/
public void publish(String topic, String data, int qos, boolean retained) {
MqttMessage message = new MqttMessage();
message.setQos(qos);
message.setRetained(retained);
message.setPayload(data.getBytes());
MqttTopic mqttTopic = client.getTopic(topic);
if(null == mqttTopic) {
log.error("Topic Not Exist");
}
MqttDeliveryToken token;
try {
token = mqttTopic.publish(message);
token.waitForCompletion();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 订阅某个主题 qos默认为1
* @param topic
*/
public void subscribe(String topic) {
subscribe(topic, 1);
}
/**
* 订阅某个主题
* @param topic
* @param qos
*/
public void subscribe(String topic, int qos) {
try {
client.subscribe(topic, qos);
} catch (Exception e) {
e.printStackTrace();
}
}
}
四、MQTT客户端代码(C#)
为了下下篇博客Grafana有数据可以展示,我需要开发一个PC小工具【设备仿真】,用来模拟设备一直发送数据。这里就不对C#开发进行过多的说明了。通过nuget,引入第三方mqtt库。这个工具是我现在开发平台工具链的一个小工具。至于里面的Payload协议,可以不用管。读者可以根据自己的业务制定自己的通信协议。
部分C#代码(连接服务器与发送数据)
using MQTTClient.Model;
using MQTTnet;
using MQTTnet.Core;
using MQTTnet.Core.Client;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms; namespace MQTTClient
{
public partial class MainPage : Form
{
public MainPage()
{
InitializeComponent();
init();
}
private void init()
{
txtusername.Text = "";
txtpassword.Text = "";
txtclientid.Text = "";
txttopic.Text = "iot/UUID/device/devicepub/update";
} IMqttClient client = null;
private async Task ConnectMqttServerAsync()
{
if(client == null)
{
client = new MqttClientFactory().CreateMqttClient() as MqttClient;
client.ApplicationMessageReceived += mqttClientApplicationMessageReceived;
client.Connected += mqttClientConnected;
client.Disconnected += mqttClientDisconnected;
}
try
{
await client.DisconnectAsync();
var option = getMQTTOption();
await client.ConnectAsync(option);
}catch(Exception e)
{
Invoke((new Action(() =>
{
lblStatus.Text = "连接服务器失败: " + e.Message;
})));
}
}
private void mqttClientDisconnected(object sender, EventArgs e)
{
Invoke((new Action(() =>
{
lblStatus.Text = "连接服务器失败: ERROR";
})));
}
private void mqttClientConnected(object sender, EventArgs e)
{
Invoke((new Action(() =>
{
lblStatus.Text = "连接服务器成功";
})));
}
private void mqttClientApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs e)
{
//本工具部收数据
throw new NotImplementedException();
} private void btnconnect_Click(object sender, EventArgs e)
{
Task.Run(async () => { await ConnectMqttServerAsync(); });
}
private void btndisconnect_Click(object sender, EventArgs e)
{
client.DisconnectAsync();
}
private void btnsendone_Click(object sender, EventArgs e)
{
sendPayload();
}
private void btnsendts_Click(object sender, EventArgs e)
{
timer1.Interval = Convert.ToInt32(txttime.Text);
timer1.Enabled = true;
}
private void btnstopts_Click(object sender, EventArgs e)
{
timer1.Enabled = false;
}
private void timer1_Tick(object sender, EventArgs e)
{
sendPayload();
}
private int sendPayload()
{
if (client.IsConnected == false)
{
return -;
}
PayloadModel payload = getPayload();
string json = JsonConvert.SerializeObject(payload, Formatting.Indented);
txtview.Text = json;
string topic = txttopic.Text;
var msg = new MqttApplicationMessage(topic, Encoding.Default.GetBytes(json),
MQTTnet.Core.Protocol.MqttQualityOfServiceLevel.AtMostOnce, false);
client.PublishAsync(msg);
lblSendStatus.Text = "发送: " + DateTime.Now.ToLongTimeString();
return ;
} private MqttClientTcpOptions getMQTTOption()
{
MqttClientTcpOptions option = new MqttClientTcpOptions();
string hostname = txthostname.Text;
string[] host_port = hostname.Split(':');
int port = ;
if(host_port.Length >= )
{
hostname = host_port[];
port = Convert.ToInt32(host_port[]);
}
option.Server = hostname;
option.ClientId = txtclientid.Text;
option.UserName = txtusername.Text;
option.Password = txtpassword.Text;
option.Port = port;
option.CleanSession = true;
return option;
} private PayloadModel getPayload()
{
PayloadModel payload = new PayloadModel();
//略
return payload;
} Random rand1 = new Random(System.DateTime.Now.Millisecond);
private int getRandomNum()
{
int data = rand1.Next(, );
return data;
} int linenum = ;
Random rand2 = new Random(System.DateTime.Now.Millisecond);
private int getLineNum()
{
int f = rand2.Next(, );
int data = rand2.Next(, );
if(f % == )
{
linenum += data;
}
else
{
linenum -= data;
}
return linenum;
} }
}

本文地址: https://www.cnblogs.com/wunaozai/p/11147841.html
物联网架构成长之路(32)-SpringBoot集成MQTT客户端的更多相关文章
- 物联网架构成长之路(13)-SpringBoot入门
1. 前言 下载最新版的JavaEE eclipse-jee-oxygen-2-win32-x86_64.zip 安装STS插件 Window->Eclipse Marketplace -> ...
- 物联网架构成长之路(14)-SpringBoot整合thymeleaf
使用thymeleaf作为模版进行测试 在pom.xml 增加依赖 <dependency> <groupId>org.springframework.boot</gro ...
- 物联网架构成长之路(25)-Docker构建项目用到的镜像1
0. 前言 现在项目处于初级阶段,按照规划,先构建几个以后可能会用到的Image,并上传到阿里云的Docker仓库.以后博客中用到的Image,大部分都会用到这几个基础的Image,构建一个简单的物联 ...
- 物联网架构成长之路(31)-EMQ基于HTTP权限验证
看过之前的文章就知道,我之前是通过搞插件,或者通过里面的MongoDB来进行EMQ的鉴权登录和权限验证.但是前段时间发现,还是通过HTTP WebHook 方式来调用鉴权接口比较适合实际使用.还是实现 ...
- 物联网架构成长之路(6)-EMQ权限控制
1. 前言 EMQTT属于一个比较小众的开源软件,很多资料不全,很麻烦,很多功能都是靠猜测,还有就是看官方提供的那几个插件,了解. 2. 说明 上一小节的插件 emq_plugin_wunaozai ...
- 物联网架构成长之路(8)-EMQ-Hook了解、连接Kafka发送消息
1. 前言 按照我自己设计的物联网框架,对于MQTT集群中的所有消息,是要持久化到磁盘的,这里采用一个消息队列中间件Kafka作为数据缓冲,缓冲结果存到数据仓库中,以供后续作为数据分析.由于MQTT集 ...
- 物联网架构成长之路(15)-Jenkins部署SpringBoot
1.前言 现在慢慢也在负责一些工作了.这段时间有空,就多了解了解软件多人开发的一些知识.以前项目都是我一个人做的,从数据库设计到后端再到前端,全部放在一个war包丢到tomcat里面然后运行,利用to ...
- 物联网架构成长之路(47)-利用GitLab实现CI持续集成
0.前言 前段时间,考虑到要练习部署一套CI/CD的系统.一开始考虑到Jenkins,随着这两天的了解,发现最新版的GitLab已经提供有CI/CD集成了.所以本次博客,干脆一步到位,直接用GitLa ...
- 物联网架构成长之路(33)-EMQ数据存储到influxDB
一.前言 时隔一年半,技术变化特别快,学习也要跟上才行.以前写过EMQ数据转存问题,当时用了比较笨的方法,通过写插件的方式,把MQTT里面的数据发送到数据库进行存储.当时也是为了学习erlang和em ...
随机推荐
- Java连载47-多态基础语法、作用
一.多态的语法 1.两个类之间没有继承关系的,使用多态是不能编译的. 2.无论向上还是向上转型,都需要有继承关系. 3.什么时候需要向下转型? 当调用的方法或者属性是子类型特有的,在父类型中不存在,就 ...
- IT兄弟连 HTML5教程 HTML5和JavaScript的关系
JavaScript可是实现HTML5重要语言.长久以来,JavaScript一直都是在HTML中实现动态效果的不二之选,而JavaScript在一些程序员眼里都是编程语言中的二等公民.早先,它经常是 ...
- 洛谷 P4999(数位DP)
###洛谷 P4999 题目链接 ### 题目大意:给你一个区间,求这段区间中所有数的,数位上的,数字之和. 分析: 这题与 洛谷 P2602 相似,稍微改一下就可以了. 求出 0 ~ 9 的个数,然 ...
- 导出HTML5 Canvas图片并上传服务器功能
这篇文章主要介绍了导出HTML5 Canvas图片并上传服务器功能,文中通过实例代码给大家介绍了HTML5 Canvas转化成图片后上传服务器,代码简单易懂非常不错,具有一定的参考借鉴价值,需要的朋友 ...
- vue 开发常见问题解决大全
vue添加favicon.ico,包含开发环境和生产环境显示. 1.把图标放在下项目的根目录.. 2.修改build文件夹下面的webpack.dev.conf.js(开发环境) 和webpack.p ...
- ActiveMQ是什么,为什么使用MQ
是基于 Java 中的 JMS 消息服务规范实现的一个消息中间件. 1.系统解耦 采用中间件之后,就可以完美解决上述中因为耦合可能导致的问题.系统 A 不用去 关心下层服务调用方的问题. 2. 异步调 ...
- C++ 类的前向声明的用法
我们知道C++的类应当是先定义,然后使用.但在处理相对复杂的问题.考虑类的组合时,很可能遇到俩个类相互引用的情况,这种情况称为循环依赖. 例如: class A { public: void f(B ...
- [debug]ubuntu共享文件夹所在目录
使用Vmware虚拟机,Vmware Tools工具的复制粘贴一直无效,之后采用共享文件夹. 其默认的是在 \mnt\hgfs 下,在Vmware的设置中建立好文件夹,将文件传入进去,之后就可以去 \ ...
- docker安装完报错:Failed to start docker.service: Unit docker.service is masked
执行 systemctl start docker 报错 Failed to start docker.service: Unit docker.service is masked. 解决 syste ...
- 2019 Python100道 面试 题,你会几道?
0 遇到过得反爬虫策略以及解决方法? 1.通过headers反爬虫 2.基于用户行为的发爬虫:(同一IP短时间内访问的频率) 3.动态网页反爬虫(通过ajax请求数据,或者通过JavaScript生成 ...