feat(resource): 添加 MQTT 消息推送
parent
4523a29c53
commit
335aaccdbb
|
@ -39,5 +39,11 @@
|
|||
<version>3.1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.paho</groupId>
|
||||
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
|
||||
<version>1.2.2</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue