[Java] SpringBoot2 integrates mqtt server EMQ to realize message subscription, publishing and storage (1)

 

It's time to show off and share technology again

This article is based on the previous two articles, using the open source MQTT server of the Internet of Things industry to receive data, so that the popular Springboot framework project in the web industry can be subscribed and published, and data is stored and displayed.

If you don’t know much about data upload, you can read my previous article

Stm32f103c8t6+ESP8266-01s+DHT11 realizes uploading temperature and humidity data to the server

Springboot+STM32+ESP8266 uses HTTP GET and POST to send requests to upload data to the Springboot project and display it

The flow of this article is:

1. Build an open source mqtt server

2. Create a Springboot project

3.Integrate MQTT in Springboot

4. Use Springboot to subscribe and publish data

5. Springboot stores the subscribed data into the database

6. Develop real-time subscription/publishing display pages

I will divide it into two articles to share. Of course, if two articles can't fit, I will come again.

 

table of Contents

1. Open source IoT server EMQ

Why use MQTT

Build mqtt service

EMQ management page

summary

2. Build Springboot2+mqtt service project

Build mqtt environment

Implement mqtt function code

summary

Three. Summary


 

 

1. Open source IoT server EMQ

Why use MQTT

When it comes to the Internet of Things, what you can think of is the interconnection of everything to the cloud, and the so-called cloud is uploading data to a network server, and uploading mostly uses the MQTT protocol.

When I first came into contact with the ESP8266 module, the first known data transmission protocol was TCP UDP, and these two protocols were used on the basis of the HTTP protocol.

And MQTT is also born on top of TCP based on HTTP protocol

So why does the IoT industry recommend using Mqtt to send data?

I understand two aspects: one is to save traffic and bandwidth, and the other is a heartbeat packet.

Save traffic and bandwidth: Because the number of bytes in the header of the data message is small, and the bytes of the protocol are small, because the number of bytes of a piece of data is very small, the traffic required for sending is small, and some mobile devices only need 2G network to upload data.

Heartbeat packet: Internet of Things devices need to know whether the device is online at all times. If the device is offline without timely feedback, it will affect the data upload if the device is offline, and it will affect the production of the device. By sending a small heartbeat packet to let the server know whether the device is online

 

In fact, the above are all nonsense used to account for the length of the article, the real dry goods are below

Build mqtt service

Directly use the open source Mqtt build solution Official website address:  EMQ Open source mqtt server build solution official website

 

This page, this style, this feeling, I can't make it

Click on the free trial directly and you will see that there are three versions.As a good young man who is diligent and thrifty, I chose EMQ X Broker 

Those with financial resources or to solve enterprise-level problems can choose to try EMQ X Enterprise and close this article directly. I wrote this article just to use it for free and to store data in the database.

Here I describe the price comparison of the differences between the various versions. The specific differences can be seen in the official document: the  specific differences between the different versions can be seen in the document.

If there is no problem, take the next step

We choose the first  EMQ X Broker 

 The following will let you choose the version you want to download, what platform you are using, and platform version. The download is also directly executed on the server and the written instructions are directly executed. Wait for the download to complete. Follow the steps of installation and operation, and mqtt will be built. And started

 

EMQ management page

Enter your IP in the browser after startup: 18083

For example, 172.0.0.1:18038, it will jump to the management page of mqtt, the initial account admin password is public

After successful login, you will see

It’s very cool, right? It’s so cool. I’m all looking at tired eyes, and it’s all in English. What is this? (Metro old man’s phone), it’s not that my English is not good. I’m for the public’s consideration. We need to modify it.

summary

Mqtt has been set up, if there are IoT devices, they can be used to access the platform through ip:1883

For example, 172.0.0.1:1883 sends data on the specified topic to the platform, or you can subscribe to the specified topic through the platform

What are the functions of the left menu on the specific EMQ platform can be Baidu by yourself, if I have time (unless I am really idle), I will also sort it out

Because this version has few features, most of the features are implemented through Springboot

 

2. Build Springboot2+mqtt service project

Considering that some developers use eclipse and some use idea, here is

Do you think I want to write out the methods of creating Springboot with two compilers? Does it make me guess? Congratulations, I don't write this paragraph

 

Build mqtt environment

Introduce the mqtt package in pom.xml

        <!--MQTT使用包-->
        <dependency>
            <groupId>org.eclipse.paho</groupId>
            <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
            <version>1.2.2</version>
        </dependency>

 

Add mqtt configuration parameters in application.yml

spring:
  mqtt:
    broker: tcp://{这里写EMQ部署的地址}:1883
    clientId: 123456 #客户端的id
    username: 登录账号
    password: 登录密码
    timeout: 2000
    KeepAlive: 20
    topics: ServerId_02 #主题
    qos: 1 #心跳包级别

 

Implement mqtt function code

Create a class to read the configuration in application.yml

Let me talk about it here that I use @Getter and @Setter annotations to automatically create the getset method. If you don't need it, use the method that comes with idea to create the getset method.

@Getter
@Setter
@Component
@Configuration
public class MqttConfiguration {

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

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

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

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

    @Value("${spring.mqtt.timeout}")
    private int timeout;

    @Value("${spring.mqtt.KeepAlive}")
    private int KeepAlive;

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

    @Value("${spring.mqtt.qos}")
    private int qos;
    
}

 

Add mqtt initialization method to the startup class Application

@SpringBootApplication
public class Application implements ApplicationRunner{

