0


SpringBoot 整合MQTT 消息推送

一: pom文件添加依赖

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

二: yml配置文件添加MQTT配置信息

###################### MQTT #################################
mqtt:
  # 服务器地址
  host: tcp://127.0.0.1:1883
  # ID唯一
  clientId: MQTT_WK
  # 主题 多个主题用逗号(,)分割 #表示这个主题下面所有
  topics: topic1,topic2,topic2/topic22/#
  # 用户名
  username: admin
  # 密码
  password: 1q2w3e4r.
  # 连接超时
  timeout: 30
  # 心跳检测
  keepalive: 60
  # 对消息处理的几种机制。
  # 0 表示的是订阅者没收到消息不会再次发送,消息会丢失
  # 1 表示的是会尝试重试,一直到接收到消息,但这种情况可能导致订阅者收到多次重复消息
  # 2 多了一次去重的动作,确保订阅者收到的消息有一次
  qos: 1
  # false为建立持久会话
  cleanSession: false
  # 断开后重新连接
  automaticReconnect: true

三: 新建一个接收MQTT消息的实体用于接收参数(@RequestBody), 也可以直接参数字段(@RequestParam)这一步就不需要了

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @Description 消息实体对象
 * @Author WangKun
 * @Date 2023/4/19 14:59
 * @Version
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MQTTMessage implements Serializable {
    /**
     * MQTT主题
     */
    private String topic;

    /**
     * qos
     */
    private Integer qos = 1;

    /**
     * MQTT内容
     */
    private String content;

}

四: 新建一个MQTT配置类(这可以改造两个配置类,一个生产者一个消费者,yml配置文件也是一个生产者一个消费者)

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @Description MQTT的配置类
 * @Author WangKun
 * @Date 2023/4/19 10:38
 * @Version
 */
@Component
@Data
public class MQTTConfig {

    /**
     * host 服务器地址配置
     */
    @Value("${mqtt.host}")
    private String[] host;

    /**
     * clientId
     */
    @Value("${mqtt.clientId}")
    private String clientId;

    /**
     * 主题
     */
    @Value("${mqtt.topics}")
    private String[] topics;

    /**
     * 用户名
     */
    @Value("${mqtt.username}")
    private String username;

    /**
     * 密码
     */
    @Value("${mqtt.password}")
    private String password;

    /**
     * 连接超时时长
     */
    @Value("${mqtt.timeout}")
    private Integer timeout;

    /**
     * keep Alive时间(心跳检测)
     */
    @Value("${mqtt.keepalive}")
    private Integer keepalive;

    /**
     * 遗嘱消息 QoS
     */
    @Value("${mqtt.qos}")
    private Integer qos;

    /**
     * false为建立持久会话
     */
    @Value("${mqtt.cleanSession}")
    private Boolean cleanSession;

    /**
     * 断开后重新连接
     */
    @Value("${mqtt.automaticReconnect}")
    private Boolean automaticReconnect;
}

五: 新建生产者与消费者

import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;

import javax.annotation.Resource;

/**
 * @Description MQTT生产端配置
 * @Author WangKun
 * @Date 2023/4/19 14:58
 * @Version
 */
@Configuration
public class MQTTProduceConfig {

    @Resource
    private MQTTConfig mqttConfig;

    // 客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息
    private static final byte[] WILL_DATA;

    static {
        WILL_DATA = "offline".getBytes();
    }

    /**
     * @param
     * @Description 出站直连通道
     * @Throws
     * @Return org.springframework.messaging.MessageChannel
     * @Date 2023-04-20 15:15:42
     * @Author WangKun
     */
    @Bean("mqttOut")
    public MessageChannel mqttOutBoundChannel() {
        return new DirectChannel();
    }

    /**
     * @param
     * @Description 创建MqttPahoClientFactory 设置MQTT的broker的连接属性
     * @Throws
     * @Return org.springframework.integration.mqtt.core.MqttPahoClientFactory
     * @Date 2023-04-20 15:15:52
     * @Author WangKun
     */
    @Bean
    public MqttPahoClientFactory outClientFactory() {
        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
        String[] hosts = mqttConfig.getHost();
        MqttConnectOptions options = new MqttConnectOptions();
        options.setServerURIs(hosts);
        options.setUserName(mqttConfig.getUsername());
        options.setPassword(mqttConfig.getPassword().toCharArray());
        options.setConnectionTimeout(mqttConfig.getTimeout());
        options.setKeepAliveInterval(mqttConfig.getKeepalive());
        options.setCleanSession(mqttConfig.getCleanSession());
        options.setAutomaticReconnect(mqttConfig.getAutomaticReconnect());
        options.setWill("willTopic", WILL_DATA, 2, false);
        factory.setConnectionOptions(options);
        return factory;
    }

