Idea+maven+spring-cloud项目搭建系列--10 整合 mqtt(mosquitto)

前言:

  • 本位为 Idea+maven+spring-cloud项目搭建系列,maven项目的创建可以参考:
    https://blog.csdn.net/l123lgx/article/details/121467823
  • 本文使用了nacos 作为微服务的注册与发现,nacos 阿里云服务器的安装可以参考:https://blog.csdn.net/l123lgx/article/details/121421431
    nacos 服务端的配置和使用可以参考:
    https://blog.csdn.net/l123lgx/article/details/121491529
  • 本文在安装Mqtt(eclipse-mosquitto)服务基础上进行的扩展,阿里云轻量服务器–Docker–Mqtt(eclipse-mosquitto)安装可以参考:https://blog.csdn.net/l123lgx/article/details/121519938

1 mqtt 简介:
Mqtt 是一中传输协议,底层依靠的是tcp ,与http 和udp 相同度都是数据传输协议;与http 不同的是他是一个客户端-服务端架构的发布/订阅模式的消息传输协议;最初目标是在昂贵的、不可靠的通信线路上尽可能地实现最小和最有效的数据传输,也即他是为受限的设备提供的数据传输协议;它的设计思想是轻巧、开放、简单、规范,易于实现。这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT),因为在物联网上大多数的设备由于带宽和内存的限制,所以在物联网中使用Mqtt 协议进行数据传输无疑是最加的选择;

2 springboot 整合mqtt:
2.1 引入jar:

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

2.2 nacos 添加配置:

spring:
	mqtt:
	    send:
	      #完成超时时间
	      completionTimeout: 3000
	      #通过mqtt发送消息验证所需用户名
	      username: mmy
	      #通过mqtt发送消息验证所需密码
	      password: mmy123
	      #连接的mqtt地址
	      url: tcp://192.168.1.101:1883
	      #客户端id
	      clientId: clint1
	      #推送主题  后面跟着#是监控下面所有的话题
	      topic: topic
	      #topic: my-test
	      # 会话心跳时间
	      keepAliveInterval: 20
	      # 设置连接超时时间
	      connectionTimeout: 3000
	    # 要消费的topic配置
	    receive:
	      topic: sharjeck/ai/test/out,test

2.3 java配置和使用类
MqttConfig:

package com.mmy.springmmyservice.config;

import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.messaging.MessageChannel;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.messaging.MessageHandler;

/**
 * @Description TODO
 * @Date 2022/12/20 10:07
 * @Author lgx
 * @Version 1.0
 */
@Configuration
public class MqttConfig {
    private static final byte[] WILL_DATA;

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

    /**
     * mqtt订阅者使用信道名称
     */
    public static final String CHANNEL_NAME_IN = "mqttInboundChannel";
    /**
     * mqtt发布者信道名称
     */
    public static final String CHANNEL_NAME_OUT = "mqttOutboundChannel";
    /**
     * mqtt发送者用户名
     */
    @Value("${spring.mqtt.send.username}")
    private String username;
    /**
     * mqtt发送者密码
     */
    @Value("${spring.mqtt.send.password}")
    private String password;
    /**
     * mqtt发送者url
     */
    @Value("${spring.mqtt.send.url}")
    private String hostUrl;
    /**
     * mqtt发送者客户端id
     */
    @Value("${spring.mqtt.send.clientId}")
    private String clientId;
    /**
     * mqtt发送者主题
     */
    @Value("${spring.mqtt.send.topic}")
    private String msgTopic;
    /**
     * mqtt发送者主题
     */
    @Value("${spring.mqtt.receive.topic}")
    private String msgReceiveTopic;
    /**
     * mqtt发送者超时时间
     */
    @Value("${spring.mqtt.send.completionTimeout}")
    private int completionTimeout;
    /**
     * @author liujianfu
     * @description
     */
    @Value("${spring.mqtt.send.keepAliveInterval}")
    private int keepAliveInterval;
    /**
     * @author liujianfu
     * @description
     */
    @Value("${spring.mqtt.send.connectionTimeout}")
    private int connectionTimeout;

    @Autowired
    private MqttCallbackHandler mqttCallbackHandler;


    /**
     * @param
     * @return org.eclipse.paho.client.mqttv3.MqttConnectOptions
     * @author liujianfu
     * @description 新建MqttConnectionOptionsBean  MQTT连接器选项
     * @date 2021/8/17 10:34
     */
    @Bean
    public MqttConnectOptions getSenderMqttConnectOptions() {
        MqttConnectOptions options = new MqttConnectOptions();
        // 设置连接的用户名
        if (!username.trim().equals("")) {
            //将用户名去掉前后空格
            options.setUserName(username);
        }
        // 设置连接的密码
        options.setPassword(password.toCharArray());
        // 转化连接的url地址
        String[] uris = {hostUrl};
        // 设置连接的地址
        options.setServerURIs(uris);
        // 设置超时时间 单位为秒
        options.setConnectionTimeout(completionTimeout);
        // 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送心跳判断客户端是否在线
        // 但这个方法并没有重连的机制
        options.setKeepAliveInterval(keepAliveInterval);
        // 设置“遗嘱”消息的话题,若客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息。
        //设置超时时间
        options.setConnectionTimeout(connectionTimeout);
        options.setCleanSession(true);
        options.setAutomaticReconnect(true);
        return options;
    }

    /**
     * 创建MqttPathClientFactoryBean
     */
    @Bean
    public MqttPahoClientFactory senderMqttClientFactory() {
        //创建mqtt客户端工厂
        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
        //设置mqtt的连接设置
        factory.setConnectionOptions(getSenderMqttConnectOptions());
        return factory;
    }

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


