MqttConfig类
package cm.soft.collect.config;
import cm.soft.collect.biz.CollectBiz;
import cm.soft.collect.service.OnMqttCallBack;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Configuration
@PropertySource("classpath:/application.yml")
public class MqttConfig implements AutoCloseable {
@Autowired
LogConfig log;
@Autowired
CollectBiz collectBiz;
@Value("${mqtt.username}")
private String username;
@Value("${mqtt.password}")
private String password;
@Value("${mqtt.subTopics}")
private String SUB_TOPICS;
@Value("${mqtt.topic}")
private String defaultTopic;
@Value(("${mqtt.brokerUrl}"))
private String brokerUrl;
@Value("${mqtt.clientId}")
private String clientId;
private int qos = 0;// 默认等级
private final MemoryPersistence persistence;
private final ExecutorService executorService;// 启动关闭连接
private final MqttConnectOptions options;// 连接选项
private final DisconnectedBufferOptions disconnectedBufferOptions;// 初始化连接必须, 连接成功之后设置连接断开的缓冲配置
private volatile IMqttAsyncClient mqttClient;
public MqttConfig() {
this.disconnectedBufferOptions = new DisconnectedBufferOptions();
this.options = new MqttConnectOptions();
this.persistence = new MemoryPersistence();
this.executorService = Executors.newSingleThreadExecutor();
}
@PostConstruct
private void init() {
// 初始化options
options.setServerURIs(new String[]{brokerUrl});
options.setUserName(username);
options.setPassword(password.toCharArray());
options.setConnectionTimeout(0);// 防止 ERROR o.e.p.c.mqttv3.internal.ClientState - Timed out as no activity 错误
options.setKeepAliveInterval(200);// 设置会话心跳时间,单位为秒 服务器会每隔1.5*20秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制
options.setCleanSession(false); // 设置是否清空session,这里设置为true表示每次连接到服务器都以新身份连接
options.setAutomaticReconnect(true);// 自动重连
//表示允许多大数量的QoS为1或2消息被同时进行传输处理。这些消息包括正在进行握手的消息和进行重新发送的消息。默认为20个,
//如果设置为0,表示不设限制;如果为1,则会确保消息被顺序处理。
// options.setMaxInflight();
// 初始化disconnectedBufferOptions
disconnectedBufferOptions.setBufferSize(100);//离线后最多缓存100条
disconnectedBufferOptions.setPersistBuffer(false); //不一直持续留存
disconnectedBufferOptions.setDeleteOldestMessages(false);//删除旧消息
disconnectedBufferOptions.setBufferEnabled(true);// 断开连接后进行缓存
// 初始化mqttClient
if (mqttClient == null) {
mqttClient = getMqqtClient();
}
((MqttAsyncClient) mqttClient).setBufferOpts(disconnectedBufferOptions);
mqttClient.setCallback(new OnMqttCallBack(log, this, collectBiz)); // 设置mqttClient的回调
// 初始化连接
connect();
}
/**
* 获得 MqttPahoClientFactory
*
* @return MqttPahoClientFactory
*/
@Bean
public MqttPahoClientFactory clientFactory() {
final DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
factory.setConnectionOptions(options);
factory.setPersistence(persistence);
return factory;
}
/**
* mqttclent连接
*/
public void connect() {
executorService.submit(() -> {// 连接关闭都用一个单独的线程控制
try {
mqttClient.connect(options);
} catch (MqttException e) {
log.error("mqtt客户端启动连接失败");
e.printStackTrace();
}
});
}
/**
* 订阅主题,订阅的时候进行连接
*
* @param topics 主题
* @throws MqttException
*/
public void subscribe(String topics) throws MqttException {
mqttClient.subscribe(topics, qos);
}
/**
* 获得单例的mqqt连接
*
* @return IMqttAsyncClient
*/
@Bean
public IMqttAsyncClient getMqqtClient() {
if (mqttClient == null) {
synchronized (MqttConfig.class) {
if (mqttClient == null) {
try {
setMqttClient();
} catch (MqttException e) {
log.error("设置mqqtClient失败");
}
}
}
}
return mqttClient;
}
/**
* 设置mqttClient
*
* @throws MqttException
*/
private void setMqttClient() throws MqttException {
this.mqttClient = clientFactory().getAsyncClientInstance(brokerUrl, clientId);
}
/**
* 关闭mqtt连接
*/
@Override
@PreDestroy
public void close() {
if (this.mqttClient != null && this.mqttClient.isConnected()) {
executorService.submit(() -> {
try {
log.info("关闭mqqt连接");
mqttClient.disconnect();
mqttClient.close();
} catch (MqttException e) {
log.error("关闭mqqt连接失败");
e.printStackTrace();
}
});
}
}
}
回调类
package cm.soft.collect.service;
import cm.soft.collect.config.LogConfig;
import cm.soft.collect.config.MqttConfig;
import cm.soft.collect.biz.CollectBiz;
import cm.soft.collect.threadUtil.CustomTaskThreads;
import cm.soft.collect.threadUtil.CustomThread;
import lombok.SneakyThrows;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.stereotype.Component;
/**
* emq回调类
*/
@Component
public class OnMqttCallBack implements MqttCallbackExtended {
private final LogConfig log;
private final MqttConfig mqttConfig;
private final CollectBiz collectBiz;
static CustomThread messageArrivedThread = (CustomThread) CustomTaskThreads.MessageArrivedThtead;
public OnMqttCallBack(final LogConfig log, final MqttConfig mqttConfig, final CollectBiz collectBiz) {
this.log = log;
this.mqttConfig = mqttConfig;
this.collectBiz = collectBiz;
}
@Override
public void connectionLost(Throwable throwable) {
log.error("连接丢失" + throwable.getMessage());
mqttConfig.connect();
}
@Override
public void messageArrived(String topic, MqttMessage mqttMessage) {
messageArrivedThread.setOnceWork(() -> {
collectBiz.saveDataToSql(topic, mqttMessage);
});
// 模拟同步代码,代表比较耗时的任务,最终会丢失连接,同步的方法必须在处理同步代码之后才能得到下一个消息,
// 得到消息的时间就会变很长,会导致大量消息的堆积,报Eof
// for(int i=0;i<1000000000;i++){
//
// }
}
/**
* 交付完成回调。在publish消息的时候会收到此回调.
* qos:
* 0 发送完则回调 最多一次传送 (只负责传送,发送过后就不管数据的传送情况)
* 1 至少一次传送 (确认数据交付) 会在对方收到时候回调
* 2 正好一次传送 (保证数据交付成功) 会在对方收到时候回调
*
* @param iMqttDeliveryToken
*/
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
iMqttDeliveryToken.isComplete();
}
/**
* 连接完成回调
*
* @param reconnect true 断开重连,false 首次连接
* @param serverURI 服务器URI
*/
@SneakyThrows
@Override
public void connectComplete(boolean reconnect, String serverURI) {
if (!reconnect) {
log.info("mqtt客户端连接成功");
} else {
log.info("mqtt客户端重新连接成功");
}
// 连接完成进行订阅
collectBiz.subscribe();
}
}
业务类
package cm.soft.collect.biz;
import cm.soft.collect.config.MqttConfig;
import cm.soft.collect.threadUtil.CustomThreadPoolFactory;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.ExecutorService;
@Component
public class CollectBiz {
@Autowired
MqttConfig mqttConfig;
@Autowired
MachineCurrentProcessBizState machineCurrentProcessForVersionBizState;
@Autowired
MachineProcessOnChangedBizState machineProcessOnChangedBizState;
/**
* 先进行订阅具体的主题
*
* @throws MqttException
*/
public void subscribe() throws MqttException {
mqttConfig.subscribe("V_2/+/+/+/+/+/+/+/+/+");
}
public void saveDataToSql(String topic, MqttMessage mqttMessage) {
BizType bizType = BizType.filterCollectType(topic);
if (bizType != null) {
if (bizType.equals(BizType.SAVE_MACHINE_PROCESS_ON_CHANGED_TOSQL)) {
machineProcessOnChangedBizState.saveDataToSql(topic, mqttMessage);
} else if (bizType.equals(BizType.SAVE_MACHINE_CURRENT_PROCESS_FOR_VERSION_TOSQL)) {
machineCurrentProcessForVersionBizState.saveDataToSqlOnRequest(topic, mqttMessage);
}
}
}
}