SpringBoot中使用Spring integration加Eclipse Paho Java Client 实现MQTT客户端

Spring Integration 是一个开源的集成消息处理框架,它提供了消息传递、消息过滤、消息转换、消息路由等功能,可以用于构建异步、分布式的系统。
Spring-integration-stream是Spring Integration框架的一个组件,用于在不同的系统和应用之间进行消息传递、集成和流处理。

它提供了一套流式编程模型,允许开发者通过定义输入源(Source)、处理器(Processor)和输出目标(Sink)来构建消息流。开发者可以使用不同的数据转换器和处理器来操纵和转换消息,在消息流中实现各种类型的操作和转换。

Spring-integration-stream还提供了一些用于连接外部系统和应用的适配器(Adapters),如JMS、HTTP、AMQP等,以便与不同的中间件和协议进行交互。开发者可以轻松地将spring-integration-stream与其他Spring Integration组件结合使用,构建更加复杂和灵活的集成解决方案。
Spring Integration MQTT 是一个基于 Spring Framework 的集成 MQTT 协议的消息通信模块,它提供了构建 MQTT 集成应用的工具和组件。通过 Spring Integration,你可以轻松地实现 MQTT 消息的发送、接收和处理。

Eclipse Paho Java Client 客户端是 Java 语言中使用最为广泛的 MQTT 客户端库。

1.连接MQTT 3.1.1协议代理服务器

1.1 添加maven依赖

添加以下依赖到项目 pom.xml 文件中

<!--集成MQTT-->
		<dependency>
			<groupId>org.springframework.integration</groupId>
			<artifactId>spring-integration-mqtt</artifactId>
		</dependency>
		<!--开启流支持-->
		<dependency>
			<groupId>org.springframework.integration</groupId>
			<artifactId>spring-integration-stream</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-integration</artifactId>
		</dependency>

1.2 添加配置类,配置入站订阅消息,出站发布消息


import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
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.event.MqttConnectionFailedEvent;
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.messaging.MessageHandler;

import javax.annotation.Resource;

/**
 * mqtt消息处理配置
 */
@Configuration
@Slf4j
public class MqttConfig {

    /**
     * 服务器地址以及端口
     */
    @Value("${mqtt.services}")
    private String[] mqttServices;

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

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

    /**
     * 心跳时间,默认为5分钟
     */
    @Value("${mqtt.KeepAliveInterval}")
    private Integer KeepAliveInterval;

    /**
     * 通信质量,详见MQTT协议
     */
    @Value("${mqtt.Qos:1}")
    private Integer Qos;
    /**
     * 是否不保持session,默认为session保持
     */
    @Value("${mqtt.CleanSession:false}")
    private Boolean CleanSession;

    /**
     * 是否自动重联,默认为开启自动重联
     */
    @Value("${mqtt.AutomaticReconnect:true}")
    private Boolean AutomaticReconnect;

    /**
     * 消费超时时间
     */
    @Value("${mqtt.CompletionTimeout:5000}")
    private Long completionTimeout;

    /**
     * 连接超时,默认为30秒
     */
    @Value("${mqtt.connectionTimeout}")
    private int  mqttConnectionTimeout;

    /**
     * 恢复间隔
     */
    @Value("${mqtt.recoveryInterval}")
    private int mqttRecoveryInterval;


    @Value("${mqtt.maxinflight:1000}")
    private int maxinflight;


    /**
     * //订阅主题,可以是多个主题
     */
    @Value("${mqtt.input.topic}")
    private String[] inputTopic;

    @Resource
    private MqttMessageReceiver mqttMessageReceiver;

