Java-Mqtt-ActiveMq(1)

Java-Mqtt-ActiveMq(1)

通过Java基于Mqtt协议与ActiveMq交互,订阅(获取消息)/推送主题

1、Maven依赖

 <dependency>
   <groupId>org.springframework.integration</groupId>
   <artifactId>spring-integration-mqtt</artifactId>
 </dependency>

2、基本逻辑

1、Mqtt为协议,ActiveMq为消息组件实现Java同消息队列的交互。
2、消息推送:根据topic、username、password将message推送至特定url。
3、消息订阅:根据topic订阅消息,回调函数中可查看消息内容,进行业务逻辑处理。

3、注意事项

1、线程问题:
  推送:只有当业务场景触发时才调用推送,向消息队列发送消息,发送完成进程结束。
  订阅:服务启动便同消息队列保持连接。
  总结:根据使用场景的不同,推送/订阅分别在不同的线程(线程表示不太合适)中使用,订阅过程中Mqtt客户端一直同服务保持连接;推送过程中Mqtt客户端在完成推送之后自动断开不需要重新连接。
2、clientId: 最好使用动态clientid(具体原因不详,暂未找到),使用静态clientid回导致无法获取订阅消息。踩坑。

4、推送-push

/**
 * Mqtt-推送(接口调用)
 * <p>
 * Mqtt-推送在接口调用时使用,支持向不同服务,不同主题发送消息
 *
 * @author: zy
 * @date: 2020-09-24 16:00
 */
@Slf4j
@Component
public class MqttProducerUtil {

    /**
     * Mqtt客户端
     */
    private MqttClient mqttClient;

    /**
     * 客户端ID
     */
    private final String clientId = "PUSH" + (int) (Math.random() * 100000000);


    /**
     * 创建客户端
     *
     * @param mqttCallback 回调函数
     */
    public void setMqttClient(MqttCallback mqttCallback, String url, String userName, String password) throws
            MqttException {
        MqttConnectOptions options = mqttConnectOptions(url, userName, password);
        if (mqttCallback == null) {
            mqttClient.setCallback(new MqttCallBack());
        } else {
            mqttClient.setCallback(mqttCallback);
        }
        mqttClient.connect(options);
    }

    /**
     * 连接客户端
     */
    private MqttConnectOptions mqttConnectOptions(String url, String userName, String password) throws MqttException {
        mqttClient = new MqttClient(url, clientId, new MemoryPersistence());
        MqttConnectOptions options = new MqttConnectOptions();
        options.setUserName(userName);
        options.setPassword(password.toCharArray());
        options.setConnectionTimeout(60);
        // 同接口业务保持同一进程,无需重复连接
        options.setAutomaticReconnect(false);
        options.setCleanSession(false);
        return options;
    }

    /**
     * 向某个主题发布消息 默认qos:1
     *
     * @param topic:发布的主题
     * @param msg:发布的消息
     */
    public void pub(String topic, String msg) throws MqttException {
        log.info("----------开始发布主题:{},消息:{}", topic, msg);
        MqttMessage mqttMessage = new MqttMessage();
        mqttMessage.setPayload(msg.getBytes());
        MqttTopic mqttTopic = mqttClient.getTopic(topic);
        MqttDeliveryToken token = mqttTopic.publish(mqttMessage);
        token.waitForCompletion();
    }

    /**
     * 向某个主题发布消息
     *
     * @param topic: 发布的主题
     * @param msg:   发布的消息
     * @param qos:   消息质量    Qos:0、1、2
     */
    public void pub(String topic, String msg, int qos) throws MqttException {
        log.info("----------开始发布主题:{},消息:{}", topic, msg);
        MqttMessage mqttMessage = new MqttMessage();
        mqttMessage.setQos(qos);
        mqttMessage.setPayload(msg.getBytes());
        MqttTopic mqttTopic = mqttClient.getTopic(topic);
        MqttDeliveryToken token = mqttTopic.publish(mqttMessage);
        token.waitForCompletion();
    }


    /**
     * 关闭MQTT连接
     */
    public void close() throws MqttException {
        mqttClient.close();
        mqttClient.disconnect();
    }

    /**
     * 接口调用
     *
     * @param topic   主题
     * @param message 消息
     */
    public void doPush(String topic, String message, String url, String userName, String password) throws
            MqttException {
        setMqttClient(new MqttCallBack(), url, userName, password);
        pub(topic, message);
    }
}

