spring-boot集成mqtt,实现发布和订阅(工厂模式)

2 篇文章 0 订阅

1.前言

上一篇文章写了spring-boot使用普通方式集成了mqtt,但是网上很多的帖子都是使用工厂模式实现,经过研究终于对工厂模式有些突破,不废话,直接上代码。

2.建一个spring-boot项目

2.1配置文件内容如下(本案例使用的是emqx作为服务端,大家在练习时自己下载,下载地址在上一篇普通方式集成mqtt的内容中有,地址:CSDN)

server.port=8085
#MQTT配置
#MQTT-服务端用户名
spring.mqtt.username=admin
#MQTT-服务端密码
spring.mqtt.password=public
#MQTT-服务端地址
spring.mqtt.url=tcp://localhost:1883
#MQTT-客户端clientid
spring.mqtt.clientid=clientid
#MQTT-默认主题
spring.mqtt.default_topic=test

 2.2加入mqtt相关的maven支持

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>mqtt-factory-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mqtt-factory-demo</name>
    <description>mqtt-factory-demo</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-integration</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-stream</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-mqtt</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2.3 创建类MqttSenderConfig

import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.IntegrationComponentScan;
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.outbound.MqttPahoMessageHandler;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;

@Configuration
@IntegrationComponentScan
@Slf4j
public class MqttSenderConfig {
    private static final Logger LOGGER = LoggerFactory.getLogger(MqttSenderConfig.class);

    @Value("${spring.mqtt.username}")
    private String username;

    @Value("${spring.mqtt.password}")
    private String password;

    @Value("${spring.mqtt.url}")
    private String hostUrl;

    @Value("${spring.mqtt.clientid}")
    private String clientid;

    @Value("${spring.mqtt.default_topic}")
    private String defaultTopic;

    /**
     * 订阅的bean名称
     */
    public static final String CHANNEL_NAME_IN = "mqttInboundChannel";
    /**
     * 发布的bean名称
     */
    public static final String CHANNEL_NAME_OUT = "mqttOutboundChannel";

    /**
     * MQTT连接器选项
     */
    @Bean
    public <hostUrl> MqttConnectOptions getMqttConnectOptions() {
        MqttConnectOptions options = new MqttConnectOptions();
        // 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,
        // 这里设置为true表示每次连接到服务器都以新的身份连接
        options.setCleanSession(false);
        // 设置连接的用户名
        options.setUserName(username);
        // 设置连接的密码
        options.setPassword(password.toCharArray());
        //可以是多个服务端
        options.setServerURIs(new String[]{hostUrl});
        // 设置超时时间 单位为秒
        options.setConnectionTimeout(10);
        // 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送心跳判断客户端是否在线,但这个方法并没有重连的机制
        options.setKeepAliveInterval(20);
        // 设置“遗嘱”消息的话题,若客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息。
        //options.setWill("willTopic", WILL_DATA, 2, false);
        return options;
    }

    /**
     * MQTT客户端
     */
    @Bean
    public MqttPahoClientFactory mqttClientFactory() {
        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
        factory.setConnectionOptions(getMqttConnectOptions());
        return factory;
    }

    /**
     * MQTT信息通道(生产者)
     */
    @Bean(name = CHANNEL_NAME_OUT)
    public MessageChannel mqttOutboundChannel() {
        return new DirectChannel();
    }

    /**
     * MQTT消息处理器(生产者)
     */
    @Bean
    @ServiceActivator(inputChannel = CHANNEL_NAME_OUT)
    public MessageHandler mqttOutbound() {
        MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(
                clientid+"_publish", mqttClientFactory());
        messageHandler.setAsync(true);
        //设置生产者的消息级别
        messageHandler.setDefaultQos(2);
        //这个地方还没理解透,生产消息的时候怎么自由的指定主题?
        messageHandler.setDefaultTopic(defaultTopic);
        return messageHandler;
    }

    /**
     * MQTT消息订阅绑定(消费者)
     */
    @Bean
    public MessageProducer inbound() {
        // 可以同时消费(订阅)多个Topic
        String []topics = new String[]{"publish", "test"};
        MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(
                clientid+"_subsribe", mqttClientFactory(), topics);
        adapter.setCompletionTimeout(5000);
        adapter.setConverter(new DefaultPahoMessageConverter());
        //设置消费者的消息级别
        adapter.setQos(2);
        // 设置订阅通道
        adapter.setOutputChannel(mqttInboundChannel());
        return adapter;
    }

    /**
     * MQTT信息通道(消费者)
     */
    @Bean(name = CHANNEL_NAME_IN)
    public MessageChannel mqttInboundChannel() {
        return new DirectChannel();
    }

    /**
     * MQTT消息处理器(消费者)
     * 持久化也是在这里处理
     */
    @Bean
    @ServiceActivator(inputChannel = CHANNEL_NAME_IN)
    public MessageHandler handler() {
        return new MessageHandler() {
            @Override
            public void handleMessage(Message<?> message)  {
                try {
                    String topic = message.getHeaders().get("mqtt_receivedTopic").toString();
                    String qos = message.getHeaders().get("mqtt_receivedQos").toString();
                    String payload = message.getPayload().toString();
                    LOGGER.info("主题:"+topic);
                    LOGGER.info("内容:"+payload);
                    LOGGER.info("级别:"+qos);
                    //message.get
                } catch (Exception e) {
                    LOGGER.error(e.getMessage(), e);
                }
            }
        };
    }
}

2.4 创建MsgWriter接口

import com.example.mqttfactorydemo.comons.MqttSenderConfig;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

@Component
@MessagingGateway(defaultRequestChannel = MqttSenderConfig.CHANNEL_NAME_OUT)
public interface MsgWriter {

    void sendToMqtt(String data);
    void sendToMqtt(String payload,@Header(MqttHeaders.TOPIC) String topic);
    void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload);

}

2.5 创建测试的controller类

import com.example.mqttfactorydemo.service.MsgWriter;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping(value = "/con")
public class PublishController {

    @Resource
    private MsgWriter msgWriter;

    @RequestMapping(value = "/test")
    public void test(String topic) {
        /**
         * 这两个发送可以
         */
        //msgWriter.sendToMqtt("我是消息");
        msgWriter.sendToMqtt("我是内容", topic);

        /**
         * 下面这个方法测试无效,qos指定了也没用,到现在还没搞清楚原因
         */
        //msgWriter.sendToMqtt("我是主题", 2, "我是消息");
    }
}

3 代码部分就完事儿了,接下来看看测试

先测试只有消息内容的接口,浏览器输入地址:http://localhost:8085/con/test

输出如下,因为默认订阅主题就是test,所以输出时按照默认主题推送

再测试有消息内容和主题的接口,浏览器输入地址:http://localhost:8085/con/test?topic=publish

后台输出如下内容(在MqttSenderConfig类中消费者我们订阅了两个主题:publish和test,所以topic的值只要是这两个的任意一个都可以):

4. 百度网盘代码获取地址

链接:https://pan.baidu.com/s/1SaH45ZYQrOecmImKXxml0Q 
提取码:1234

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值