    /**
     * 生成配置对象,用户名,密码等
     */
    public MqttConnectOptions getOptions() {
        //MQTT连接器选项
        MqttConnectOptions options = new MqttConnectOptions();
        // 设置代理端的URL地址,可以是多个
        options.setServerURIs(mqttServices);
        // 配置mqtt服务端地址,登录账号和密码
        options.setUserName(user);
        options.setPassword(password.toCharArray());
        // 配置 最大传输中数,默认值10,qos!=0 时生效
        //表示允许同时在传输中的最大消息数量。
        // MQTT 协议规定,在未收到 ACK 确认之前,客户端只能同时传输一定数量的消息。
        // MaxInflight 指标用来控制该数量,以避免网络拥塞
        options.setMaxInflight(maxinflight);
        //心跳时间
        // 设置会话心跳时间 单位为秒 服务器会每隔KeepAliveInterval秒的时间向客户端发送心跳判断客户端是否在线,
        options.setKeepAliveInterval(KeepAliveInterval);
        //断开是否自动重联
        options.setAutomaticReconnect(AutomaticReconnect);
        // 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,
        // 这里设置为true表示每次连接到服务器都以新的身份连接
        options.setCleanSession(CleanSession);
        options.setConnectionTimeout(mqttConnectionTimeout);
        // 设置“遗嘱”消息的话题,若客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息。
        //options.setWill("willTopic", WILL_DATA, 1, true);
        log.info("----生成mqtt配置对象----");
        return options;
    }


    /**
     * MQTT客户端
     * 配置DefaultMqttPahoClientFactory
     * 1. 配置基本的链接信息
     * 2. 配置maxInflight,在mqtt消息量比较大的情况下将值设大
     */
    @Bean
    public MqttPahoClientFactory mqttClientFactory() {
        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
        factory.setConnectionOptions(getOptions());
        return factory;
    }


    /**
     * MQTT消息处理器(生产者)
     * mqtt消息出站通道默认配置,用于向外发出mqtt消息,当前案例中发到了同一个mqtt服务器
     *
     * @return
     */
    @Bean
    @ServiceActivator(inputChannel = "mqttOutputChannel")
    public MessageHandler mqttOutbound(MqttPahoClientFactory factory) {
        String clientId = BaseConstants.MQTT_OUTBOUND + IdUtil.simpleUUID();
        log.info("MQTT消息处理器(生产者)clientId:{}", clientId);
        // 发送消息和消费消息Channel可以使用相同MqttPahoClientFactory
        MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(clientId, factory);
        // 如果设置成true,即异步,发送消息时将不会阻塞。
        messageHandler.setAsync(true);
        // 设置默认QoS
        messageHandler.setDefaultQos(Qos);
        // Paho消息转换器
        DefaultPahoMessageConverter defaultPahoMessageConverter = new DefaultPahoMessageConverter();
        // defaultPahoMessageConverter.setPayloadAsBytes(true); // 发送默认按字节类型发送消息
        messageHandler.setConverter(defaultPahoMessageConverter);
        return messageHandler;
    }

    /**
     * MQTT信息通道(生产者)
     * mqtt消息出站通道,用于发送出站消息
     *
     * @return
     */
    @Bean
    public MessageChannel mqttOutputChannel() {
        return new DirectChannel();
    }

    /**
     * MQTT信息通道(消费者)
     * mqtt消息入站通道,订阅消息后消息进入的通道。
     * 可创建多个入站通道,对应多个不同的消息生产者。
     *
     * @return
     */
    @Bean
    public MessageChannel mqttInputChannel() {
        return new DirectChannel();
    }


    /**
     * MQTT消息订阅绑定(消费者)
     * 对于当前应用来讲,接收的mqtt消息的生产者。将生产者绑定到mqtt入站通道,即通过入站通道进入的消息为生产者生产的消息。
     * 可创建多个消息生产者,对应多个不同的消息入站通道,同时生产者监听不同的topic
     *
     * @return
     */
    @Bean
    public MessageProducer channelInbound(MessageChannel mqttInputChannel, MqttPahoClientFactory factory) {
        String clientId = BaseConstants.MQTT_INBOUND + IdUtil.simpleUUID();
        log.info("MQTT消息处理器(消费者)clientId:{}", clientId);
        MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(clientId, factory, inputTopic);
        adapter.setCompletionTimeout(completionTimeout);
        adapter.setRecoveryInterval(mqttRecoveryInterval);
        adapter.setConverter(new DefaultPahoMessageConverter());
        adapter.setQos(Qos);
        adapter.setOutputChannel(mqttInputChannel);
        return adapter;
    }