    //读取mqtt配置
    @Autowired
    private MqttConfiguration mqttConfiguration;


    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
}

    /**
     * mqtt 初始化
     * @param args
     * @throws Exception
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        if(true){
            if (log.isInfoEnabled()){
                log.info("===============>>>Mqtt is run starting:<<==================");
            }
            MqttPushClient mqttPushClient = new MqttPushClient();
            mqttPushClient.connect(mqttConfiguration);
        }
    }


}

Mqttbeanbak class

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

 MqttPushClient class

@Slf4j
public class MqttPushClient  {
    private static MqttClient client;

    public static MqttClient getClient() {
        return client;
    }

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




    /**
     * 编辑连接信息
     * @param userName
     * @param password
     * @param outTime
     * @param KeepAlive
     * @return
     */
    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(StandardCharsets.UTF_8), 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连接成功<<======================");

                } 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);
        try {
            message.setPayload(pushMessage.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        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);

    }

    /**
     *
     * 订阅多个主题
     *
     * @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();
        }
    }

    /**
     * 清空主题
     * @param topic
     */
    public void cleanTopic(String topic) {
        try {
            MqttPushClient.getClient().unsubscribe(topic);
        } catch (MqttException e) {
            log.error(e.getMessage());
            e.printStackTrace();
        }
    }




}

 

MqttSender class

@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);
        try {
            message.setPayload(pushMessage.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        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);
    }
}

 

PushCallback class

Pay more attention to this class, because the subscribed data will enter the messageArrived method. If you want to process and save the data, you need to operate in this class

Since this class cannot use @Autowired to call the data processing data storage method, it needs special processing

The next article will explain in detail

@Slf4j
@Component
public class PushCallback implements MqttCallback {


    private MqttPushClient client;
    private MqttConfiguration mqttConfiguration;


    public PushCallback(MqttPushClient client ,MqttConfiguration mqttConfiguration) {
        this.client = client;
        this.mqttConfiguration = mqttConfiguration;
    }

    @Override
    public void connectionLost(Throwable cause) {
        /** 连接丢失后,一般在这里面进行重连 **/
        if(client != null) {
            while (true) {
                try {
                    log.info("==============》》》[MQTT] 连接断开,5S之后尝试重连...");
                    Thread.sleep(5000);
                    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("publish后会执行到这里");
        log.info("pushComplete==============>>>" + token.isComplete());
    }


    /**
     * 监听对应的主题消息
     * @param topic
     * @param message
     * @throws Exception
     */
    @Override
    public void messageArrived(String topic, MqttMessage message) throws Exception {
        // subscribe后得到的消息会执行到这里面
        String Payload = new String(message.getPayload());

        log.info("============》》接收消息主题 : " + topic);
        log.info("============》》接收消息Qos : " + message.getQos());
        log.info("============》》接收消息内容 : " + Payload);
        log.info("============》》接收ID : " + message.getId());
        log.info("接收数据结束 下面可以执行数据处理操作");

    }
}

 

MqttController class

@Controller
@Slf4j
@ResponseBody
@RequestMapping("/mqtt")
public class MqttController {

    //发送逻辑
    @Autowired
    private MqttSender mqttSender;
    
    //订阅逻辑
    @Autowired
    private MqttPushClient mqttPushClient;

    @RequestMapping("/sendmqtt")
    public String sendmqtt(){
        String TOPIC1="testtest1";
        String JSON = "{'gender':'man','hobby':'girl'}";
        log.info("    本机主题:"+TOPIC1+" 发送数据为:"+JSONObject.toJSONString(JSON));
        mqttSender.send(TOPIC1, JSON);
        log.info("     发送结束");
        return "发送结束";
    }

    @RequestMapping("/subscriptionmqtt")
    public String subscriptionmqtt(){
        int Qos=1;
        String TOPIC1="testtest1";
        String[] topics={TOPIC1};
        int[] qos={Qos};
        mqttPushClient.subscribe(topics,qos);
        return "订阅主题";
    }
}

 

The code of the integrated mqtt is these, the following is the test whether it can communicate with the mqtt server

Send data directly when there is no subscription

Send data access http://127.0.0.1:8080/mqtt/sendmqtt 

Now subscribe to this topic and visit http://127.0.0.1:8085/mqtt/subscriptionmqtt again 

Send the data again and you will see that because of the subscription to the topic, the data we sent to the topic will be subscribed, and the sent will be received 

 

summary

You can see that the subscription method can already read the sent data,

Someone may think of using the @Autowired annotation in the receiving method to inject the data into the database method, so that the data can be stored in the database,

Congratulations, you will see that your mqtt connection will continue to reconnect and report a null pointer.

You will find that the method injected with @Autowired is empty, this is what the next article will talk about

 

Three. Summary

So far, the simple EMQ service deployment and Springboot's mqtt environment have been set up, which can realize simple data sending topic subscription

It can be regarded as returning to my line of development for development, from hardware microcontrollers to software program development, it is very interesting and interesting

I also want to be able to communicate with each other to share technology with like-minded people, it is best to pay a female friend

Hey, maybe blogging is one of the few ways I can show myself

If you want to communicate technology, you can directly trust me, I have a good temper (nod)

 

 

Next article Solve the injection method to report a null pointer and store the data received by the subscription into the database, and then develop a real-time send/subscribe tool page

 

 

First send out the page to show it off

 

Guess you like

Origin blog.csdn.net/qq_41873771/article/details/114965238