管理员 3 months ago
parent 5d5063a291
commit 94f7d67452

@ -17,9 +17,8 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.eclipse.paho</groupId> <groupId>org.springframework.integration</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId> <artifactId>spring-integration-mqtt</artifactId>
<version>1.2.5</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
@ -27,15 +26,9 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId> <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-cache</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

@ -1,178 +1,70 @@
package com.ruoyi.mqtt; package com.ruoyi.mqtt;
import com.ruoyi.mqtt.config.MqttConfig; import com.ruoyi.mqtt.config.MqttConfig;
import org.eclipse.paho.client.mqttv3.IMqttAsyncClient; import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
import java.nio.charset.StandardCharsets; import java.nio.charset.Charset;
/**
* MQTT便MQTT
* <p>
* {@link MqttFactory}MQTT
* (QoS)
* </p>
*/
public class MqttUtil { public class MqttUtil {
private MqttUtil(){ private MqttUtil(){
}
/**
* MQTT
*
* @return MQTT
*/
public static MqttFactory getFactory() {
return MqttConfig.getMqttFactory();
}
/**
* MQTT
*
* @return MQTT
*/
public static MqttItem getItem() {
return getFactory().get();
}
/**
* MQTT
*
* @param configName
* @return MQTT
*/
public static MqttItem getItem(String configName) {
return getFactory().get(configName);
}
/**
* MQTT
*
* @return MQTT
*/
public static IMqttAsyncClient getClient() {
return getItem().getClient();
} }
/** public static MqttClient getClient(){
* MQTT return MqttConfig.getClient();
*
* @param configName
* @return MQTT
*/
public static IMqttAsyncClient getClient(String configName) {
return getItem(configName).getClient();
} }
/** /**
* MQTT *
* * @param topic -
* @param configName * @param message -
* @param sendName * @throws MqttException
* @param payload () * @throws MqttPersistenceException
* @param params ,{0},{1}...
* @throws MqttException MQTT
*/
public static IMqttDeliveryToken send(String configName, String sendName, byte[] payload, String... params)throws MqttException {
return getFactory().send(configName, sendName, payload, params);
}
/**
* MQTT
*
* @param configName
* @param sendName
* @param payload ()
* @param params ,{0},{1}...
* @throws MqttException MQTT
*/
public static IMqttDeliveryToken send(String configName, String sendName, String payload, String... params)throws MqttException {
return getFactory().send(configName, sendName, payload.getBytes(StandardCharsets.UTF_8), params);
}
/**
* MQTT(使)
*
* @param sendName
* @param payload ()
* @param params ,{0},{1}...
* @throws MqttException MQTT
*/ */
public static IMqttDeliveryToken send(String sendName, byte[] payload, String... params)throws MqttException { public static void publish(String topic, MqttMessage message) throws MqttException, MqttPersistenceException{
return getFactory().send(sendName, payload, params); if(MqttConfig.getClient()==null || !MqttConfig.getClient().isConnected()) {
throw new RuntimeException("mqtt未连接");
} }
MqttConfig.getClient().publish(topic,message);
/**
* MQTT(使)
*
* @param sendName
* @param payload ()
* @param params ,{0},{1}...
* @throws MqttException MQTT
*/
public static IMqttDeliveryToken send(String sendName, String payload, String... params)throws MqttException {
return getFactory().send(sendName, payload.getBytes(StandardCharsets.UTF_8), params);
} }
/** /**
* MQTT *
* * @param topic -
* @param configName * @param payload -
* @param topic * @param qos - qos
* @param payload () * @param retained -
* @param qos QoS 0 QoS 1 QoS 2 * @throws MqttException
* @param retained * @throws MqttPersistenceException
* @throws MqttException MQTT
*/ */
public static IMqttDeliveryToken send(String configName, String topic, byte[] payload, int qos, boolean retained) throws MqttException{ public static void publish(String topic, byte[] payload, int qos, boolean retained) throws MqttException, MqttPersistenceException{
return getFactory().send(configName, topic, payload, qos, retained); MqttMessage message = new MqttMessage(payload);
message.setQos(qos);
message.setRetained(retained);
publish(topic,message);
} }
/**
* MQTT
*
* @param configName
* @param topic
* @param payload ()
* @param qos QoS 0 QoS 1 QoS 2
* @param retained
* @throws MqttException MQTT
*/
public static IMqttDeliveryToken send(String configName, String topic, String payload, int qos, boolean retained) throws MqttException{
return getFactory().send(configName, topic, payload.getBytes(StandardCharsets.UTF_8), qos, retained);
}
/**
* MQTT(使)
*
* @param topic
* @param payload ()
* @param qos QoS 0 QoS 1 QoS 2
* @param retained
* @throws MqttException MQTT
*/
public static IMqttDeliveryToken send(String topic, byte[] payload, int qos, boolean retained) throws MqttException{
return getFactory().send(topic, payload, qos, retained);
}
/** /**
* MQTT(使) *
* * @param topic -
* @param topic * @param payload -
* @param payload () * @param qos - qos
* @param qos QoS 0 QoS 1 QoS 2 * @param retained -
* @param retained * @throws MqttException
* @throws MqttException MQTT * @throws MqttPersistenceException
*/ */
public static IMqttDeliveryToken send(String topic, String payload, int qos, boolean retained) throws MqttException{ public static void publish(String topic, String payload, int qos, boolean retained) throws MqttException, MqttPersistenceException{
return getFactory().send(topic, payload.getBytes(StandardCharsets.UTF_8), qos, retained); MqttMessage message = new MqttMessage(payload.getBytes(Charset.forName("UTF-8")));
message.setQos(qos);
message.setRetained(retained);
publish(topic,message);
} }
} }

@ -1,181 +1,167 @@
package com.ruoyi.mqtt.config; package com.ruoyi.mqtt.config;
import com.ruoyi.mqtt.MqttEventHandler; import com.ruoyi.mqtt.MqttConnectionEvent;
import com.ruoyi.mqtt.MqttFactory; import com.ruoyi.mqtt.MqttConnectionLostEvent;
import com.ruoyi.mqtt.MqttItem; import com.ruoyi.mqtt.MqttMessageDeliveryEvent;
import com.ruoyi.mqtt.MqttUtil; import com.ruoyi.mqtt.MqttMessageEvent;
import com.ruoyi.mqtt.event.MqttEvent;
import com.ruoyi.mqtt.event.MqttSendEvent;
import com.ruoyi.mqtt.event.MqttSendTopicEvent;
import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.boot.ApplicationArguments; import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.ApplicationRunner; import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.core.Ordered; import org.springframework.integration.mqtt.support.MqttUtils;
import org.springframework.core.PriorityOrdered; import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import java.util.Collections; import java.util.Arrays;
import java.util.Map; import java.util.Random;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.ScheduledThreadPoolExecutor;
@Configuration @Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = MqttProperties.PREFIX, name = "enabled", havingValue = "true", matchIfMissing = false)
@EnableConfigurationProperties(MqttProperties.class) @EnableConfigurationProperties(MqttProperties.class)
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class MqttConfig implements MqttFactory, MqttEventHandler, PriorityOrdered { public class MqttConfig {
private final MqttProperties properties; private final MqttProperties properties;
private final ApplicationContext act; private final ApplicationEventPublisher publisher;
@Getter private static MqttClient client;
private static MqttFactory mqttFactory;
private ScheduledExecutorService executorService; private static MqttCallback mqttCallback;
private final Map<String, MqttItem> clients = new ConcurrentHashMap<>(); private static MqttConnectOptions options;
@PostConstruct private static AtomicBoolean isReConnect = new AtomicBoolean(false);
public void init() throws Exception {
if (!properties.getEnabled()) {
log.info("mqtt模块未激活");
return;
}
log.info("mqtt模块启动中...");
try {
executorService = act.getBeansOfType(ScheduledExecutorService.class).values().stream().findFirst().get();
} catch (Exception e) {
executorService = new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors());
}
properties.getConfigs().forEach((a, b) -> {
if (!b.getEnabled()) {
return;
}
put(a, b, this);
});
mqttFactory = this;
}
@Override public static MqttClient getClient() {
public boolean next(MqttEvent event, MqttItem item) { return client;
log.debug("mqtt event: {} = {}", event.getConfigName(), event.getClass().getName());
act.publishEvent(event);
return true;
} }
@PreDestroy @Bean
public void destroy() { @ConditionalOnMissingBean(MqttClient.class)
clients.forEach((a, b) -> { public MqttClient mqttClient(MqttConnectOptions options, MqttCallback callback) throws Exception {
log.debug("mqtt:开始创建mqtt客户端");
client = new MqttClient(properties.getUrl(), properties.getClientId()+'_'+Long.toString(System.currentTimeMillis()-new Random().nextInt(),36), new MemoryPersistence());
try{ try{
b.destroy(); connect();
} catch (Exception e) { }catch (Exception ex) {
log.error("mqtt客户端关闭失败:" + a, e); log.error("mqtt:连接异常:"+properties.getUrl(), ex);
}
});
try {
executorService.shutdown();
} catch (Exception e) {
throw new RuntimeException(e);
} }
return client;
} }
public MqttItem get(String configName) { @Bean
return clients.get(configName); public MqttCallback mqttCallback() {
} mqttCallback = new MqttCallback() {
@Override
public void put(String configName, MqttProperties.Config config, MqttEventHandler... handlers) { public void connectionLost(Throwable throwable) {
if (clients.containsKey(configName)) { log.warn("mqtt:连接断开", throwable);
throw new RuntimeException("配置项已经存在"); publisher.publishEvent(new MqttConnectionLostEvent(throwable));
}
MqttItem mqttItem = new MqttItem(configName, config, executorService);
if (handlers != null && handlers.length > 0) {
Collections.addAll(mqttItem.getMessageHandlers(), handlers);
}
clients.put(configName, mqttItem);
log.debug("mqtt配置项添加:" + configName);
} }
public boolean contains(String configName) { @Override
return clients.containsKey(configName); public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
log.trace("mqtt:接收到主题为{}的消息", s);
publisher.publishEvent(new MqttMessageEvent(s, mqttMessage));
} }
public void remove(String configName) { @Override
if (contains(configName)) { public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
get(configName).destroy(); log.trace("mqtt:确认发送到主题为{}的消息", iMqttDeliveryToken.getTopics());
clients.remove(configName); publisher.publishEvent(new MqttMessageDeliveryEvent(iMqttDeliveryToken));
} }
};
return mqttCallback;
} }
@Bean
public MqttConnectOptions mqttConnectOptions() {
// 连接设置
options = new MqttConnectOptions();
// 是否清空session设置false表示服务器会保留客户端的连接记录订阅主题qos,客户端重连之后能获取到服务器在客户端断开连接期间推送的消息
// 设置为true表示每次连接服务器都是以新的身份
options.setCleanSession(properties.getCleanSession());
// 设置连接用户名
options.setUserName(properties.getUsername());
// 设置连接密码
options.setPassword(properties.getPassword().toCharArray());
// 设置超时时间,单位为秒
options.setConnectionTimeout(properties.getConnectionTimeout());
// 设置心跳时间 单位为秒,表示服务器每隔 1.5*20秒的时间向客户端发送心跳判断客户端是否在线
options.setKeepAliveInterval(properties.getKeepAliveInterval());
// 设置遗嘱消息的话题,若客户端和服务器之间的连接意外断开,服务器将发布客户端的遗嘱信息
options.setWill(properties.getWillTopic(), properties.getWillMessage().getBytes(), properties.getWillQos(), false);
// 设置重连
options.setAutomaticReconnect(false);
public IMqttDeliveryToken send(String configName, String topic, byte[] payload, int qos, boolean retained) throws MqttException { return options;
MqttItem item = get(configName);
if (item == null) {
throw new RuntimeException("mqtt客户端未找到:" + configName);
}
log.debug("mqtt发送成功:configName={},topic={}", configName, topic);
return item.getClient().publish(topic, payload, qos, retained);
} }
public IMqttDeliveryToken send(String configName, String sendName, byte[] payload, String... params) throws MqttException { public void connect() throws Exception {
MqttItem item = get(configName); log.debug("mqtt:url={},clientId={}", client.getServerURI(), client.getClientId());
if (item == null) { // 设置回调
throw new RuntimeException("mqtt客户端未找到:" + configName); client.setCallback(mqttCallback);
}
MqttProperties.Topic topic = item.getConfig().getSends().get(sendName); client.connect(MqttUtils.cloneConnectOptions(options));
if (topic == null) { if (properties.getTopic() != null && properties.getTopic().length > 0) {
throw new RuntimeException("mqtt配置的主题未找到:" + configName + " = " + sendName); if (properties.getTopic().length != properties.getQos().length) {
throw new Exception("mqtt:订阅的主题和qos不一致");
} }
String topicTempalte = topic.getTopic(); client.subscribe(properties.getTopic(), properties.getQos());
if (params != null) { log.debug("mqtt:订阅了主题={}", Arrays.toString(properties.getTopic()));
for (int i = 0; i < params.length; i++) {
topicTempalte = topicTempalte.replace("{" + i + "}", params[i]);
} }
log.debug("mqtt:创建客户端成功");
publisher.publishEvent(new MqttConnectionEvent());
} }
log.debug("mqtt发送成功:configName={},sendName={},topic={}", configName, sendName, topicTempalte); @Scheduled(fixedDelay = 5000)
return item.getClient().publish(topicTempalte, payload, topic.getQos(), topic.getRetained()); public void reConnect() {
}
if (!client.isConnected()) {
@EventListener try {
public void listener(MqttSendEvent event) throws MqttException { client.disconnect();
send(event.getConfigName(), event.getSendName(), event.getPayload(), event.getParams()); } catch (Exception e) {
}
/* try {
client.close();
} catch (Exception e) {
}*/
try {
connect();
} catch (Exception ex) {
log.error("mqtt:连接异常:"+properties.getUrl(), ex);
}
} }
@EventListener
public void listener(MqttSendTopicEvent event) throws MqttException {
send(event.getConfigName(), event.getTopic(), event.getPayload(), event.getQos(), event.isRetained());
} }
// @Scheduled(cron = "*/5 * * * * ?") /**
// public void test() { *
// String s = Long.toString(System.currentTimeMillis(), 36); */
// try { @PreDestroy
//// act.publishEvent(new MqttSendEvent("test",s.getBytes(StandardCharsets.UTF_8),s)); public void disConnect() {
// MqttUtil.send("test", s, new String[]{s}); try {
// log.info("test success:{}", s); if (client != null) {
// } catch (Exception e) { client.disconnect();
// log.info("test error:" + s, e); }
// } } catch (Exception e) {
// } }
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE; // 最高优先级
} }
} }