    /**
     * MQTT消息处理器(消费者)
     * mqtt入站消息处理工具,对于指定消息入站通道接收到生产者生产的消息后处理消息的工具。
     *
     * @return
     */
    @Bean
    @ServiceActivator(inputChannel = "mqttInputChannel")
    public MessageHandler mqttMessageHandler() {
        return this.mqttMessageReceiver;
    }

    @Bean
    public ApplicationListener<?> eventListener() {
        return new ApplicationListener<MqttConnectionFailedEvent>() {
            @Override
            public void onApplicationEvent(MqttConnectionFailedEvent event) {
                log.info("MqttConnection异常:", event.getCause());
            }
        };
    }
}



1.3 消息发送器

@Component
@MessagingGateway(defaultRequestChannel = "mqttOutputChannel")
public interface MqttGateway {
    /**
     * 定义重载方法,用于消息发送
     * @param  payload
     */
    void sendToMqtt(String payload);

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

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

    /**
     * 指定topic和消息保留时间进行消息发送
     * @param topic
     * @param qos
     * @param messageExpiryInterval
     * @param payload
     */
    void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos,@Header(MqttHeaders.MESSAGE_EXPIRY_INTERVAL) int messageExpiryInterval, String payload);


    /**
     * 指定topic进行消息发送
     * @param topic
     * @param qos
     * @param payload
     */
    void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, byte[] payload);
}

1.4 消息接收器

@Component
@Slf4j
public class MqttMessageReceiver implements MessageHandler {

    @Value("${mqtt.client.message.expiry.interval}")
    private Long messageExpiryInterval;


    @Override
    public void handleMessage(Message<?> message) throws MessagingException {
        log.info("接收到mqtt消息{}", message);
        String topic = String.valueOf(message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC));
        String payload = String.valueOf(message.getPayload());
    }
}



1.5 配置文件

#订阅主题,多个主题用逗号分隔
mqtt.input.topic=test/client
#MQTT服务器地址,可以是多个地址
mqtt.services=tcp://127.0.0.0:1883
#mqtt用户名,默认无
mqtt.user=xx
#mqtt密码,默认无
mqtt.password=xx
#心跳间隔时间,默认60
mqtt.KeepAliveInterval=60
#连接超时时间30
mqtt.connectionTimeout=30
#是否不保持session,默认false
mqtt.CleanSession=false
#是否自动连接,默认true
mqtt.AutomaticReconnect=true
mqtt.maxinflight=1000
#操作的完成超时时间
mqtt.CompletionTimeout=30000
mqtt.recoveryInterval=10000
#传输质量,默认1
mqtt.Qos=1

2.连接MQTT 5.0协议代理服务器

使用单向认证连接MQTT 5.0协议代理服务器,设置了断线自动重连,连接MQTT 5.0协议代理服务器

2.1 添加maven依赖

添加以下依赖到项目 pom.xml 文件中

   <!--集成MQTT-->
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-mqtt</artifactId>
            <version>5.5.14</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.paho</groupId>
            <artifactId>org.eclipse.paho.mqttv5.client</artifactId>
            <version>1.2.5</version>
            <scope>compile</scope>
            <optional>true</optional>
        </dependency>
        <!--开启流支持-->
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-stream</artifactId>
            <version>5.5.14</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-core</artifactId>
            <version>5.5.14</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-integration</artifactId>
        </dependency>

2.2 添加配置类,配置入站订阅消息,出站发布消息

import cn.hutool.core.util.IdUtil;
import com.xx.message.util.SSLUtils;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.mqttv5.client.MqttConnectionOptions;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
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.event.MqttConnectionFailedEvent;
import org.springframework.integration.mqtt.event.MqttSubscribedEvent;
import org.springframework.integration.mqtt.outbound.Mqttv5PahoMessageHandler;
import org.springframework.integration.mqtt.support.MqttHeaderMapper;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;

import javax.annotation.Resource;

/**
 * mqtt消息处理配置
 *
 */
@Configuration
@Slf4j
public class MqttConfig {

    /**
     * 服务器地址以及端口
     */
    @Value("${mqtt.services}")
    private String[] mqttServices;

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

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

