MQTT使用

背景

MQTT协议是为大量计算能力有限,且工作在低带宽、不可靠的网络的远程传感器和控制设备通讯而设计的协议

使用阿里云提供的MQTT服务

详情可以参考阿里的api和使用例子

话不多说,上代码安排

1.pom文件

        <!--   mqtt     -->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.10</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.paho</groupId>
            <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
            <version>1.2.2</version>
        </dependency>

2.配置文件 application.yaml

mqtt:
  instanceId: post-cn-v561idsmm0e
  #endPoint: post-cn-xxxxxxxxx-internal.mqtt.aliyuncs.com  #内网
  endPoint: post-cn-xxxxxxxxx.mqtt.aliyuncs.com
  accessKey: aaaaaaaaaaaaaaaaaaaaaaaaa
  secretKey: aaaaaaaaaaaaaaaaaaaaaaaaaa
  groupId: GID_java
  deviceIdNum: 5
  maxIntervalTime: 500
  maxConnect: 100
  qosLevel: 0

3.配置类

说一下遇到的坑

mqttClient.setTimeToWait(60000); 

这个要改,因为当时我是放容器里跑的,放外面没事,放容器里不行,发现是超时造成的,时间不能太短也不能太长,毕竟连接要网络不是吗

import com.gatechat.util.ConnectionOptionWrapper;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.*;

@Configuration
@Slf4j
public class MQTTConfig {

    /**
     * MQ4IOT 实例 ID,购买后控制台获取
     */
    @Value("${mqtt.instanceId}")
    private String instanceId;
    /**
     * 接入点地址,购买 MQ4IOT 实例,且配置完成后即可获取,接入点地址必须填写分配的域名,不得使用 IP 地址直接连接,否则可能会导致客户端异常。
     */
    @Value("${mqtt.endPoint}")
    private String endPoint;
    /**
     * 账号 accesskey,从账号系统控制台获取
     */
    @Value("${mqtt.accessKey}")
    private String accessKey;
    /**
     * 账号 secretKey,从账号系统控制台获取,仅在Signature鉴权模式下需要设置
     */
    @Value("${mqtt.secretKey}")
    private String secretKey;
    /**
     * MQ4IOT clientId,由业务系统分配,需要保证每个 tcp 连接都不一样,保证全局唯一,如果不同的客户端对象(tcp 连接)使用了相同的 clientId 会导致连接异常断开。
     * clientId 由两部分组成,格式为 GroupID@@@DeviceId,其中 groupId 在 MQ4IOT 控制台申请,DeviceId 由业务方自己设置,clientId 总长度不得超过64个字符。
     */
    @Value("${mqtt.groupId}")
    private String groupId;
    @Value("${mqtt.deviceIdNum}")
    private int deviceIdNum;


    @Bean(name = "clientIdList")
    public List<String> deviceIdList() throws Exception {
        //String mac = SyStemMacUtil.getMac();
        //mac = "FC-45-96-A1-92-F3";
        //mac = mac.replaceAll("-", "");
        String code = couponCode();
        List<String> clientIdList = new ArrayList<>();
        for (int i = 0 ; i < deviceIdNum ; i++) {
            String device = "java" + code + (i + 10);
            clientIdList.add(groupId + "@@@" + device);
        }
        return clientIdList;
    }


    @Bean(name = "mqttClientList")
    public ArrayList<MqttClient> mqttClientList(@Qualifier("clientIdList") List<String> clientIdList) throws Exception {
        ArrayList<MqttClient> mqttClientList = new ArrayList<>();
        for (String clientId : clientIdList) {

            ConnectionOptionWrapper connectionOptionWrapper = new ConnectionOptionWrapper(instanceId, accessKey, secretKey, clientId);
            MemoryPersistence memoryPersistence = new MemoryPersistence();
            MqttClient mqttClient = new MqttClient("tcp://" + endPoint + ":1883", clientId, memoryPersistence);
//            MqttClient mqttClient = new MqttClient("tcp://112.65.79.208:1883", clientId, memoryPersistence);
            // 客户端设置好发送超时时间,防止无限阻塞
            mqttClient.setTimeToWait(60000);
            mqttClient.setCallback(new MqttCallbackExtended() {
                @Override
                public void connectComplete(boolean reconnect, String serverURI) {
                    /**
                     * 客户端连接成功后就需要尽快订阅需要的 topic
                     */
//                    log.info(serverURI + "-------------------connect success");
                }

                @Override
                public void connectionLost(Throwable throwable) {
                    throwable.printStackTrace();
                }

                @Override
                public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
                    /**
                     * 消费消息的回调接口,需要确保该接口不抛异常,该接口运行返回即代表消息消费成功。
                     * 消费消息需要保证在规定时间内完成,如果消费耗时超过服务端约定的超时时间,对于可靠传输的模式,服务端可能会重试推送,
                     * 业务需要做好幂等去重处理。超时时间约定参考限制
                     * https://help.aliyun.com/document_detail/63620.html?spm=a2c4g.11186623.6.546.229f1f6ago55Fj
                     */
//                    log.info("receive msg from topic " + s + " , body is " + new String(mqttMessage.getPayload()));
                }

                @Override
                public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
//                    log.info("send msg succeed topic is : " + iMqttDeliveryToken.getTopics()[0]);
                }
            });
            mqttClient.connect(connectionOptionWrapper.getMqttConnectOptions());
            mqttClientList.add(mqttClient);
        }

        log.info("需创建当前MQTT连接数  :  " + deviceIdNum);
        log.info("当前已创建MQTT连接数 : " + mqttClientList.size());
        return mqttClientList;
    }

    public String couponCode(){
        String s = UUID.randomUUID().toString();
        s = s.replaceAll("-","");
        return s.substring(3, 13);
    }


}

