一、EMQ配置
其中mqtt的属性是写在Consul配置中心或者其他配置中心的例如Nacos
1、配置类
package com.lkd.emq;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
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.stereotype.Component;
@Configuration
@Component
@Data
@Slf4j
public class MqttConfig {
/*
* 读取Consul中的配置文件
*
* */
@Value("${mqtt.client.username}")
private String username;
@Value("${mqtt.client.password}")
private String password;
@Value("${mqtt.client.serverURI}")
private String serverURI;
@Value("${mqtt.client.clientId}")
private String clientId;
@Value("${mqtt.client.keepAliveInterval}")
private int keepAliveInterval;
@Value("${mqtt.client.connectionTimeout}")
private int connectionTimeout;
@Autowired
private MqttCallback mqttCallback;
@Bean
public MqttClient mqttClient() {
try {
MqttClient client = new MqttClient(serverURI, clientId, mqttClientPersistence());
client.setManualAcks(true); //设置手动消息接收确认
mqttCallback.setMqttClient(client);
client.setCallback(mqttCallback);
client.connect(mqttConnectOptions());
return client;
} catch (MqttException e) {
log.error("emq connect error",e);
return null;
}
}
//封装连接信息
@Bean
public MqttConnectOptions mqttConnectOptions() {
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(username);
options.setPassword(password.toCharArray());
options.setAutomaticReconnect(true);//是否自动重新连接
options.setCleanSession(true);//是否清除之前的连接信息
options.setConnectionTimeout(connectionTimeout);//连接超时时间
options.setKeepAliveInterval(keepAliveInterval);//心跳
options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);//设置mqtt版本
return options;
}
public MqttClientPersistence mqttClientPersistence() {
return new MemoryPersistence();
}
}
2、注册中心
二、应用配置
package com.lkd.emq;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.List;
/*
* 连接建立成功之后,就会执行CallBack
*
* */
@Component
@Slf4j
public class MqttCallback implements MqttCallbackExtended {
//需要订阅的topic配置
@Value("${mqtt.consumer.consumerTopics}")
private List<String> consumerTopics;
@Autowired
private MqttService mqttService;
@Override
public void connectionLost(Throwable throwable) {
log.error("emq error.",throwable);
}
/*2*/
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
log.info( "topic:"+topic+" message:"+ new String(message.getPayload()) );
//处理消息
mqttService.processMessage(topic, message);
//处理成功后确认消息
mqttClient.messageArrivedComplete(message.getId(),message.getQos());
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
log.info("deliveryComplete---------" + iMqttDeliveryToken.isComplete());
}
/*
* 1
*
* */
@Override
public void connectComplete(boolean b, String s) {
//和EMQ连接成功后根据配置自动订阅topic
if(consumerTopics != null && consumerTopics.size() > 0){
consumerTopics.forEach(t->{
try {
log.info(">>>>>>>>>>>>>>subscribe topic:"+t);
mqttClient.subscribe(t, 2);//订阅主题
} catch (MqttException e) {
log.error("emq connect error", e);
}
});
}
}
private MqttClient mqttClient;
public void setMqttClient(MqttClient mqttClient) {
this.mqttClient = mqttClient;
}
}
三、封装发送消息模版
在要发送消息的类中,直接注入MqttProducer ,调用 send方法即可
package com.lkd.emq;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.lkd.utils.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class MqttProducer {
@Value("${mqtt.producer.defaultQos}")
private int defaultProducerQos;
@Value("${mqtt.producer.defaultRetained}")
private boolean defaultRetained;
@Value("${mqtt.producer.defaultTopic}")
private String defaultTopic;
@Autowired
private MqttClient mqttClient;
public void send(String payload) {
this.send(defaultTopic, payload);
}
public void send(String topic, String payload) {
this.send(topic, defaultProducerQos, payload);
}
public void send(String topic, int qos, String payload) {
this.send(topic, qos, defaultRetained, payload);
}
public void send(String topic, int qos, boolean retained, String payload) {
try {
mqttClient.publish(topic, payload.getBytes(), qos, retained);
} catch (MqttException e) {
log.error("publish msg error.",e);
}
}
public <T extends Object> void send(String topic, int qos, T msg) throws JsonProcessingException {
String payload = JsonUtil.serialize(msg);
this.send(topic,qos,payload);
}
}
消息处理接口
package com.lkd.business;
import java.io.IOException;
/**
* 消息处理接口
*/
public interface MsgHandler{
void process(String jsonMsg) throws IOException;
}
消息处理上下文
package com.lkd.business;
/**
* 消息处理上下文
*/
public interface MsgHandlerContext{
MsgHandler getMsgHandler(String msgType);
}
根据不同的主题获取主题内容
package com.lkd.business.handler;
import com.google.common.collect.Maps;
import com.lkd.business.MsgHandler;
import com.lkd.business.MsgHandlerContext;
import com.lkd.emq.Topic;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
public class MsgHandlerContextImp implements ApplicationContextAware, MsgHandlerContext{
private ApplicationContext ctx;
private Map<String, MsgHandler> handlerMap = Maps.newHashMap();
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ctx = applicationContext;
Map<String,MsgHandler> map = ctx.getBeansOfType(MsgHandler.class);
map.values().stream().forEach(v->{
String topic = v.getClass().getAnnotation(Topic.class).value();
handlerMap.put(topic,v);
});
}
public MsgHandler getMsgHandler(String msgType){
return handlerMap.get(msgType);
}
}
三、 接收消息
package com.lkd.handler;
import com.google.common.base.Strings;
import com.lkd.business.MsgHandler;
import com.lkd.config.TopicConfig;
import com.lkd.contract.StatusInfo;
import com.lkd.contract.VmStatusContract;
import com.lkd.emq.Topic;
import com.lkd.service.TaskService;
import com.lkd.utils.JsonUtil;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
/*
*
* 定时,创建自动维修工单
* */
@Topic(TopicConfig.VMS_STATUS_TOPIC)
@Component
public class AutoPublishTaskJob implements MsgHandler {
@Resource
private TaskService taskService;
/*
* 订阅来自售货机网关发送来的异常报文
* */
@Override
public void process(String jsonMsg) throws IOException {
VmStatusContract vmStatusContract = JsonUtil.getByJson(jsonMsg, VmStatusContract.class);
//判断发过来的消息是否为空
if(vmStatusContract==null || Strings.isNullOrEmpty(vmStatusContract.getInnerCode()) ) return;
List<StatusInfo> statusInfoList = vmStatusContract.getStatusInfo();
for (StatusInfo statusInfo : statusInfoList) {
//如果有传感器为异常状态,那么
if (!statusInfo.isStatus()){
//
taskService.AutoSaveTask(vmStatusContract);
return;
}
}
}
}