    /**
     * 心跳时间,默认为5分钟
     */
    @Value("${mqtt.KeepAliveInterval}")
    private Integer KeepAliveInterval;

    /**
     * 通信质量,详见MQTT协议
     */
    @Value("${mqtt.Qos:1}")
    private Integer Qos;
    /**
     * 是否不保持session,默认为session保持
     */
    @Value("${mqtt.CleanSession:false}")
    private Boolean CleanSession;

    /**
     * 是否自动重联,默认为开启自动重联
     */
    @Value("${mqtt.AutomaticReconnect:true}")
    private Boolean AutomaticReconnect;

    /**
     * 消费超时时间
     */
    @Value("${mqtt.CompletionTimeout:5000}")
    private Long completionTimeout;

    /**
     * 连接超时,默认为30秒
     */
    @Value("${mqtt.connectionTimeout}")
    private int mqttConnectionTimeout;

    /**
     * 恢复间隔
     */
    @Value("${mqtt.recoveryInterval:5000}")
    private int mqttRecoveryInterval;

    @Value("${mqtt.automaticReconnectMinDelay:5}")
    private int mqttAutomaticReconnectMinDelay;

    @Value("${mqtt.automaticReconnectMaxDelay:30}")
    private int mqttAutomaticReconnectMaxDelay;

    @Value("${mqtt.maxReconnectDelay:40}")
    private int mqttMaxReconnectDelay;

    @Value("${mqtt.maxinflight:1000}")
    private int maxinflight;

    @Value("${mqtt.sessionExpiryInterval:172800}")
    private Long sessionExpiryInterval;

    /**
     * 订阅主题,可以是多个主题
     */
    @Value("${mqtt.input.topic}")
    private String[] inputTopic;


    @Resource
    private MqttMessageReceiver mqttMessageReceiver;