配置类里有个对象ConnectionOptionWrapper 需要我们写一下

import org.eclipse.paho.client.mqttv3.MqttConnectOptions;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import static org.eclipse.paho.client.mqttv3.MqttConnectOptions.MQTT_VERSION_3_1_1;

/**
 * 工具类:负责封装 MQ4IOT 客户端的初始化参数设置
 */
public class ConnectionOptionWrapper {
    /**
     * 内部连接参数
     */
    private MqttConnectOptions mqttConnectOptions;
    /**
     * MQ4IOT 实例 ID,购买后控制台获取
     */
    private String instanceId;
    /**
     * 账号 accesskey,从账号系统控制台获取
     */
    private String accessKey;
    /**
     * 账号 secretKey,从账号系统控制台获取,仅在Signature鉴权模式下需要设置
     */
    private String secretKey;
    /**
     * MQ4IOT clientId,由业务系统分配,需要保证每个 tcp 连接都不一样,保证全局唯一,如果不同的客户端对象(tcp 连接)使用了相同的 clientId 会导致连接异常断开。
     * clientId 由两部分组成,格式为 GroupID@@@DeviceId,其中 groupId 在 MQ4IOT 控制台申请,DeviceId 由业务方自己设置,clientId 总长度不得超过64个字符。
     */
    private String clientId;

    /**
     * Signature 鉴权模式下构造方法
     *
     * @param instanceId MQ4IOT 实例 ID,购买后控制台获取
     * @param accessKey 账号 accesskey,从账号系统控制台获取
     * @param clientId MQ4IOT clientId,由业务系统分配
     * @param secretKey 账号 secretKey,从账号系统控制台获取
     */
    public ConnectionOptionWrapper(String instanceId, String accessKey, String secretKey,
                                   String clientId) throws NoSuchAlgorithmException, InvalidKeyException {
        this.instanceId = instanceId;
        this.accessKey = accessKey;
        this.secretKey = secretKey;
        this.clientId = clientId;
        mqttConnectOptions = new MqttConnectOptions();
        mqttConnectOptions.setUserName("Signature|" + accessKey + "|" + instanceId);
        mqttConnectOptions.setPassword(Tools.macSignature(clientId, secretKey).toCharArray());
        mqttConnectOptions.setCleanSession(true);
        mqttConnectOptions.setKeepAliveInterval(90);
        mqttConnectOptions.setAutomaticReconnect(true);
        mqttConnectOptions.setMqttVersion(MQTT_VERSION_3_1_1);
        mqttConnectOptions.setConnectionTimeout(5000);
    }

    public MqttConnectOptions getMqttConnectOptions() {
        return mqttConnectOptions;
    }

}

 

4.发送mqtt消息

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.ArrayList;

@Service
@Slf4j
public class MQTTService {
    @Autowired
    @Qualifier("mqttClientList")
    private ArrayList<MqttClient> mqttClientList;
    @Value("${mqtt.qosLevel}")
    private int qosLevel;
    private int index = 0;
    /**
     *
     * @param msg json格式的string
     */
    public void publish(String parentTopic, String msg, String deviceId){
        try {
            MqttClient client = mqttClientList.get(getIndex());
            MqttMessage message = new MqttMessage(msg.getBytes());
            message.setQos(qosLevel);
            /**
             *  发送普通消息时,topic 必须和接收方订阅的 topic 一致,或者符合通配符匹配规则
             */
            String topic = parentTopic + "/channel/";
            if(StringUtils.isNotBlank(deviceId)) {
                topic = topic + deviceId;
            }
            client.publish(topic, message);
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }

    private synchronized int getIndex() {
        index ++;
        if(index == mqttClientList.size()) {
            index = 0;
        }
        return index;
    }

}

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MQTT(Message Queuing Telemetry Transport)是一种轻量级的消息传输协议,常用于物联网设备之间的通信。以下是使用MQTT的基本步骤: 1. 安装MQTT客户端库:你可以使用Java、Python等语言的MQTT客户端库,例如Paho MQTT,Eclipse MQTT等。 2. 配置MQTT客户端:在客户端代码中,你需要设置MQTT代理的IP地址和端口号,以及订阅或发布的主题(topic)。 3. 连接到MQTT代理:使用MQTT客户端库,你可以创建一个MQTT客户端实例,并使用该实例连接到MQTT代理。 4. 发布消息:使用MQTT客户端库,你可以向MQTT代理发布消息。在发布消息时,你需要指定消息的主题和内容。 5. 订阅消息:使用MQTT客户端库,你可以订阅MQTT代理中的主题。当MQTT代理接收到与订阅的主题匹配的消息时,将会将该消息发送给订阅者。 以下是Python中使用Paho MQTT库实现发布和订阅MQTT消息的示例代码: ``` import paho.mqtt.client as mqtt # 连接到MQTT代理 client = mqtt.Client() client.connect("localhost", 1883) # 发布消息 client.publish("topic/test", "Hello, MQTT!") # 订阅消息 def on_message(client, userdata, msg): print(msg.topic + " " + str(msg.payload)) client.on_message = on_message client.subscribe("topic/test") client.loop_forever() ``` 该代码将会连接到localhost:1883的MQTT代理,发布一条主题为“topic/test”,内容为“Hello, MQTT!”的消息,并订阅“topic/test”主题。当有符合该主题的消息到达代理时,将会调用on_message函数并打印该消息的主题和内容。 希望这些信息能够对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值