.Springboot+mqtt物联网开发

1.开篇聊聊,本人之前从事通信软件开发,对高并发和限流等有一定的实战,不过最近做物联网项目,这里聊聊怎么用mqtt进行双向通信,后面大家对通信开发有兴趣的也可以关注下本人博客!!!

 

 

进入主题:

目前市面上有几种mqtt的开源项目,本人觉得emqtt这款开源的项目好用,是EMQTT,

Erlang MQTT消息服务器简称EMQTT。

EMQTT是采用Erlang语言开发,全面支持MQTT V3.1.1协议,支持集群和大规模连接的开源MQTT消息服务器。

EMQTT致力于发布一个基于Erlang/OTP语言平台,企业级稳定可靠,完全开源免费,可集群支持大规模物联网、移动互联网连接的MQTT消息服务器。

本人使用dcoker进行集成安装的在阿里云上面的:

如何安装自行百度!!!

------------------------------》》》》》

下面介绍下emqtt的视图化工具:Dashboard

.Springboot+mqtt物联网开发_第1张图片

 

2.好了,一些环境工作完后就进入到代码阶段了

进行borker,心跳等配置,springboot中的application.properties配置

sprig.mqtt.broker=安装的borker的ip+端口
spring.mqtt.clientId=xxxxx ------>>客户端 ,每一个borker上面的客户端id是唯一的
spring.mqtt.username=xxxxx
spring.mqtt.password=xxxxxx
spring.mqtt.timeout=2000
spring.mqtt.KeepAlive=20
spring.mqtt.topics=xxxx ------->>主题
spring.mqtt.qos=1 ------>>心跳包级别

(上面对这些参数不明白的,可自行百度)

1.进行mqtt项目启动:

-------------------------------------------------------------------

/**
 * Title: ApplicationRunnerImpl
 * Description:项目启动后执行mqtt
 *
 * @author YFW
 * @version V1.0
 * @date 2020-03-06
 */
@Component
@Slf4j
public class MqttApplicationRunner implements ApplicationRunner {
    @Autowired
    private MqttConfiguration mqttConfiguration;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        if (log.isInfoEnabled()){
            log.info("===============>>>Mqtt is run starting:<<==================");
        }
        MqttPushClient mqttPushClient = new MqttPushClient();
        mqttPushClient.connect(mqttConfiguration);
    }

    public static void main(String[] args) {
        System.out.println(MD5Utils.getMd5("abc123456"));
    }
}

------------------------------------------------------------------------------------------------------

 

2.进行MqttConfiguration 的配置;

.Springboot+mqtt物联网开发_第2张图片

3.MqttBean的配置:

--------------------------------------------------------

/**
 * Title: Mqttbean
 * Description: TODO
 *
 * @author YFW
 * @version V1.0
 * @date 2020-03-06
 */

@Component
public class Mqttbean {
    @Autowired
    private MqttConfiguration mqttConfiguration;
    @Bean("mqttPushClient")
    public MqttPushClient getMqttPushClient() {
        MqttPushClient mqttPushClient = new MqttPushClient();
        return mqttPushClient;
    }
}

-----------------------------------------------------------------------------------

4.MqttClient客户端代码段:

--------------------------------------------------------------

/**
 * Title: MqttPushClient
 * Description: TODO
 *
 * @author PJ-YFW
 * @version V1.0
 * @date 2020-03-06
 */
@Slf4j
public class MqttPushClient {
    private static MqttClient client;

    public static MqttClient getClient() {
        return client;
    }

    public static void setClient(MqttClient client) {
        MqttPushClient.client = client;
    }

    private MqttConnectOptions getOption(String userName, String password, int outTime, int KeepAlive) {
        //MQTT连接设置
        MqttConnectOptions option = new MqttConnectOptions();
        //设置是否清空session,false表示服务器会保留客户端的连接记录,true表示每次连接到服务器都以新的身份连接
        option.setCleanSession(false);
        //设置连接的用户名
        option.setUserName(userName);
        //设置连接的密码
        option.setPassword(password.toCharArray());
        //设置超时时间 单位为秒
        option.setConnectionTimeout(outTime);
        //设置会话心跳时间 单位为秒 服务器会每隔(1.5*keepTime)秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制
        option.setKeepAliveInterval(KeepAlive);
        //setWill方法,如果项目中需要知道客户端是否掉线可以调用该方法。设置最终端口的通知消息
        //option.setWill(topic, "close".getBytes(), 2, true);
        option.setMaxInflight(1000);
        return option;
    }