    /**
     * 生成配置对象,用户名,密码等
     */
    public MqttConnectionOptions getOptions() {
        //MQTT连接器选项
        MqttConnectionOptions options = new MqttConnectionOptions();
        // 设置代理端的URL地址,可以是多个
        options.setServerURIs(mqttServices);
        // 配置mqtt服务端地址,登录账号和密码
        options.setUserName(user);
        options.setPassword(password.getBytes());
        // 配置 最大传输中数,默认值10,qos!=0 时生效
        //表示允许同时在传输中的最大消息数量。
        // MQTT 协议规定,在未收到 ACK 确认之前,客户端只能同时传输一定数量的消息。
        // MaxInflight 指标用来控制该数量,以避免网络拥塞
        options.setReceiveMaximum(maxinflight);
        options.setAutomaticReconnectDelay(mqttAutomaticReconnectMinDelay, mqttAutomaticReconnectMaxDelay);
        options.setMaxReconnectDelay(mqttMaxReconnectDelay);
        //心跳时间
        // 设置会话心跳时间 单位为秒 服务器会每隔KeepAliveInterval秒的时间向客户端发送心跳判断客户端是否在线,
        options.setKeepAliveInterval(KeepAliveInterval);
        //断开是否自动重联
        options.setAutomaticReconnect(AutomaticReconnect);
        // 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,
        // 这里设置为true表示每次连接到服务器都以新的身份连接
        options.setCleanStart(false);
        options.setSessionExpiryInterval(sessionExpiryInterval);
        options.setConnectionTimeout(mqttConnectionTimeout);
        try {
            options.setSocketFactory(SSLUtils.getSingleSocketFactory("ca.crt"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.info("----生成mqtt配置对象----");
        return options;
    }




    /**
     * MQTT消息处理器(生产者)
     * mqtt消息出站通道默认配置,用于向外发出mqtt消息,当前案例中发到了同一个mqtt服务器
     *
     * @return
     */
    @Bean
    @ServiceActivator(inputChannel = "mqttOutputChannel")
    public MessageHandler mqttOutbound() {
        String clientId = BaseConstants.MQTT_OUTBOUND + IdUtil.simpleUUID();
        log.info("MQTT消息处理器(生产者)clientId:{}", clientId);
        // 发送消息和消费消息Channel可以使用相同MqttPahoClientFactory
        MqttHeaderMapper mqttHeaderMapper = new MqttHeaderMapper();
        Mqttv5PahoMessageHandler messageHandler = new Mqttv5PahoMessageHandler(getOptions(), clientId);
        messageHandler.setHeaderMapper(mqttHeaderMapper);
        // 如果设置成true,即异步,发送消息时将不会阻塞。
        messageHandler.setAsync(false);
        // 设置默认QoS
        messageHandler.setDefaultQos(Qos);
        return messageHandler;
    }

    /**
     * MQTT信息通道(生产者)
     * mqtt消息出站通道,用于发送出站消息
     *
     * @return
     */
    @Bean
    public MessageChannel mqttOutputChannel() {
        return new DirectChannel();
    }

    /**
     * MQTT信息通道(消费者)
     * mqtt消息入站通道,订阅消息后消息进入的通道。
     * 可创建多个入站通道,对应多个不同的消息生产者。
     *
     * @return
     */
    @Bean
    public MessageChannel mqttInputChannel() {
        return new DirectChannel();
    }


    /**
     * MQTT消息订阅绑定(消费者)
     * 对于当前应用来讲,接收的mqtt消息的生产者。将生产者绑定到mqtt入站通道,即通过入站通道进入的消息为生产者生产的消息。
     * 可创建多个消息生产者,对应多个不同的消息入站通道,同时生产者监听不同的topic
     *
     * @return
     */
    @Bean
    public MessageProducer channelInbound(MessageChannel mqttInputChannel) {
        String clientId = BaseConstants.MQTT_INBOUND + IdUtil.simpleUUID();
        log.info("MQTT消息处理器(消费者)clientId:{}", clientId);
        MyMqttv5PahoMessageDrivenChannelAdapter adapter = new MyMqttv5PahoMessageDrivenChannelAdapter (getOptions(), clientId, inputTopic);
        adapter.setCompletionTimeout(completionTimeout);
        adapter.setPayloadType(String.class);
        adapter.setQos(Qos);
        adapter.setOutputChannel(mqttInputChannel);
        return adapter;
    }

    /**
     * MQTT消息处理器(消费者)
     * mqtt入站消息处理工具,对于指定消息入站通道接收到生产者生产的消息后处理消息的工具。
     *
     * @return
     */
    @Bean
    @ServiceActivator(inputChannel = "mqttInputChannel")
    public MessageHandler mqttMessageHandler() {
        return this.mqttMessageReceiver;
    }

    @Bean
    public ApplicationListener<?> eventListener() {
        return new ApplicationListener<MqttConnectionFailedEvent>() {
            @Override
            public void onApplicationEvent(MqttConnectionFailedEvent event) {
                log.info("MqttConnection异常:", event.getCause());
            }
        };
    }

    @Bean
    public ApplicationListener<?> mqttSubscribedEventListener() {
        return new ApplicationListener<MqttSubscribedEvent>() {
            @Override
            public void onApplicationEvent(MqttSubscribedEvent event) {
                log.info("连接完成做订阅动作:{}", event.getMessage());
            }
        };
    }
}

SSLUtils

 // 单向认证
    public static SSLSocketFactory getSingleSocketFactory(final String caCrtFile) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        X509Certificate caCert = null;
        Resource resource = new ClassPathResource(caCrtFile);
        BufferedInputStream bis = new BufferedInputStream(resource.getInputStream());
        CertificateFactory cf = CertificateFactory.getInstance("X.509");

        while (bis.available() > 0) {
            caCert = (X509Certificate) cf.generateCertificate(bis);
        }
        KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
        caKs.load(null, null);
        caKs.setCertificateEntry("cert-certificate", caCert);
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(caKs);
        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(null, tmf.getTrustManagers(), null);
        return sslContext.getSocketFactory();
    }

    public static SSLSocketFactory getSocketFactory(final String caCrtFile, final String crtFile, final String keyFile,
                                                    final String password) throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        // load CA certificate
        PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile)))));
        X509Certificate caCert = (X509Certificate) reader.readObject();
        reader.close();

        // load client certificate
        reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile)))));
        X509Certificate cert = (X509Certificate) reader.readObject();
        reader.close();

        // load client private key
        reader = new PEMReader(
                new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFile)))),
                new PasswordFinder() {
                    @Override
                    public char[] getPassword() {
                        return password.toCharArray();
                    }
                }
        );
        // CA certificate is used to authenticate server
        KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
        caKs.load(null, null);
        caKs.setCertificateEntry("ca-certificate", caCert);
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(caKs);

        // client key and certificates are sent to server so it can authenticate us
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(null, null);
        ks.setCertificateEntry("certificate", cert);
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(ks, password.toCharArray());

        // finally, create SSL socket factory
        SSLContext context = SSLContext.getInstance("TLSv1.2");
        context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

        return context.getSocketFactory();
    }
}

