mqtt

package pro.nbbt.xulian.business.wireless.map.config;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.IntegrationComponentScan;
import org.springframework.stereotype.Component;
import pro.nbbt.xulian.business.common.util.mqtt.wireless.WirelessMqttClientWorker;

import javax.annotation.PostConstruct;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

@Data
@Slf4j
@Component
@IntegrationComponentScan
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "nbbt.wireless.mqtt")
public class WirelessMqttClientPool {

private static final int POOL_SIZE = 4; // Adjust the pool size as needed
private static final BlockingQueue<WirelessMqttClientWorker> pool = new LinkedBlockingQueue<>(POOL_SIZE);

/**
 * 用户名
 */
private String username;
/**
 * 密码
 */
private String password;
/**
 * 连接地址
 */
private String hostUrl;
/**
 * 客户Id
 */
private String clientId;
/**
 * 监听 topic
 */
private String listenTopics;
/**
 * 超时时间
 */
private int timeout;
/**
 * 保持连接数
 */
private int keepalive;

/**
 * 设备在线超时时间,默认10分钟,超过10分钟设备无新消息,则主动设置为离线
 */
private Integer expireSeconds;
/**
 * 是否保存509日志
 */
private boolean savelog;

/**
 * 设备状态延迟更新到数据,单位毫秒
 */
private int statusdelayupdatemillseconds;

private MqttConnectOptions mqttConnectOptions;

public WirelessMqttClientPool() {

}

@PostConstruct
private void initializePool() {
    if (mqttConnectOptions == null) {
        mqttConnectOptions = new MqttConnectOptions();
        mqttConnectOptions.setUserName(username);
        mqttConnectOptions.setPassword(password.toCharArray());
        mqttConnectOptions.setServerURIs(new String[]{hostUrl});
        mqttConnectOptions.setMaxInflight(100000000);
        mqttConnectOptions.setConnectionTimeout(30 * 1000);//连接超时时间
        log.debug("初始化mqttConnectOptions完成:{}", username);
    }
    try {
        for (int i = 0; i < POOL_SIZE; i++) {
            WirelessMqttClientWorker mqttClient = createMqttClientWorker();
            pool.offer(mqttClient);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}


private WirelessMqttClientWorker createMqttClientWorker() throws MqttException {
    // You may need to modify this method based on your requirements
    // For example, you may want to customize host, clientId, options, etc.
    log.debug("打印出url======================:{}", hostUrl);
    return new WirelessMqttClientWorker(hostUrl, clientId + MqttAsyncClient.generateClientId(), mqttConnectOptions,
            "defaultTopic", null);
}

public WirelessMqttClientWorker acquireConnection() {
    try {
        WirelessMqttClientWorker mqttClient = pool.take();
        if (!mqttClient.isConnected()) {
            // If the connection is lost, reconnect
            mqttClient.reconnect();
        }
        return mqttClient;
    } catch (InterruptedException e) {
        log.error("获取连接中断异常");
        return null;
    }
}

public void releaseConnection(WirelessMqttClientWorker mqttClient) {
    if (mqttClient != null) {
        pool.offer(mqttClient);
    }
}

}
package pro.nbbt.xulian.business.common.util.mqtt.wireless;

import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import pro.nbbt.xulian.business.common.util.mqtt.MqttMessageRecieverInterface;
import pro.nbbt.xulian.common.util.redission.RedissionUtils;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Slf4j
public class WirelessMqttClientWorker {
private String host;
private String clientId;
private MqttConnectOptions options;
private String subscribeTopic;
private MqttMessageRecieverInterface msgCallback;

private MqttClient client;
private final int DEFAULT_TOPIC_QOS = 1;
private final String DEFAULT_TEXT_CODE = "UTF-8";
private final Integer timeToWait = 1000;
@Resource
RedissionUtils redissionUtils;

private static final AtomicInteger idWorker = new AtomicInteger(0);

public WirelessMqttClientWorker(String host, String clientId, MqttConnectOptions options, String subscribeTopic, MqttMessageRecieverInterface msgCallback) throws MqttException {
    this.host = host;
    this.clientId = clientId;
    this.options = options;
    this.subscribeTopic = subscribeTopic;
    this.msgCallback = msgCallback;
    log.info(String.format("wireless connet param : %s : %s", this.host, this.clientId));
    this.client = new MqttClient(this.host, this.clientId, new MemoryPersistence());
    this.client.setCallback(new MqttCallbackImpl());
    this.client.setTimeToWait(this.timeToWait);    // 增加pushlish的时间限制,防止发生堵塞
    this.client.connect(this.options);
    log.info(String.format("wireless client param : %s ", client));
    if (StrUtil.isNotBlank(subscribeTopic)) {
        String[] topics = StrUtil.split(this.subscribeTopic, ',').stream()
                .map(String::trim)
                .filter(StrUtil::isNotBlank)
                .collect(Collectors.toList())
                .toArray(new String[]{});

        if (topics.length > 0) {
            this.client.subscribe(topics);
        }
    }
    log.info("wireless MQTT CLIENT({}) 正在连接", clientId);
}

public void close() {
    redissionUtils.setConcurrentLock(clientId);
    try {
        this.client.close();
    } catch (MqttException e) {
        e.printStackTrace();
    } finally {
        redissionUtils.deleteConcurrentLock(clientId);
    }
}

public boolean isConnected() {
    return this.client.isConnected();
}

public void publish(String topic, byte[] bytes) {
    this.publish(topic, bytes, DEFAULT_TOPIC_QOS);
}

public void publish(String topic, byte[] bytes, int qos) {
  /*  redissionUtils.setConcurrentLock(topic);
    try {*/
    MqttMessage msg = new MqttMessage(bytes);
    msg.setQos(qos);
    if (idWorker.get() >= Integer.MAX_VALUE) {
        idWorker.set(0);
    }
    msg.setId(idWorker.getAndIncrement());
    try {
        this.client.publish(topic, msg);
        log.debug("MQTT CLIENT({}) ==={}===> {}", clientId, topic, Message2String(msg));
    } catch (MqttException e) {
        log.warn("MQTT CLIENT({}) 在发送消息时发生错误 {},{},{}", clientId, e.getMessage(), topic, Message2String(msg));
        e.printStackTrace();

        throw new RuntimeException(e.getMessage());
    }
    /*} finally {
        redissionUtils.deleteConcurrentLock(topic);
    }*/
}

public void publish(String topic, String text) {
    try {
        this.publish(topic, text.getBytes(DEFAULT_TEXT_CODE), 0);
    } catch (UnsupportedEncodingException e) {
        log.warn("MQTT CLIENT({}) 在发送消息时发生转换错误 {},{},{}", clientId, e.getMessage(), topic, text);
        e.printStackTrace();

        throw new RuntimeException(e.getMessage());
    }
}

public void publish(String topic, String text, int qos) {
    try {

// this.publish(topic, text.getBytes(DEFAULT_TEXT_CODE),2);
// test 临时改为1,验证fluxMQ无法释放堆内存的问题 记得改回
this.publish(topic, text.getBytes(DEFAULT_TEXT_CODE), qos);
} catch (UnsupportedEncodingException e) {
log.warn("MQTT CLIENT({}) 在发送消息时发生转换错误 {},{},{}", clientId, e.getMessage(), topic, text);
e.printStackTrace();

        throw new RuntimeException(e.getMessage());
    }
}


private final class MqttCallbackImpl implements MqttCallback {

    @Override
    public void connectionLost(Throwable cause) {
        log.error("MQTT CLIENT({}) 丢失连接", clientId);
        if (!client.isConnected()) {
            redissionUtils.setConcurrentLock(clientId);
            try {
                client.reconnect();
                log.info("MQTT CLIENT({}) 正在连接(重连)", clientId);
            } catch (Exception e) {
                log.error("MQTT CLIENT({}) 重连发生错误 {}", clientId, e.getMessage());
                e.printStackTrace();
            } finally {
                redissionUtils.deleteConcurrentLock(clientId);
            }
        }
    }

    @Override
    public void messageArrived(String topic, MqttMessage message) throws Exception {
        log.debug("MQTT CLIENT({}) <==={}=== {}", clientId, topic, Message2String(message));
        if (null != msgCallback) {
            try {
                msgCallback.onMessage(topic, message);
            } catch (Exception ex) {
                log.warn("MQTT CLIENT({}) 在处理消息时发生错误 {},{},{}", clientId, ex.getMessage(), topic, Message2String(message));
                ex.printStackTrace();
            }
        }
    }

    @Override
    public void deliveryComplete(IMqttDeliveryToken token) {

// log.debug("MQTT CLIENT({}) Delivery Complete {}",clientId,token);
}

}


public void reconnect() {
    try {
        log.debug("打印出mqttClient进行重连======================:{}");

        client.reconnect();
    } catch (Exception e) {
        log.error("MQTT CLIENT({}) 重连发生错误 {}", clientId, e.getMessage());
        e.printStackTrace();
    }
}


private String Message2String(MqttMessage message) {
    return "【message:id=" + message.getId() +
            ",qos=" + message.getQos() +
            ",dup=" + message.isDuplicate() +
            ",retain=" + message.isRetained() +
            ",qos=" + message.getQos() +
            ",data=" + HexUtil.encodeHexStr(message.getPayload(), false)
            + "】";
}

}

    try {
        log.info("------------------------------------给基站发送的topic是:" + STATION + StrUtil.SLASH + deviceSn + TOPIC + CMD + "发送的内容是:" + content);
        mqttClientWorker.publish(STATION + StrUtil.SLASH + deviceSn + TOPIC + CMD, content);
        sendStationDeviceLogs(deviceSn, content);
    } catch (Exception e) {
        log.error("给基站发送消息失败,发送的topic是:" + STATION + StrUtil.SLASH + deviceSn + TOPIC + CMD + "发送的内容是:" + content, e);
    } finally {
        bean.releaseConnection(mqttClientWorker);
    }
posted @ 2025-08-14 17:13  kevinWwm  阅读(8)  评论(0)    收藏  举报