feat(resource): 添加 MQTT 消息推送

master
yang 2025-03-13 14:54:21 +08:00
parent 4523a29c53
commit 335aaccdbb
8 changed files with 383 additions and 0 deletions

View File

@ -39,5 +39,11 @@
<version>3.1.2</version> <version>3.1.2</version>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.2</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -0,0 +1,29 @@
package com.mcwl.resource.domain;
import lombok.extern.log4j.Log4j2;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
@Log4j2
public class ImageCommentLikePushCallback implements MqttCallback {
@Override
public void connectionLost(Throwable cause) {
// 连接丢失后,一般在这里面进行重连
System.out.println("连接断开,可以做重连");
}
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
// subscribe后得到的消息会执行到这里面
System.out.println("接收消息主题:" + topic);
System.out.println("接收消息Qos:" + message.getQos());
System.out.println("接收消息内容:" + new String(message.getPayload()));
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("deliveryComplete---------" + token.isComplete());
}
}

View File

@ -0,0 +1,29 @@
package com.mcwl.resource.domain;
import lombok.extern.log4j.Log4j2;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
@Log4j2
public class ImageLikePushCallback implements MqttCallback {
@Override
public void connectionLost(Throwable cause) {
// 连接丢失后,一般在这里面进行重连
System.out.println("连接断开,可以做重连");
}
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
// subscribe后得到的消息会执行到这里面
System.out.println("接收消息主题:" + topic);
System.out.println("接收消息Qos:" + message.getQos());
System.out.println("接收消息内容:" + new String(message.getPayload()));
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("deliveryComplete---------" + token.isComplete());
}
}

View File

@ -0,0 +1,28 @@
package com.mcwl.resource.domain;
import lombok.extern.log4j.Log4j2;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
@Log4j2
public class ModelCommentLikePushCallback implements MqttCallback {
@Override
public void connectionLost(Throwable cause) {
// 连接丢失后,一般在这里面进行重连
System.out.println("连接断开,可以做重连");
}
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
// subscribe后得到的消息会执行到这里面
System.out.println("接收消息主题:" + topic);
System.out.println("接收消息Qos:" + message.getQos());
System.out.println("接收消息内容:" + new String(message.getPayload()));
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("deliveryComplete---------" + token.isComplete());
}
}

View File

@ -0,0 +1,28 @@
package com.mcwl.resource.domain;
import lombok.extern.log4j.Log4j2;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
@Log4j2
public class ModelLikePushCallback implements MqttCallback {
@Override
public void connectionLost(Throwable cause) {
// 连接丢失后,一般在这里面进行重连
System.out.println("连接断开,可以做重连");
}
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
// subscribe后得到的消息会执行到这里面
System.out.println("接收消息主题:" + topic);
System.out.println("接收消息Qos:" + message.getQos());
System.out.println("接收消息内容:" + new String(message.getPayload()));
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("deliveryComplete---------" + token.isComplete());
}
}

View File

@ -0,0 +1,28 @@
package com.mcwl.resource.domain;
import lombok.extern.log4j.Log4j2;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
@Log4j2
public class WorkFlowCommentLikePushCallback implements MqttCallback {
@Override
public void connectionLost(Throwable cause) {
// 连接丢失后,一般在这里面进行重连
System.out.println("连接断开,可以做重连");
}
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
// subscribe后得到的消息会执行到这里面
System.out.println("接收消息主题:" + topic);
System.out.println("接收消息Qos:" + message.getQos());
System.out.println("接收消息内容:" + new String(message.getPayload()));
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("deliveryComplete---------" + token.isComplete());
}
}

View File

@ -0,0 +1,28 @@
package com.mcwl.resource.domain;
import lombok.extern.log4j.Log4j2;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
@Log4j2
public class WorkFlowLikePushCallback implements MqttCallback {
@Override
public void connectionLost(Throwable cause) {
// 连接丢失后,一般在这里面进行重连
System.out.println("连接断开,可以做重连");
}
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
// subscribe后得到的消息会执行到这里面
System.out.println("接收消息主题:" + topic);
System.out.println("接收消息Qos:" + message.getQos());
System.out.println("接收消息内容:" + new String(message.getPayload()));
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("deliveryComplete---------" + token.isComplete());
}
}

View File