@ -1,194 +1,93 @@
package com.ruoyi.mqtt.config; package com.ruoyi.mqtt.config;
import com.ruoyi.mqtt.MqttVersion;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.*; import java.util.UUID;
/**
* MQTT
* <p>
* MQTTMQTT
* </p>
*/
@Data @Data
@ConfigurationProperties(value = MqttProperties.PREFIX,ignoreInvalidFields = true,ignoreUnknownFields = true) @ConfigurationProperties(value = MqttProperties.PREFIX,ignoreInvalidFields = true,ignoreUnknownFields = true)
public class MqttProperties { public class MqttProperties {
public final static String DEFAULT = "default";
/** /**
* *
*/ */
public static final String PREFIX = "spring.mqtt"; public static final String PREFIX = "spring.mqtt";
/** /**
* MQTT *
* : false * : false
*/ */
private Boolean enabled = false; private Boolean enabled = false;
/**
*
* : "default"
*/
private String defaultConfig = DEFAULT;
/**
* MQTTkeyvalue
*/
private Map<String, Config> configs = new HashMap<>();
/**
* MQTT
* <p>
* MQTT(QoS)
* </p>
*/
@Data
public static class Topic {
/**
* 使 "+" "#"
* 使:{0},{1},{2},使params
*/
private String topic;
/**
* (QoS)
* : 0
*/
private Integer qos = 0;
/**
* ,
* : false
*/
private Boolean retained = false;
}
/**
* MQTT
* <p>
* MQTT
* </p>
*/
@Data
public static class Config {
/**
* MQTT
* : true
*/
private Boolean enabled = true;
/** /**
* () * ()
* : 36 * : UUID
*/ */
private String clientId = Long.toString(System.currentTimeMillis(), 36); private String clientId = UUID.randomUUID().toString();
/** /**
* URL * url
* : tcp://127.0.0.1:1883 * : tcp://127.0.0.1:1883
*/ */
private String url="tcp://127.0.0.1:1883"; private String url="tcp://127.0.0.1:1883";
/**
* URL()
*/
private List<String> urls = new ArrayList<>();
/** /**
* sessionfalseqos, * sessionfalseqos,
* true * true
* : true * : true
*/ */
private Boolean cleanSession = true; private Boolean cleanSession = true;
/** /**
* *
* : admin * : admin
*/ */
private String username = "admin"; private String username = "admin";
/** /**
* * : 123456
* : 123456
*/ */
private String password = "123456"; private String password = "123456";
/** /**
* , * ,
* : 5 * : 100
*/ */
private Integer connectionTimeout = 5; private Integer connectionTimeout = 100;
/** /**
* 60线 * 60线
* : 60 * : 60
*/ */
private Integer keepAliveInterval = 60; private Integer keepAliveInterval = 60;
/** /**
* *
* : will/topic * : will/topic
*/ */
private String willTopic = "will/topic"; private String willTopic = "will/topic";
/** /**
* *
* : offline * : offline
*/ */
private String willMessage = "offline"; private String willMessage = "offline";
/** /**
* (QoS) * Qos
* : 0 * : 0
*/ */
private Integer willQos = 0; private Integer willQos = 0;
/** /**
* *
*/
private List<Topic> subscribes = new ArrayList<>();
/**
*
* : true ()
*/ */
// private Boolean automaticReconnect = true; private String[] topic;
// private Integer maxReconnectDelay = Integer.MAX_VALUE;
/** /**
* WebSocket * qos
*/ */
private Properties customWebSocketHeaders = new Properties(); private int[] qos;
/**
* SSL
*/
private Properties sslProperties = new Properties();
/**
* keyvalue
*/
private Map<String, Topic> sends = new HashMap<>();
/**
* MQTT
* : MQTT_VERSION_DEFAULT
*/
private MqttVersion mqttVersion = MqttVersion.MQTT_VERSION_DEFAULT;
/**
*
* : 10
*/
private Integer maxInflight = 10;
}
} }