    /**
     * @param
     * @Description 出站
     * @Throws
     * @Return org.springframework.messaging.MessageHandler
     * @Date 2023-04-20 15:16:05
     * @Author WangKun
     */
    @Bean
    @ServiceActivator(inputChannel = "mqttOut")
    public MessageHandler mqttOutbound() {
        MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(mqttConfig.getClientId() + "_producer", outClientFactory());
        //如果设置成true,即异步,发送消息时将不会阻塞。
        messageHandler.setAsync(true);
        //设置默认QoS
        messageHandler.setDefaultQos(mqttConfig.getQos());
        DefaultPahoMessageConverter defaultPahoMessageConverter = new DefaultPahoMessageConverter();
        //发送默认按字节类型发送消息
//        defaultPahoMessageConverter.setPayloadAsBytes(true);
        messageHandler.setConverter(defaultPahoMessageConverter);
        return messageHandler;
    }
}
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.core.MessageProducer;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import javax.annotation.Resource;

/**
 * @Description MQTT 消费端的配置
 * @Author WangKun
 * @Date 2023/4/19 14:56
 * @Version
 */
@Configuration
@Slf4j
public class MQTTConsumeConfig {

    @Resource
    private MQTTConfig mqttConfig;

    @Resource
    private MQTTMessageHandler mqttMessageHandler;

    // 客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息
    private static final byte[] WILL_DATA;

    static {
        WILL_DATA = "offline".getBytes();
    }

    /**
     * @param
     * @Description 入站直连通道
     * @Throws
     * @Return org.springframework.messaging.MessageChannel
     * @Date 2023-04-20 15:10:25
     * @Author WangKun
     */
    @Bean("mqttInput")
    public MessageChannel mqttInputChannel() {
        return new DirectChannel();
    }

    /**
     * @param
     * @Description 创建MqttPahoClientFactory 设置MQTT的broker的连接属性
     * @Throws
     * @Return org.springframework.integration.mqtt.core.MqttPahoClientFactory
     * @Date 2023-04-20 15:11:33
     * @Author WangKun
     */
    @Bean
    public MqttPahoClientFactory inClientFactory() {
        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
        MqttConnectOptions options = new MqttConnectOptions();
        options.setServerURIs(mqttConfig.getHost());
        options.setUserName(mqttConfig.getUsername());
        options.setPassword(mqttConfig.getPassword().toCharArray());
        options.setConnectionTimeout(mqttConfig.getTimeout());
        options.setKeepAliveInterval(mqttConfig.getKeepalive());
        options.setCleanSession(mqttConfig.getCleanSession());
        options.setAutomaticReconnect(mqttConfig.getAutomaticReconnect());
        options.setWill("willTopic", WILL_DATA, 2, false);
        factory.setConnectionOptions(options);
        return factory;
    }

    /**
     * @param
     * @Description 入站
     * @Throws
     * @Return org.springframework.integration.core.MessageProducer
     * @Date 2023-04-20 15:12:06
     * @Author WangKun
     */
    @Bean
    public MessageProducer producer() {
        MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(mqttConfig.getClientId() + "_customer", inClientFactory(), mqttConfig.getTopics());
        adapter.setCompletionTimeout(5000);
        DefaultPahoMessageConverter defaultPahoMessageConverter = new DefaultPahoMessageConverter();
        // 按字节接收消息
        // defaultPahoMessageConverter.setPayloadAsBytes(true);
        adapter.setConverter(defaultPahoMessageConverter);
        adapter.setQos(mqttConfig.getQos());
        adapter.setOutputChannel(mqttInputChannel());

        return adapter;
    }

    /**
     * @param
     * @Description 通过通道获取数据
     * ServiceActivator注解表明:当前方法用于处理MQTT消息,inputChannel参数指定了用于消费消息的channel。
     * tips:
     * 异步处理
     * @Throws
     * @Return org.springframework.messaging.MessageHandler
     * @Date 2023-04-20 15:12:55
     * @Author WangKun
     */
    @Bean
    @ServiceActivator(inputChannel = "mqttInput")
    public MessageHandler handler() {
        return this.mqttMessageHandler;
    }
}

六: 重写MQTT网关接口

import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.handler.annotation.Header;

/**
 * @Description 网关接口MqttGateway
 * @Author WangKun
 * @Date 2023/4/19 15:01
 * @Version
 */
@MessagingGateway(defaultRequestChannel = "mqttOut")
public interface MQTTGateway {

    /**
     * @param message 消息
     * @Description 定义重载方法,用于消息发送
     * @Throws
     * @Return void
     * @Date 2023-04-20 16:53:22
     * @Author WangKun
     */
    void sendToMqtt(String message);