@ -0,0 +1,207 @@
package com.mcwl.resource.util;
import com.mcwl.resource.domain.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static com.mcwl.common.utils.Threads.sleep;
@Log4j2
public class EMQXUtil {
private static final String BROKER_URL = "tcp://192.168.136.128:1883";
private static final int connectionTimeout = 30;
private static final int keepAliveInterval = 60;
private static final int MAX_RETRIES = 3;
// 线程池管理连接线程
private static final ExecutorService executor = Executors.newFixedThreadPool(3);
// 客户端存储(替代原来的静态变量)
private static final Map<TopicConfig, MqttClient> clients = new ConcurrentHashMap<>();
// 主题配置(修正后的主题定义)
@Getter
@AllArgsConstructor
public enum TopicConfig {
MODEL_LIKE("modelLikeTopic/1",
"modelLikeTopic/1",
"modelLikeClient_"),
MODEL_COMMENT_LIKE("modelCommentLikeTopic/1",
"modelCommentLikeTopic/1",
"modelCommentLikeClient_"),
IMAGE_LIKE("imageLikeTopic/1",
"imageLikeTopic/1",
"imageLikeClient_"),
IMAGE_COMMENT_LIKE("imageCommentLikeTopic/1",
"imageCommentLikeTopic/1",
"imageCommentLikeClient_"),
WORKFLOW_LIKE("workFlowLikeTopic/1",
"workFlowLikeTopic/1",
"workFlowLikeClient_"),
WORKFLOW_COMMENT_LIKE("workFlowCommentLikeTopic/1",
"workFlowCommentLikeTopic/1",
"workFlowCommentLikeClient_");
final String subTopic;
final String pubTopic;
final String clientIdPrefix;
}
// 获取客户端(线程安全版本)
private static MqttClient getClient(TopicConfig type) {
return clients.computeIfAbsent(type, t -> {
try {
MqttClient client = new MqttClient(BROKER_URL,
generateClientId(t), new MemoryPersistence());
setupConnection(client, t);
return client;
} catch (MqttException e) {
throw new RuntimeException("客户端初始化失败", e);
}
});
}
// 带自动重连的连接设置
private static void setupConnection(MqttClient client, TopicConfig type) {
MqttConnectOptions opts = new MqttConnectOptions();
opts.setAutomaticReconnect(true);
opts.setCleanSession(true);
opts.setConnectionTimeout(connectionTimeout);
opts.setKeepAliveInterval(keepAliveInterval);
client.setCallback(createCallback(type));
// 异步连接(避免阻塞)
executor.submit(() -> {
int retries = 0;
while (!client.isConnected() && retries < MAX_RETRIES) {
try {
client.connect(opts);
client.subscribe(type.subTopic);
clients.put(type, client);
} catch (MqttException e) {
long delay = (long) Math.min(1000 * Math.pow(2, retries), 30000);
sleep(delay);
retries++;
}
}
if (retries >= MAX_RETRIES) {
clients.remove(type);
log.error("连接失败超过最大重试次数");
}
});
}
// 回调工厂方法
private static MqttCallback createCallback(TopicConfig type) {
switch (type) {
case MODEL_LIKE:
return new ModelLikePushCallback();
case IMAGE_LIKE:
return new ImageLikePushCallback();
case WORKFLOW_LIKE:
return new WorkFlowLikePushCallback();
case MODEL_COMMENT_LIKE:
return new ModelCommentLikePushCallback();
case IMAGE_COMMENT_LIKE:
return new ImageCommentLikePushCallback();
case WORKFLOW_COMMENT_LIKE:
return new WorkFlowCommentLikePushCallback();
default:
throw new IllegalArgumentException("未知主题类型: " + type);
}
}
// 使用不同的ClientId增加随机后缀
private static String generateClientId(TopicConfig config) {
return config.clientIdPrefix + UUID.randomUUID().toString().substring(0, 8);
}
// 消息发布方法(带连接检查)
public static void sendMessage(TopicConfig type, String message) {
sendMessage(type, type.pubTopic, 2, message);
}
// 其他发布方法类似,可进一步抽象
public static void sendMessage(TopicConfig type, String topic, int qos, String msg) {
int retry = 0;
MqttClient client = getClient(type);
send(client, topic, qos, msg, retry);
}
public static void sendMessage(MqttClient client, String topic, int qos, String msg) {
int retry = 0;
send(client, topic, qos, msg, retry);
}
private static void send(MqttClient client, String topic, int qos, String msg, int retry) {
waitForConnection(client); // 等待最多5秒
while (retry < 3) {
try {
if (client.isConnected()) {
client.publish(topic, msg.getBytes(), qos, false);
} else {
log.error("客户端未连接!");
}
break;
} catch (MqttException e) {
retry++;
sleep(1000L * retry);
log.error("发布失败: {}", e.getMessage());
}
}
}
private static void waitForConnection(MqttClient client) {
long start = System.currentTimeMillis();
while (!client.isConnected() && (System.currentTimeMillis() - start) < 5000) {
sleep(100);
}
}
// 关闭所有连接
public static void shutdown() {
executor.shutdown();
try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
clients.forEach((k, v) -> closeClient(v));
clients.clear();
}
private static void closeClient(MqttClient client) {
try {
if (client != null) {
client.disconnect();
client.close();
}
} catch (MqttException e) {
log.error("关闭客户端错误: {}", e.getMessage());
}
}
}