@ -1,2 +1 @@
com.ruoyi.mqtt.config.MqttConfig com.ruoyi.mqtt.config.MqttConfig
com.ruoyi.mqtt.rmi.config.MqttRmiConfig

@ -1,8 +1,12 @@
package com.ruoyi.cron.event; package com.ruoyi.cron.event;
import cn.hutool.core.collection.ListUtil;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.cron.vo.CronTaskVo;
import lombok.Getter; import lombok.Getter;
import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEvent;
import java.lang.reflect.Method;
import java.util.List; import java.util.List;
/** /**
@ -26,4 +30,16 @@ public class CronTaskEvent extends ApplicationEvent {
this.paramELs = paramELs; this.paramELs = paramELs;
this.cronId = cronId; this.cronId = cronId;
} }
public static CronTaskEvent of(Method method, String... paramEls) {
String taskId = CronTaskVo.id(method);
return new CronTaskEvent(null,taskId,ListUtil.of(paramEls));
}
public static void publishEvent(Method method,String... paramEls) {
SpringUtils.publishEvent(of(method, paramEls));
}
} }

@ -170,7 +170,7 @@ public class CronRunner implements ApplicationRunner {
// 执行成功后,继续执行子任务 // 执行成功后,继续执行子任务
if (ObjUtil.isNotNull(event.getCronId() != null)) { if (ObjUtil.isNotNull(event.getCronId())) {
List<CronTask> list = MongoUtil.find(CronTask.class, MongoUtil.conditions().put("pid", event.getCronId()).put("enabled", true)); List<CronTask> list = MongoUtil.find(CronTask.class, MongoUtil.conditions().put("pid", event.getCronId()).put("enabled", true));
if (CollUtil.isNotEmpty(list)) { if (CollUtil.isNotEmpty(list)) {
list.forEach(cronTask -> { list.forEach(cronTask -> {

Loading…
Cancel
Save