5、订阅-sub

/**
 * Mqtt-订阅(服务启动自动加载)
 * <p>
 * Mqtt-订阅为服务启动自动加载,如需新增订阅主题需在配置文件中新增(新服务需重新配置)
 *
 * @author: zy
 * @date: 2020-09-24 16:00
 */
@Slf4j
@Component
public class MqttConsumerUtil {

    /**
     * Mqtt客户端
     */
    private MqttClient mqttClient;

    /**
     * 客户端ID
     */
    private final String clientId = "SUB" + (int) (Math.random() * 100000000);

    @Value("${config.mq-url}")
    private String mqUrl;

    @Value("config.mq-username")
    private String mqUserName;

    @Value("${config.mq-password}")
    private String mqPassword;

    @Value("${config.mq-topiclist}")
    private List<String> mqTopicList;


    /**
     * 创建客户端
     *
     * @param mqttCallback 回调函数
     */
    public void setMqttClient(MqttCallback mqttCallback) throws MqttException {
        MqttConnectOptions options = mqttConnectOptions();
        if (mqttCallback == null) {
            mqttClient.setCallback(new MqttCallBack());
        } else {
            mqttClient.setCallback(mqttCallback);
        }
        mqttClient.connect(options);
    }

    /**
     * 客户端连接
     */
    private MqttConnectOptions mqttConnectOptions() throws MqttException {
        mqttClient = new MqttClient(mqUrl, clientId, new MemoryPersistence());
        MqttConnectOptions options = new MqttConnectOptions();
        options.setUserName(mqUserName);
        options.setPassword(mqPassword.toCharArray());
        options.setConnectionTimeout(10);
        options.setAutomaticReconnect(true);
        options.setCleanSession(false);
        return options;
    }

    /**
     * 订阅某一个主题 ,此方法默认的的Qos等级为:1
     */
    public void sub() throws MqttException {
        log.info("----------开始订阅主题----------");
        for (String topic : mqTopicList) {
            mqttClient.subscribe(topic);
        }
    }

    /**
     * 订阅某一个主题,可携带Qos
     *
     * @param qos 消息质量:0、1、2
     */
    public void sub(int qos) throws MqttException {
        log.info("----------开始订阅主题----------");
        for (String topic : mqTopicList) {
            mqttClient.subscribe(topic, qos);
        }
    }

    /**
     * 关闭MQTT连接
     */
    public void close() throws MqttException {
        mqttClient.close();
        mqttClient.disconnect();
    }
}

6、回调函数

/**
 * Mqtt-回调函数
 *
 * @author: zy
 * @date: 2020-09-24 16:01
 */
@Slf4j
@Component
public class MqttCallBack implements MqttCallback {

    /**
     * MQTT 断开连接会执行此方法
     */
    @Override
    public void connectionLost(Throwable throwable) {
        log.info("断开了MQTT连接 :{}", throwable.getMessage());
        log.error(throwable.getMessage(), throwable);
    }

    /**
     * publish发布成功后会执行到这里
     */
    @Override
    public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
        log.info("发布消息成功");
    }

    /**
     * subscribe订阅后得到的消息会执行到这里
     */
    @Override
    public void messageArrived(String topic, MqttMessage message) throws Exception {
        //  TODO    此处可以将订阅得到的消息进行业务处理、数据存储
        log.info("收到来自 " + topic + " 的消息:{}", new String(message.getPayload()));
    }
}

7、监听器

/**
 * Mqtt-监听器
 *
 * @author: zy
 * @date: 2020-09-24 16:14
 */
@Slf4j
@Component
public class MqttListenerUtil implements ApplicationListener<ContextRefreshedEvent> {

    private final MqttConsumerUtil server;

    @Autowired
    public MqttListenerUtil(MqttConsumerUtil server) {
        this.server = server;
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        try {
            server.setMqttClient(new MqttCallBack());
            server.sub();
        } catch (MqttException e) {
            log.error(e.getMessage(), e);
        }
    }
}

代码亲测有效,如有疑问请留言!

往外张望的人在做梦,向内审视的人才是清醒的
原文地址:https://www.cnblogs.com/StefanieYang/p/13725494.html