    /**
     * @param topic   主题
     * @param message 消息
     * @Description 指定topic进行消息发送
     * @Throws
     * @Return void
     * @Date 2023-04-20 16:54:03
     * @Author WangKun
     */
    void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, String message);

    /**
     * @param topic   主题
     * @param qos     qos
     * @param message 消息
     * @Description 指定topic和qos进行消息发送
     * @Throws
     * @Return void
     * @Date 2023-04-20 16:54:33
     * @Author WangKun
     */
    void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String message);

    /**
     * @param topic   主题
     * @param qos     qos
     * @param message 消息(字节数字类型)
     * @Description 指定topic和qos进行消息发送
     * @Throws
     * @Return void
     * @Date 2023-04-20 16:54:56
     * @Author WangKun
     */
    void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, byte[] message);
}

七: 消费端消息拦截提取

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.stereotype.Component;

/**
 * @Description 消费端消息处理工具(消费端消息处理)
 * @Author WangKun
 * @Date 2023/4/20 15:31
 * @Version
 */

@AllArgsConstructor
@Component
@Slf4j
public class MQTTMessageHandler implements MessageHandler {

    /**
     * @Description 消息处理(在这实现接收消息处理业务, 资源注入不进来,可以手动注入)
      * @param message
     * @Throws
     * @Return void
     * @Date 2023-04-20 15:38:22
     * @Author WangKun
     */
    @Override
    public void handleMessage(Message<?> message) throws MessagingException {
        log.info("收到的完整消息为--->{}", message);
        log.info("----------------------");
        log.info("message:" + message.getPayload());
        log.info("Id:" + message.getHeaders().getId());
        log.info("receivedQos:" + message.getHeaders().get(MqttHeaders.RECEIVED_QOS));
        String topic = (String) message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC);
        log.info("topic:" + topic);
        log.info("----------------------");
    }
}

八: 为了方便使用,再封装发送工具


import org.springframework.stereotype.Component;

/**
 * @Description MQTT 消息发送工具类
 * @Author WangKun
 * @Date 2023/4/20 16:13
 * @Version
 */
@Component
public class MQTTUtils {

    /**
     * 资源手动注入
     */
    private static final MQTTGateway mqttGateway = SpringUtils.getBean(MQTTGateway.class);

    /**
     * @param message 消息
     * @Description 定义重载方法,用于消息发送
     * @Throws
     * @Return void
     * @Date 2023-04-20 16:53:22
     * @Author WangKun
     */
    public static void send(String message) {
        mqttGateway.sendToMqtt(message);
    }

    /**
     * @param topic   主题
     * @param message 消息
     * @Description 指定topic进行消息发送
     * @Throws
     * @Return void
     * @Date 2023-04-20 16:54:03
     * @Author WangKun
     */
    public static void send(String topic, String message) {
        mqttGateway.sendToMqtt(topic, message);
    }

    /**
     * @param topic   主题
     * @param qos     qos
     * @param message 消息
     * @Description 指定topic和qos进行消息发送
     * @Throws
     * @Return void
     * @Date 2023-04-20 16:54:33
     * @Author WangKun
     */
    public static void send(String topic, int qos, String message) {
        mqttGateway.sendToMqtt(topic, qos, message);
    }

    /**
     * @param topic   主题
     * @param qos     qos
     * @param message 消息(字节数字类型)
     * @Description 指定topic和qos进行消息发送
     * @Throws
     * @Return void
     * @Date 2023-04-20 16:54:56
     * @Author WangKun
     */
    public static void send(String topic, int qos, byte[] message) {
        mqttGateway.sendToMqtt(topic, qos, message);
    }

}

九: 测试类

/**
 * @Description TODO
 * @Author WangKun
 * @Date 2023/4/20 16:58
 * @Version
 */
@RestController
public class MQTTController {

    @PostMapping("/send")
    public String send(@RequestBody MQTTMessage message) {
        // 发送消息到指定主题
//        mqttGateWay.sendToMqtt(myMessage.getTopic(), 1, myMessage.getContent());
        MQTTUtils.send(message.getTopic(), message.getQos(), message.getContent());
        return "send topic: " + message.getTopic() + ", message : " + message.getContent();
    }

}

整体代码结构

测试结果:

EMQX服务监控:

Apifox:

MQTTX订阅该主题同时也收到消息

MQTTX发送消息到该主题

标签: spring boot java spring

本文转载自: https://blog.csdn.net/WangKun_0612/article/details/130271126
版权归原作者 掐指一算乀缺钱 所有, 如有侵权,请联系我们删除。

“SpringBoot 整合MQTT 消息推送”的评论:

还没有评论