解决重连后不重新订阅的问题

/**
 * @Desc   解决重连后不重新订阅的问题
 * @create 2023-08-31 16:07
 **/
@Slf4j
public class MyMqttv5PahoMessageDrivenChannelAdapter extends Mqttv5PahoMessageDrivenChannelAdapter {

    public MyMqttv5PahoMessageDrivenChannelAdapter (String url, String clientId, String... topic) {
        super(url, clientId, topic);
    }

    public MyMqttv5PahoMessageDrivenChannelAdapter (MqttConnectionOptions connectionOptions, String clientId, String... topic) {
        super(connectionOptions, clientId, topic);
    }

    @Override
    public void connectComplete(boolean reconnect, String serverURI) {
        if (reconnect) {
            log.info("重连需要重新订阅");
            reconnect = false;
        }
        super.connectComplete(reconnect, serverURI);
    }


}

2.3 消息发送器

@Component
@MessagingGateway(defaultRequestChannel = "mqttOutputChannel")
public interface MqttGateway {
    /**
     * 定义重载方法,用于消息发送
     * @param  payload
     */
    void sendToMqtt(String payload);

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

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

    /**
     * 指定topic和消息保留时间进行消息发送
     * @param topic
     * @param qos
     * @param messageExpiryInterval
     * @param payload
     */
    void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload,@Header(MqttHeaders.MESSAGE_EXPIRY_INTERVAL) Long messageExpiryInterval);


    /**
     * 指定topic进行消息发送
     * @param topic
     * @param qos
     * @param payload
     */
    void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, byte[] payload);
}


2.4 消息接收器

@Component
@Slf4j
public class MqttMessageReceiver implements MessageHandler {

    @Value("${mqtt.client.message.expiry.interval}")
    private Long messageExpiryInterval;


    @Override
    public void handleMessage(Message<?> message) throws MessagingException {
        log.info("接收到mqtt消息{}", message);
        String topic = String.valueOf(message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC));
        String payload = String.valueOf(message.getPayload());
    }
}



2.5 配置文件

#订阅主题,多个主题用逗号分隔
mqtt.input.topic=$share/zm/test/client
#MQTT服务器地址,可以是多个地址
mqtt.services=ssl://127.0.0:8883
#mqtt用户名,默认无
mqtt.user=xx
#mqtt密码,默认无
mqtt.password=xx
#心跳间隔时间,默认60
mqtt.KeepAliveInterval=60
#连接超时时间30
mqtt.connectionTimeout=30
#是否不保持session,默认false
mqtt.CleanSession=false
#是否自动连接,默认true
mqtt.AutomaticReconnect=true
mqtt.maxinflight=2000
#操作的完成超时时间
mqtt.CompletionTimeout=30000
#自动重连执行时使用的最小和最大延迟-没用
mqtt.automaticReconnectMinDelay=5
mqtt.automaticReconnectMaxDelay=30
#自动重连执行时最大延迟
mqtt.maxReconnectDelay=60
#离线有效期要少于会话过期时间
mqtt.sessionExpiryInterval=172800
#传输质量,默认1
mqtt.Qos=1

2.6 ca证书

ca.crt

-----BEGIN CERTIFICATE-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-----END CERTIFICATE-----

你可能感兴趣的:(MQTT,spring,java,spring,boot,MQTT,MQTT5.0,MQTT单向认证)