    /**
     * 连接
     */
    public void connect(MqttConfiguration mqttConfiguration) {
        MqttClient client;
        try {
            client = new MqttClient(mqttConfiguration.getHost(), mqttConfiguration.getClientId(), new MemoryPersistence());
            MqttConnectOptions options = getOption(mqttConfiguration.getUsername(), mqttConfiguration.getPassword(),
                    mqttConfiguration.getTimeout(), mqttConfiguration.getKeepAlive());
            MqttPushClient.setClient(client);
            try {
                client.setCallback(new PushCallback(this, mqttConfiguration));
                if (!client.isConnected()) {
                    client.connect(options);
                    log.info("================>>>MQTT连接成功<<======================");
                    //订阅主题
                    subscribe(mqttConfiguration.getTopic(), mqttConfiguration.getQos());
                } else {//这里的逻辑是如果连接不成功就重新连接
                    client.disconnect();
                    client.connect(options);
                    log.info("===================>>>MQTT断连成功<<<======================");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 断线重连
     *
     * @throws Exception
     */
    public Boolean reConnect() throws Exception {
        Boolean isConnected = false;
        if (null != client) {
            client.connect();
            if (client.isConnected()) {
                isConnected = true;
            }
        }
        return isConnected;
    }

    /**
     * 发布,默认qos为0,非持久化
     *
     * @param topic
     * @param pushMessage
     */
    public void publish(String topic, String pushMessage) {
        publish(0, false, topic, pushMessage);
    }

    /**
     * 发布
     *
     * @param qos
     * @param retained
     * @param topic
     * @param pushMessage
     */
    public void publish(int qos, boolean retained, String topic, String pushMessage) {
        MqttMessage message = new MqttMessage();
        message.setQos(qos);
        message.setRetained(retained);
        message.setPayload(pushMessage.getBytes());
        MqttTopic mTopic = MqttPushClient.getClient().getTopic(topic);
        if (null == mTopic) {
            log.error("===============>>>MQTT topic 不存在<<=======================");
        }
        MqttDeliveryToken token;
        try {
            token = mTopic.publish(message);
            token.waitForCompletion();
        } catch (MqttPersistenceException e) {
            e.printStackTrace();
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }

    /**
     * 发布消息的服务质量(推荐为:2-确保消息到达一次。0-至多一次到达;1-至少一次到达,可能重复),
     * retained 默认:false-非持久化(是指一条消息消费完,就会被删除;持久化,消费完,还会保存在服务器中,当新的订阅者出现,继续给新订阅者消费)
     *
     * @param topic
     * @param pushMessage
     */
    public void publish(int qos, String topic, String pushMessage) {
        publish(qos, false, topic, pushMessage);
    }

    /**
     * 订阅某个主题,qos默认为0
     * @param topic
     */
    //    public void subscribe(String[] topic){
    //        subscribe(topic,);
    //    }

    /**
     * 订阅某个主题
     *
     * @param topic
     * @param qos
     */
    public void subscribe(String[] topic, int[] qos) {
        try {
            MqttPushClient.getClient().unsubscribe(topic);
            MqttPushClient.getClient().subscribe(topic, qos);
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }
}

------------------------------------------------------------------------------------------------

5.主题发布代码段:

---------------------------------------------------------------------------------

/**
 * Title: MqttSender
 * Description: TODO
 *
 * @author PJ-YFW
 * @version V1.0
 * @date 2020-03-06
 */
@Component(value = "mqttSender")
@Slf4j
public class MqttSender
{

    @Async
    public void send(String queueName, String msg) {
        log.info("=====================>>>>发送主题"+queueName);
        publish(2,queueName, msg);
    }


    /**
     * 发布,默认qos为0,非持久化
     * @param topic
     * @param pushMessage
     */
    public void publish(String topic,String pushMessage){
        publish(1, false, topic, pushMessage);
    }

    /**
     * 发布
     * @param qos
     * @param retained
     * @param topic
     * @param pushMessage
     */
    public void publish(int qos,boolean retained,String topic,String pushMessage){
        MqttMessage message = new MqttMessage();
        message.setQos(qos);
        message.setRetained(retained);
        message.setPayload(pushMessage.getBytes());
        MqttTopic mTopic = MqttPushClient.getClient().getTopic(topic);
        if(null == mTopic){
            log.error("===================>>>MQTT topic 不存在<<=================");
        }
        MqttDeliveryToken token;
        try {
            token = mTopic.publish(message);
            token.waitForCompletion();
        } catch (MqttPersistenceException e) {
            log.error("============>>>publish fail",e);
            e.printStackTrace();
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }

    /**
     * 发布消息的服务质量(推荐为:2-确保消息到达一次。0-至多一次到达;1-至少一次到达,可能重复),
     * retained 默认:false-非持久化(是指一条消息消费完,就会被删除;持久化,消费完,还会保存在服务器中,当新的订阅者出现,继续给新订阅者消费)
     * @param topic
     * @param pushMessage
     */
    public void publish(int qos, String topic, String pushMessage){
        publish(qos, false, topic, pushMessage);
    }
}

-----------------------------------------------------------------------------------------------------

6.进行双向通信的时候,监听订阅的客户端和主题是否处于连接状态:

-----------------------------------------------------------------------------------------

/**
 * Title: PushCallback
 * Description: TODO
 *
 * @author PJ-YFW
 * @version V1.0
 * @date 2020-03-06
 */
@Slf4j
public class PushCallback implements MqttCallback {
    private MqttPushClient client;
    private MqttConfiguration mqttConfiguration;
    public PushCallback(MqttPushClient client,MqttConfiguration mqttConfiguration) {
        this.client = client;
        this.mqttConfiguration = mqttConfiguration;
    }
  /*  public PushCallback(MqttPushClient client,RedisTemplate redisUtils,MqttPtsService mqttStpService) {
        this.client = client;
        this.redisDao = redisUtils;
        this.mqttStpService=mqttStpService;
    }*/

    @Override
    public void connectionLost(Throwable cause) {
        /** 连接丢失后,一般在这里面进行重连 **/
        if(client != null) {
            while (true) {
                try {
                    log.info("==============》》》[MQTT] 连接断开,30S之后尝试重连...");
                    Thread.sleep(30000);
                    MqttPushClient mqttPushClient = new MqttPushClient();
                    mqttPushClient.connect(mqttConfiguration);
                    if(MqttPushClient.getClient().isConnected()){
                        log.info("=============>>重连成功");
                    }
                    break;
                } catch (Exception e) {
                    log.error("=============>>>[MQTT] 连接断开,重连失败!<<=============");
                    continue;
                }
            }
        }
        log.info(cause.getMessage());
    }


    @Override
    public void deliveryComplete(IMqttDeliveryToken token) {
        //publish后会执行到这里
        log.info("pushComplete==============>>>" + token.isComplete());
    }

    /**
     * 监听对应的主题消息
     * @param topic
     * @param message
     * @throws Exception
     */
    @Override
    public void messageArrived(String topic, MqttMessage message) throws Exception {
        // subscribe后得到的消息会执行到这里面
        log.info("============》》接收消息主题 : " + topic);
        log.info("============》》接收消息Qos : " + message.getQos());
        log.info("============》》接收消息内容 : " + new String(message.getPayload()));

        try {
            //            JSONObject jsonObject = JSON.parseObject(new String(message.getPayload(), "UTF-8"));
            //            MqttDataEntity mqttDataEntity=(MqttDataEntity)JSONObject.toJavaObject(jsonObject, MqttDataEntity.class);
            //            getMqttDataEntity(jsonObject);
            //            mqttStpService.handleReceiveMsg(mqttDataEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

-------------------------------------------------------------------------------------------------

 

好了,进过上面的6个步骤,基本一个简单版的mqtt双向通信完成了,下面用一个测试类测试下:

----------------------------------------------------------------------------------

@RestController
public class MqSubController
{
    @Autowired
    private MqttSender mqttSender;
    @Autowired
    private MqttPushClient mqttPushClient;

    @RequestMapping("/mqttop")
    public String mqttop(){
        String TOPIC1="test_topic1";
        String TOPIC2="test_topic2";
        String TOPIC3="test_topic3";
        String TOPIC4="test_topic4";
       
        int Qos1=1;
        int Qos2=1;
        int Qos3=1;
        int Qos4=1;
      
        String[] topics={TOPIC1,TOPIC2,TOPIC3,TOPIC4};
        int[] qos={Qos1,Qos2,Qos3,Qos4};
        mqttPushClient.subscribe(topics,qos);
        return "订阅主题";
    }



    @RequestMapping("/mqttest")
    public String mqtest(){
        String TOPIC1="test_topic1";
        String TOPIC2="test_topic2";
        String TOPIC3="test_topic3";
        String TOPIC4="test_topic4";
     
        String[] topics={TOPIC1,TOPIC2,TOPIC3,TOPIC4};

        mqttSender.send(topics[new Random().nextInt(5)], JSONObject.toJSONString("测试消息发送"));
        return "发送成功吗!!!!!!!!";
    }

}

--------------------------------------------------------------

 

总结:上面的代码就是本人在物联网项目中用的mqtt进行长连接进行消息的推送等功能,在当今的物联网开发中经常用用到mqtt进行双向通信的,介绍一个官方的学习地址:https://docs.emqx.io/tutorial/latest/en/

有兴趣的朋友可以上去看下哦,也欢迎同行互联交流技术!!!!!

你可能感兴趣的:(物联网开发)