    /**
     * 发布者-MQTT消息处理器(生产者)  将channel绑定到MqttClientFactory上
     *
     * @return {@link org.springframework.messaging.MessageHandler}
     */
    @Bean
    @ServiceActivator(inputChannel = CHANNEL_NAME_OUT)
    public MessageHandler mqttOutbound() {
        //创建消息处理器
        MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(
                clientId + "_pub",
                senderMqttClientFactory());
        //设置消息处理类型为异步
        messageHandler.setAsync(true);
        //设置消息的默认主题
        messageHandler.setDefaultTopic(msgTopic);
        messageHandler.setDefaultRetained(false);
        //1.重新连接MQTT服务时,不需要接收该主题最新消息,设置retained为false;
        //2.重新连接MQTT服务时,需要接收该主题最新消息,设置retained为true;
        return messageHandler;
    }


    /************                             消费者,订阅者的消费信息                               *****/

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

    /**
     * MQTT消息订阅绑定(消费者)
     */
    @Bean
    public MessageProducer inbound() {
        // System.out.println("topics:" + msgTopic);
        // 可以同时消费(订阅)多个Topic
        MqttPahoMessageDrivenChannelAdapter adapter =
                new MqttPahoMessageDrivenChannelAdapter(
                        clientId + "_sub", senderMqttClientFactory(), msgReceiveTopic.split(","));
        adapter.setCompletionTimeout(5000);
        adapter.setConverter(new DefaultPahoMessageConverter());
        adapter.setQos(0);
        // 设置订阅通道
        adapter.setOutputChannel(mqttInboundChannel());
        return adapter;
    }

    /**
     * MQTT消息处理器(消费者)
     */
    @Bean
    @ServiceActivator(inputChannel = CHANNEL_NAME_IN)
    public MessageHandler handler() {
        return message -> {
            String topic = message.getHeaders().get("mqtt_receivedTopic").toString();
            String payload = message.getPayload().toString();
            mqttCallbackHandler.handle(topic, payload);
        };
    }
}

MqttCallbackHandler:消费topic 处理类:

@Service
public class MqttCallbackHandler {
    public void handle(String topic, String payload) {
        // 根据topic分别进行消息处理。
        System.out.println("MqttCallbackHandle:" + topic + "|" + payload);
    }
}

发送消息接口MqSendMessageGateWay:

package com.mmy.springmmyservice.config;

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

/**
 * @Description TODO
 * @Date 2022/12/20 10:15
 * @Author lgx
 * @Version 1.0
 */
@Component
@MessagingGateway(defaultRequestChannel = MqttConfig.CHANNEL_NAME_OUT)
public interface  MqSendMessageGateWay {
    /**
     * 默认的消息机制
     *
     * @param data
     */
    void sendToMqtt(String data);

    /**
     * 发送消息 向mqtt指定topic发送消息
     *
     * @param topic
     * @param payload
     */
    void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, String payload);

    /**
     * 发送消息 向mqtt指定topic发送消息
     *
     * @param topic
     * @param qos
     * @param payload
     */
    void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload);
}

控制器发送消息测试MqttController:

package com.mmy.springmmyservice.modules.pagerecord.controller;

import com.alibaba.fastjson2.JSONObject;
import com.mmy.springmmyservice.common.CommonResponseBody;
import com.mmy.springmmyservice.config.MqSendMessageGateWay;
import com.mmy.springmmyservice.modules.pagerecord.dto.MqttSendDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

/**
 * @Description TODO
 * @Date 2022/12/20 10:18
 * @Author lgx
 * @Version 1.0
 */
@RestController
@CommonResponseBody
public class MqttController {
    @Autowired
    private MqSendMessageGateWay mqSendMessageGateWay;

    @RequestMapping("/send")
    private ResponseEntity<String> send() {
        String data = "我是springboot发送的数据";
        mqSendMessageGateWay.sendToMqtt(data);
        // return R.ok("OK");
        return new ResponseEntity<>("OK", HttpStatus.OK);
    }

    /**
     * 动态增加主题
     *
     * @param
     * @param
     */
    @RequestMapping("/sendToTopic")
    private ResponseEntity<String> sendToTopic() {
        String topic = "sharjeck/ai/test/out";
        String data = "这是出的主题";
        mqSendMessageGateWay.sendToMqtt(topic, data);
        return new ResponseEntity<>("OK", HttpStatus.OK);
    }

    /**
     * 动态增加主题
     *
     * @param
     * @param
     */
    @PostMapping("/sendToTopic")
    private String sendToTopic(@RequestBody MqttSendDto reqDto) {
        mqSendMessageGateWay.sendToMqtt(reqDto.getTopic(), JSONObject.toJSONString(reqDto.getJsonStr()));
        return "OK";
    }
}

3 总结:
3.1 mqtt 和http ,tcp,udp,一样只是传输协议使用sdk 即可,并不像kafka ,rabbitmq 一样是消息组件;
3.2 场景不同 kafka ,rabbitmq 面向服务端的消息引擎,主要用于服务组件之间的解耦、异步通知、削峰填谷等,服务器规模较小(极少企业服务器规模过万),但需要大量的消息处理,吞吐量要求高;mqtt 面向移动端场景,移动端场景一般都具备海量设备,单设备数据较少的特点。因此,微消息队列 MQTT 版适用于拥有大量在线客户端(很多企业设备端过万,甚至上百万),但每个客户端消息较少的场景;
3.3 对于部署在服务器上的应用,推荐使用消息队列 RocketMQ 版接入;对于部署在移动终端、App 或浏览器页面等平台上的应用,推荐使用微消息队列 MQTT 版接入。

参考:
1 MQTT Support;
2 springBoot整合mosquitto MQTT服务接收和发送;
3 微消息队列MQTT与RocketMQ/Kafka/RabbitMQ区别;

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值