消息接收+数据持久化:Mosquitto+MQTT+MySQL笔记

https://blog.csdn.net/yangkaizzz/article/details/112258463

消息接收+数据持久化:Mosquitto+MQTT+MySQL
文章目录
消息接收+数据持久化:Mosquitto+MQTT+MySQL
业务需求:
具体实现
1 数据库连接——C3P0数据库连接池
1.1添加依赖
1.2 配置
1.3 编写工具类
2 Mosquitto 客户端的JAVA实现
2.1 MQTT协议实现方式
2.2 从Mosquitto存取数据
2.2.1 添加依赖
2.2.2 客户端订阅
2.2.3 消息接收后,业务逻辑添加
业务需求:
服务器能实时更新设备参数和加载最新数据
服务器能将从Mosquitto收到的数据持久化到本地的数据库
具体实现
1 数据库连接——C3P0数据库连接池
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。

1.1添加依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
1.2 配置
在src下的source配置目录(maven项目)新建一个名叫 c3p0-config.xml 的文件(必须是这个文件名)
主要包括:初始化连接池时建立多少个连接、连接池最少多少个连接 最多容纳多少连接、每个连接的生存时间、连接池能同时允许多少个操作进行, 以及对具体数据库连接的配置:数据库的驱动、 数据库的URL、 数据库登录名、 数据库密码、对这个数据库的连接池的细化配置(比如初始化时建立多少连接, 最多最少连接数等等)。
一个数据库的连接池配置用一个 节点来定义。在C3P0Utils中创建连接池时把 “标识” 作为连接池的构造函数的参数传入,则C3P0在配置文件中找到同名节点,按照这个节点的配置来创建相应配置的连接池。

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- 默认配置,如果没有指定则使用这个配置 -->
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">
<![CDATA[jdbc:mysql://127.0.0.1:3306/aa?useUnicode=true&characterEncoding=UTF-8]]>
</property>
<property name="user">root</property>
<property name="password">0000</property>
<!-- 初始化池大小 -->
<property name="initialPoolSize">2</property>
<!-- 最大空闲时间 -->
<property name="maxIdleTime">30</property>
<!-- 最多有多少个连接 -->
<property name="maxPoolSize">10</property>
<!-- 最少几个连接 -->
<property name="minPoolSize">2</property>
<!-- 每次最多可以执行多少个批处理语句 -->
<property name="maxStatements">50</property>
</default-config>

<!-- 命名的配置 -->
<named-config name="yk">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/aa</property>
<property name="user">userName</property>
<property name="password">password</property>
<property name="acquireIncrement">5</property><!-- 如果池中数据连接不够时一次增长多少个 -->
<property name="initialPoolSize">100</property>
<property name="minPoolSize">50</property>
<property name="maxPoolSize">1000</property>
<property name="maxStatements">0</property>
<property name="maxStatementsPerConnection">5</property>
</named-config>
</c3p0-config>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1.3 编写工具类
需要定义获取connection、释放connection的方法

package data.c3p0Utils;

import java.sql.Connection;
import java.sql.SQLException;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Utils {
private C3P0Utils() {}
private static ComboPooledDataSource ds = null;
private static ThreadLocal<Connection>tLocal = new ThreadLocal<>();
static {
ds = new ComboPooledDataSource("yk");//读取名为yk的配置
public static Connection getConnection() {
Connection con = tLocal.get();
if(con==null) {
try {
con = ds.getConnection();
tLocal.set(con);
} catch (SQLException e) {
System.out.println("连接数据库出错");
e.printStackTrace();
}
}
return con;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2 Mosquitto 客户端的JAVA实现
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议,MQTT消息的发送和订阅都是依赖MQTT服务器。

2.1 MQTT协议实现方式
实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者、代理、订阅者,其中消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者也可以是订阅者。

MQTT传输的消息分为:主题(Topic)和负载(payload)两部分:

主题:可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload)
负载:可以理解为消息内容,是指的订阅者具体收到的数据。
1
2
2.2 从Mosquitto存取数据
MQ是消息中间件,是一种在分布式系统中应用程序借以传递消息的媒介,常用的有ActiveMQ,RabbitMQ,kafka,Mosquitto (我们这里使用的Mosquitto)

2.2.1 添加依赖
了解Paho
Paho Java客户端是一个用Java编写的MQTT客户端库,用于开发在JVM或其他Java兼容平台(如Android)上运行的应用程序。
Paho Java客户端提供了两个API:MqttAsyncClient提供了一个完全异步的API,通过已注册的回调通知完成活动。 MqttClient是MqttAsyncClient的一个同步包装,其中函数与应用程序同步。

<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.0</version>
</dependency>
1
2
3
4
5
2.2.2 客户端订阅
package com.NWPU.wsn.data;

import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


public class MyMqttClient {

public static final String HOST = "tcp://10.70.143.83:1883";
public static final String TOPIC = "test";
private static final String clientid = "client1ID";
private MqttClient client;
private MqttConnectOptions options;
private String userName = "userName";
private String passWord = "password";

private ScheduledExecutorService scheduler;

//重新链接
public void startReconnect() {
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new Runnable() {
public void run() {
if (!client.isConnected()) {
try {
client.connect(options);
} catch (MqttSecurityException e) {
e.printStackTrace();
} catch (MqttException e) {
e.printStackTrace();
}
}
}
}, 0 * 1000, 10 * 1000, TimeUnit.MILLISECONDS);
}

private void start() {
try {
// host为主机名,test为clientid即连接MQTT的客户端ID,一般以客户端唯一标识符表示,MemoryPersistence设置clientid的保存形式,默认为以内存保存
client = new MqttClient(HOST, clientid, new MemoryPersistence());
// MQTT的连接设置
options = new MqttConnectOptions();
// 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,这里设置为true表示每次连接到服务器都以新的身份连接
options.setCleanSession(true);
// 设置连接的用户名
options.setUserName(userName);
// 设置连接的密码
options.setPassword(passWord.toCharArray());
// 设置超时时间 单位为秒
options.setConnectionTimeout(10);
// 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制
options.setKeepAliveInterval(20);
// 设置回调
client.setCallback(new MqttReceriveCallback());
MqttTopic topic = client.getTopic(TOPIC);

//setWill方法,如果项目中需要知道客户端是否掉线可以调用该方法。设置最终端口的通知消息
options.setWill(topic, "close".getBytes(), 0, true);

client.connect(options);
//订阅消息
int[] Qos = {1};
String[] topic1 = {TOPIC};
client.subscribe(topic1, Qos);
} catch (Exception e) {
e.printStackTrace();
}
}
public void disconnect() {
try {
client.disconnect();
} catch (MqttException e) {
e.printStackTrace();
}
}

public static void main(String[] args) throws MqttException {
MyMqttClient client = new MyMqttClient();
client.start();
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
2.2.3 消息接收后,业务逻辑添加
即编写发布消息的回调类:

必须实现MqttCallback的接口并实现对应的相关接口方法CallBack 类将实现 MqttCallBack。

每个客户机标识都需要一个回调实例。在此示例中,构造函数传递客户机标识以另存为实例数据。

在回调中,将它用来标识已经启动了该回调的哪个实例。

必须在回调类中实现三个方法:

public void connectionLost(Throwable cause) 在断开连接时调用。调用在

public void deliveryComplete(MqttDeliveryToken token))

public void messageArrived(MqttTopic topic, MqttMessage message) 接收已经预订的发布。在此部分需要将接收到的消息解析,数据的持久化操作,具体为流程为:先对收到的来自于网关的json字符串进行反序列化,将其转化为sensor实体类,编写SQL语句,实现写数据库

package data;

import data.c3p0Utils.C3P0Utils;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;

import java.sql.Connection;
import java.sql.Statement;

public class MqttReceriveCallback implements MqttCallback {

public void connectionLost(Throwable cause) {
// 连接丢失后,一般在这里面进行重连
System.out.println("连接断开,正在尝试做重连.......");
MyMqttClient client = new MyMqttClient();
client.startReconnect();
System.out.println("重连成功..........");
}
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("deliveryComplete---------" + token.isComplete());
}
public void messageArrived(String topic, MqttMessage message) throws Exception {
System.out.println("接收消息主题 : " + topic);
System.out.println("接收消息Qos : " + message.getQos());
System.out.println("接收消息内容 : " + new String(message.getPayload()));

//json字符串返回值反序列化为实体类
Sensor sensor = JSONObject.parseObject(new String(message.getPayload()), Sensor.class);
//将消息存储进入数据库

String sql= "INSERT INTO sensor (sID,sTemperature,sHumidity) VALUES ("
+sensor.getsId()+","+sensor.getsTemperature()+","+sensor.getsHumidity()+");";
Connection conn = null;
try {
conn = C3p0Utils.getConnection();
} catch (MyError myError) {
myError.printStackTrace();
}
Statement stmt=conn.createStatement();
stmt.executeUpdate(sql);;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
**注:**Statement 接口提供了三种执行 SQL 语句的方法:executeQuery、executeUpdate 和execute。使用哪一个方法由 SQL 语句所产生的内容决定。

executeQuery 用于产生单个结果集的语句,例如 SELECT 语句

executeUpdate 用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQLDDL(数据定义语言)语句。
INSERT、UPDATE 或 DELETE语句的效果是修改表中零行或多行中的一列或多列,executeUpdate 的返回值是一个整数,表示受影响的行数(即更新计数)。
对于CREATE TABLE 或 DROP TABLE 等不操作行的语句,executeUpdate 的返回值总为零

execute 用于执行返回多个结果集、多个更新计数或二者组合的语句,多数程序员不会需要该高级功能。

sensor实体类

package com.NWPU.wsn.data.pojo;

import java.util.Date;

public class Sensor {
private float sTemperature;
private float sHumidity;
private String sDate;
private int sId;

public Sensor(float sTemperature, float sHumidity, String sDate, int sId) {
this.sTemperature = sTemperature;
this.sHumidity = sHumidity;
this.sDate = sDate;
this.sId = sId;
}

@Override
public String toString() {
return "Sensor{" +
"sTemperature=" + sTemperature +
", sHumidity=" + sHumidity +
", sDate='" + sDate + '\'' +
", sId=" + sId +
'}';
}

public float getsTemperature() {
return sTemperature;
}

public void setsTemperature(float sTemperature) {
this.sTemperature = sTemperature;
}

public float getsHumidity() {
return sHumidity;
}

public void setsHumidity(float sHumidity) {
this.sHumidity = sHumidity;
}

public String getsDate() {
return sDate;
}

public void setsDate(String sDate) {
this.sDate = sDate;
}

public int getsId() {
return sId;
}

public void setsId(int sId) {
this.sId = sId;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

————————————————
版权声明:本文为CSDN博主「木易岂几」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yangkaizzz/article/details/112258463

posted @ 2021-05-25 10:51  _海阔天空  阅读(3766)  评论(0编辑  收藏  举报