MQTT(EMQX) - Linux CentOS Docker 安装

MQTT 概述

MQTT (Message Queue Telemetry Transport) 是一个轻量级传输协议,它被设计用于轻量级的发布/订阅式消息传输,MQTT协议针对低带宽网络,低计算能力的设备,做了特殊的优化。是一种简单、稳定、开放、轻量级易于实现的消息协议,在物联网的应用下的信息采集,工业控制,智能家居等方面具有广泛的适用性。

  1. MQTT更加简单:MQTT是一种消息队列协议,使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合,相对于其他协议,开发更简单;
  2. MQTT网络更加稳定:工作在TCP/IP协议上;由TCP/IP协议提供稳定的网络连接;
  3. 轻量级:小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量;适合低带宽,数据量较小的应用;

MQTT支持三种消息发布服务质量(QoS):

  • “至多一次”(QoS==0):消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
  • “至少一次”(QoS==1):确保消息到达,但消息重复可能会发生。
  • “只有一次”(QoS==2):确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果。小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量。

MQTT 三种身份

  • 发布者、代理、订阅者,发布者和订阅者都为客户端,代理为服务器,同时消息的发布者也可以是订阅者(为了节约内存和流量发布者和订阅者一般都会定义在一起)。
  • MQTT传输的消息分为主题(Topic,可理解为消息的类型,订阅者订阅后,就会收到该主题的消息内容(payload))和负载(payload,可以理解为消息的内容)两部分。

MQTT和Websocket的区别是什么?

MQTT是为了物联网场景设计的基于TCP的Pub/Sub协议,有许多为物联网优化的特性,比如适应不同网络的QoS、层级主题、遗言等等。

WebSocket是为了HTML5应用方便与服务器双向通讯而设计的协议,HTTP握手然后转TCP协议,用于取代之前的Server Push、Comet、长轮询等老旧实现。

两者之所有有交集,是因为一个应用场景:如何通过HTML5应用来作为MQTT的客户端,以便接受设备消息或者向设备发送信息,那么MQTT over WebSocket自然成了最合理的途径了。

语言支持

Java、C#、Python、C/C++、Objective-C、Node.js、Javascript、Ruby、Golang、PHP

应用场景

遥感数据、汽车、智能家居、智慧城市、医疗医护

即时通讯:MQ 可以通过订阅主题,轻松实现 1对1、1对多的通讯。

连接

  1. 登录IM服务
  2. 获取MQTT 服务器地址
  3. 建立MQTT连接

通讯

1、4. 发送者 向IM 服务发送消息

2、5. IM 服务,将消息持久化,并发给 MQTT

3、6. 消费者 从MQTT订阅到消息

本文 Demo 介绍

主要用到 InitializingBean、BasePooledObjectFactory、GenericObjectPool、GenericObjectPoolConfig

InitializingBean:实例化工厂、连接池,参考:Java SpringBoot Bean InitializingBean

GenericObjectPool:获取连接对象,如果池中没有,通过工厂创建 参考:Java GenericObjectPool 对象池化技术--SpringBoot sftp 连接池工具类

BasePooledObjectFactory::创建 MqttClient 连接 参考:Java BasePooledObjectFactory 对象池化技术

GenericObjectPoolConfig:GenericObjectPoolConfig是封装GenericObject池配置的简单“结构”,此类不是线程安全的;它仅用于提供创建池时使用的属性。大多数情况,可以使用GenericObjectPoolConfig提供的默认参数就可以满足日常的需求。



对象获取流程图

username(用户名)和password(密码)。这里的用户名和密码是用于客户端连接服务端时进行认证需要的。

有些MQTT服务端需要客户端在连接时提供用户名和密码。只有客户端正确提供了用户名和密码后,才能连接服务端。否则服务端将会拒绝客户端连接,那么客户端也就无法发布和订阅消息了。 当然,那些没有开启用户密码认证的服务端无需客户端提供用户名和密码认证信息。

Deom代码

POM

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>vipsoft-parent</artifactId>
  7. <groupId>com.vipsoft.boot</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>vipsoft-mqtt</artifactId>
  12. <version>1.0-SNAPSHOT</version>
  13. <dependencies>
  14. <dependency>
  15. <groupId>org.apache.commons</groupId>
  16. <artifactId>commons-pool2</artifactId>
  17. <version>2.7.0</version>
  18. </dependency>
  19. <dependency>
  20. <groupId>org.eclipse.paho</groupId>
  21. <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
  22. <version>1.2.5</version>
  23. </dependency>
  24. <dependency>
  25. <groupId>cn.hutool</groupId>
  26. <artifactId>hutool-all</artifactId>
  27. <version>5.3.6</version>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.springframework.boot</groupId>
  31. <artifactId>spring-boot-starter-web</artifactId>
  32. </dependency>
  33. <dependency>
  34. <groupId>org.springframework.boot</groupId>
  35. <artifactId>spring-boot-starter-actuator</artifactId>
  36. </dependency>
  37. <dependency>
  38. <groupId>org.springframework.boot</groupId>
  39. <artifactId>spring-boot-starter-test</artifactId>
  40. <scope>test</scope>
  41. <exclusions>
  42. <exclusion>
  43. <groupId>org.junit.vintage</groupId>
  44. <artifactId>junit-vintage-engine</artifactId>
  45. </exclusion>
  46. </exclusions>
  47. </dependency>
  48. </dependencies>
  49. <build>
  50. <plugins>
  51. <plugin>
  52. <groupId>org.springframework.boot</groupId>
  53. <artifactId>spring-boot-maven-plugin</artifactId>
  54. </plugin>
  55. </plugins>
  56. </build>
  57. </project>

Resource

application.yml

  1. server:
  2. port: 8088
  3. application:
  4. name: MQTT Demo
  5. mqtt:
  6. host: tcp://172.16.0.88:1883
  7. clientId: VipSoft_MQTT
  8. poolConfig:
  9. customSet: false
  10. minIdle: 8
  11. maxIdle: 20
  12. maxTotal: 20
  13. lifo: false

Config

MqttConfig

用户名和密码除了有以上功能外,有些公用MQTT服务端也利用此信息来识别客户端属于哪一个用户,从而对客户端进行管理。比如用户可以拥有私人主题,这些主题只有该用户可以发布和订阅。对于私人主题,服务端就可以利用客户端连接时的用户名和密码来判断该客户端是否有发布订阅该用户私人主题的权限。

  1. package com.vipsoft.mqtt.config;
  2. import org.springframework.boot.context.properties.ConfigurationProperties;
  3. @ConfigurationProperties(prefix = "mqtt")
  4. public class MqttConfig {
  5. /**
  6. * MQTT host 地址
  7. */
  8. private String host;
  9. /**
  10. * 客户端Id
  11. */
  12. private String clientId;
  13. /**
  14. * 登录用户(可选)
  15. */
  16. private String userName;
  17. /**
  18. * 登录密码(可选)
  19. */
  20. private String password;
  21. /**
  22. * Mqtt Pool Config
  23. */
  24. private MqttPoolConfig poolConfig;
  25. public String getHost() {
  26. return host;
  27. }
  28. public void setHost(String host) {
  29. this.host = host;
  30. }
  31. public String getClientId() {
  32. return clientId;
  33. }
  34. public void setClientId(String clientId) {
  35. this.clientId = clientId;
  36. }
  37. public String getUserName() {
  38. return userName;
  39. }
  40. public void setUserName(String userName) {
  41. this.userName = userName;
  42. }
  43. public String getPassword() {
  44. return password;
  45. }
  46. public void setPassword(String password) {
  47. this.password = password;
  48. }
  49. public MqttPoolConfig getPoolConfig() {
  50. return poolConfig;
  51. }
  52. public void setPoolConfig(MqttPoolConfig poolConfig) {
  53. this.poolConfig = poolConfig;
  54. }
  55. }

MqttPoolConfig

  1. package com.vipsoft.mqtt.config;
  2. public class MqttPoolConfig {
  3. /**
  4. * 是否启用自定义配置
  5. */
  6. private boolean customSet;
  7. /**
  8. * 最小的空闲连接数
  9. */
  10. private int minIdle;
  11. /**
  12. * 最大的空闲连接数
  13. */
  14. private int maxIdle;
  15. /**
  16. * 最大连接数
  17. */
  18. private int maxTotal;
  19. public boolean isCustomSet() {
  20. return customSet;
  21. }
  22. public void setCustomSet(boolean customSet) {
  23. this.customSet = customSet;
  24. }
  25. public int getMinIdle() {
  26. return minIdle;
  27. }
  28. public void setMinIdle(int minIdle) {
  29. this.minIdle = minIdle;
  30. }
  31. public int getMaxIdle() {
  32. return maxIdle;
  33. }
  34. public void setMaxIdle(int maxIdle) {
  35. this.maxIdle = maxIdle;
  36. }
  37. public int getMaxTotal() {
  38. return maxTotal;
  39. }
  40. public void setMaxTotal(int maxTotal) {
  41. this.maxTotal = maxTotal;
  42. }
  43. }

Pool

MqttClientManager

  1. package com.vipsoft.mqtt.pool;
  2. import cn.hutool.core.util.StrUtil;
  3. import com.vipsoft.mqtt.config.MqttConfig;
  4. import com.vipsoft.mqtt.config.MqttPoolConfig;
  5. import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
  6. import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
  7. import org.slf4j.Logger;
  8. import org.slf4j.LoggerFactory;
  9. import org.springframework.beans.factory.InitializingBean;
  10. import org.springframework.stereotype.Service;
  11. /**
  12. * 对类的创建之前进行初始化的操作,在afterPropertiesSet()中完成。
  13. */
  14. @Service
  15. public class MqttClientManager implements InitializingBean {
  16. private static Logger logger = LoggerFactory.getLogger(MqttClientManager.class);
  17. /**
  18. * mqtt连接配置
  19. */
  20. private final MqttConfig mqttConfig;
  21. private MqttConnectionPool<MqttConnection> mqttPool;
  22. public MqttClientManager(MqttConfig mqttConfig) {
  23. this.mqttConfig = mqttConfig;
  24. }
  25. /**
  26. * 创建连接池
  27. */
  28. @Override
  29. public void afterPropertiesSet() {
  30. try {
  31. // 连接池配置
  32. GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
  33. this.initPoolConfig(poolConfig);
  34. // mqtt连接配置
  35. MqttConnectOptions connOpts = new MqttConnectOptions();
  36. connOpts.setUserName(this.mqttConfig.getUserName());
  37. if (StrUtil.isNotEmpty(mqttConfig.getPassword())) {
  38. connOpts.setPassword(this.mqttConfig.getPassword().toCharArray());
  39. }
  40. // 创建工厂对象
  41. MqttConnectionFactory connectionFactory = new MqttConnectionFactory(mqttConfig.getHost(), connOpts);
  42. // 创建连接池
  43. mqttPool = new MqttConnectionPool<>(connectionFactory, poolConfig);
  44. } catch (Exception e) {
  45. logger.error(e.getMessage(), e);
  46. }
  47. }
  48. private void initPoolConfig(GenericObjectPoolConfig poolConfig) {
  49. MqttPoolConfig mqttConnectionPoolConfig = this.mqttConfig.getPoolConfig();
  50. if (mqttConnectionPoolConfig.isCustomSet()) {
  51. // 设置连接池配置信息
  52. poolConfig.setMinIdle(mqttConnectionPoolConfig.getMinIdle());
  53. poolConfig.setMaxIdle(mqttConnectionPoolConfig.getMaxIdle());
  54. poolConfig.setMaxTotal(mqttConnectionPoolConfig.getMaxTotal());
  55. // TODO 补全
  56. }
  57. }
  58. /**
  59. * 根据key找到对应连接
  60. */
  61. public MqttConnection getConnection() throws Exception {
  62. return this.mqttPool.borrowObject();
  63. }
  64. }

MqttConnection

  1. package com.vipsoft.mqtt.pool;
  2. import org.apache.commons.pool2.impl.GenericObjectPool;
  3. import org.eclipse.paho.client.mqttv3.MqttClient;
  4. import org.eclipse.paho.client.mqttv3.MqttMessage;
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. public class MqttConnection {
  8. private Logger logger = LoggerFactory.getLogger(this.getClass());
  9. private MqttClient mqttClient;
  10. public MqttConnection(MqttClient mqttClient) {
  11. this.mqttClient = mqttClient;
  12. }
  13. /**
  14. * 隶属于的连接池
  15. */
  16. private GenericObjectPool<MqttConnection> belongedPool;
  17. /**
  18. * 推送方法消息
  19. */
  20. public void publish(String topic, String message) throws Exception {
  21. MqttMessage mqttMessage = new MqttMessage();
  22. mqttMessage.setPayload(message.getBytes());
  23. mqttClient.publish(topic, mqttMessage);
  24. System.out.println("对象:" + mqttClient + " " + "发送消息:" + message);
  25. }
  26. /**
  27. * 销毁连接
  28. */
  29. public void destroy() {
  30. try {
  31. if (this.mqttClient.isConnected()) {
  32. this.mqttClient.disconnect();
  33. }
  34. this.mqttClient.close();
  35. } catch (Exception e) {
  36. logger.error("MqttConnection destroy ERROR ; errorMsg={}", e.getMessage(), e, e);
  37. }
  38. }
  39. /**
  40. * 换回连接池
  41. */
  42. public void close() {
  43. if (belongedPool != null) {
  44. this.belongedPool.returnObject(this);
  45. }
  46. }
  47. public MqttClient getMqttClient() {
  48. return mqttClient;
  49. }
  50. public void setMqttClient(MqttClient mqttClient) {
  51. this.mqttClient = mqttClient;
  52. }
  53. public GenericObjectPool<MqttConnection> getBelongedPool() {
  54. return belongedPool;
  55. }
  56. public void setBelongedPool(GenericObjectPool<MqttConnection> belongedPool) {
  57. this.belongedPool = belongedPool;
  58. }
  59. }

MqttConnectionFactory

  1. package com.vipsoft.mqtt.pool;
  2. import cn.hutool.core.date.DateUtil;
  3. import cn.hutool.core.util.StrUtil;
  4. import cn.hutool.system.HostInfo;
  5. import org.apache.commons.pool2.BasePooledObjectFactory;
  6. import org.apache.commons.pool2.PooledObject;
  7. import org.apache.commons.pool2.impl.DefaultPooledObject;
  8. import org.eclipse.paho.client.mqttv3.MqttClient;
  9. import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
  10. import org.slf4j.Logger;
  11. import org.slf4j.LoggerFactory;
  12. import java.util.concurrent.atomic.AtomicInteger;
  13. public class MqttConnectionFactory extends BasePooledObjectFactory<MqttConnection> {
  14. private static final Logger logger = LoggerFactory.getLogger(MqttConnectionFactory.class);
  15. // AtomicInteger是一个提供原子操作的Integer类,通过线程安全的方式操作加减
  16. private AtomicInteger counter = new AtomicInteger();
  17. /**
  18. * 连接地址
  19. */
  20. private String serverURI;
  21. /**
  22. * 当前服务IP
  23. */
  24. private String localHostIP;
  25. /**
  26. * mqtt连接配置
  27. */
  28. private MqttConnectOptions mqttConnectConfig;
  29. /**
  30. * 根据mqtt连接 配置创建工厂
  31. */
  32. public MqttConnectionFactory(String serverURI, MqttConnectOptions mqttConnectConfig) {
  33. this.serverURI = serverURI;
  34. this.mqttConnectConfig = mqttConnectConfig;
  35. }
  36. /**
  37. * 在对象池中创建对象
  38. *
  39. * @return
  40. * @throws Exception
  41. */
  42. @Override
  43. public MqttConnection create() throws Exception {
  44. // 实现线程安全避免在高并发的场景下出现clientId重复导致无法创建连接的情况
  45. int count = this.counter.addAndGet(1);
  46. // 根据ip+编号,生成唯一clientId
  47. String clientId = this.getLosthostIp() + "_" + DateUtil.thisMillsecond();
  48. // 创建MQTT连接对象
  49. MqttClient mqttClient = new MqttClient(serverURI, clientId);
  50. // 建立连接
  51. mqttClient.connect(mqttConnectConfig);
  52. // 构建mqttConnection对象
  53. MqttConnection mqttConnection = new MqttConnection(mqttClient);
  54. logger.info("在对象池中创建对象 {}", clientId);
  55. return mqttConnection;
  56. }
  57. /**
  58. * common-pool2 中创建了 DefaultPooledObject 对象对对象池中对象进行的包装。
  59. * 将我们自定义的对象放置到这个包装中,工具会统计对象的状态、创建时间、更新时间、返回时间、出借时间、使用时间等等信息进行统计
  60. *
  61. * @param mqttConnection
  62. * @return
  63. */
  64. @Override
  65. public PooledObject<MqttConnection> wrap(MqttConnection mqttConnection) {
  66. logger.info("封装默认返回类型 {}", mqttConnection.toString());
  67. return new DefaultPooledObject<>(mqttConnection);
  68. }
  69. /**
  70. * 销毁对象
  71. *
  72. * @param p 对象池
  73. * @throws Exception 异常
  74. */
  75. @Override
  76. public void destroyObject(PooledObject<MqttConnection> p) throws Exception {
  77. if (p == null) {
  78. return;
  79. }
  80. MqttConnection mqttConnection = p.getObject();
  81. logger.info("销毁对象 {}", p.getObject().getMqttClient());
  82. mqttConnection.destroy();
  83. }
  84. /**
  85. * 校验对象是否可用
  86. *
  87. * @param p 对象池
  88. * @return 对象是否可用结果,boolean
  89. */
  90. @Override
  91. public boolean validateObject(PooledObject<MqttConnection> p) {
  92. MqttConnection mqttConnection = p.getObject();
  93. boolean result = mqttConnection.getMqttClient().isConnected();
  94. logger.debug("validateObject serverURI {},client_id {},result {}", mqttConnection.getMqttClient().getServerURI(),
  95. mqttConnection.getMqttClient().getClientId(), result);
  96. return result;
  97. }
  98. /**
  99. * 激活钝化的对象系列操作
  100. *
  101. * @param p 对象池
  102. * @throws Exception 异常信息
  103. */
  104. @Override
  105. public void activateObject(PooledObject<MqttConnection> p) throws Exception {
  106. logger.info("激活钝化的对象 {}", p.getObject().getMqttClient());
  107. }
  108. /**
  109. * 钝化未使用的对象
  110. *
  111. * @param p 对象池
  112. * @throws Exception 异常信息
  113. */
  114. @Override
  115. public void passivateObject(PooledObject<MqttConnection> p) throws Exception {
  116. logger.info("钝化未使用的对象 {}", p.getObject().getMqttClient());
  117. }
  118. /**
  119. * 获取当前服务真实IP
  120. */
  121. private String getLosthostIp() {
  122. if (StrUtil.isNotBlank(this.localHostIP)) {
  123. return this.localHostIP;
  124. }
  125. HostInfo hostInfo = new HostInfo();
  126. this.localHostIP = hostInfo.getAddress();
  127. return this.localHostIP;
  128. }
  129. }

MqttConnectionPool

  1. package com.vipsoft.mqtt.pool;
  2. import org.apache.commons.pool2.impl.GenericObjectPool;
  3. import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
  4. public class MqttConnectionPool<T> extends GenericObjectPool<MqttConnection> {
  5. public MqttConnectionPool(MqttConnectionFactory factory, GenericObjectPoolConfig config) {
  6. super(factory, config);
  7. }
  8. /**
  9. * 从对象池获得一个对象
  10. */
  11. @Override
  12. public MqttConnection borrowObject() throws Exception {
  13. MqttConnection conn = super.borrowObject();
  14. // 设置所属连接池
  15. if (conn.getBelongedPool() == null) {
  16. conn.setBelongedPool(this);
  17. }
  18. return conn;
  19. }
  20. /**
  21. * 归还一个连接对象
  22. * @param conn
  23. */
  24. @Override
  25. public void returnObject(MqttConnection conn) {
  26. if (conn!=null) {
  27. super.returnObject(conn);
  28. }
  29. }
  30. }

utils

MqttClientManager

  1. package com.vipsoft.mqtt.utils;
  2. import com.vipsoft.mqtt.pool.MqttClientManager;
  3. import com.vipsoft.mqtt.pool.MqttConnection;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.stereotype.Service;
  8. @Service
  9. public class MqttUtil {
  10. private final Logger logger = LoggerFactory.getLogger(this.getClass());
  11. @Autowired
  12. MqttClientManager mqttManager;
  13. public void publish(String clientId, String message) {
  14. logger.info("publish INFO ; clientId={}, message={}", clientId, message);
  15. MqttConnection connection = null;
  16. try {
  17. connection = mqttManager.getConnection();
  18. logger.info("publish INFO ; clientId={},targetUrl={}", clientId, connection.getMqttClient().getServerURI());
  19. connection.publish(clientId, message);
  20. } catch (Exception e) {
  21. logger.error("publish ERROR ; clientId={},message={}", clientId, message, e, e);
  22. } finally {
  23. if (null != connection) {
  24. connection.close();
  25. }
  26. }
  27. }
  28. }

test

PushCallback

  1. package com.vipsoft.mqtt;
  2. import cn.hutool.core.date.DateUtil;
  3. import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
  4. import org.eclipse.paho.client.mqttv3.MqttCallback;
  5. import org.eclipse.paho.client.mqttv3.MqttMessage;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. public class PushCallback implements MqttCallback {
  9. private final Logger logger = LoggerFactory.getLogger(this.getClass());
  10. @Override
  11. public void connectionLost(Throwable cause) {
  12. // 连接丢失后进行重连
  13. System.out.println("连接断开,可以做重连");
  14. logger.info("掉线时间:{}", DateUtil.now());
  15. }
  16. @Override
  17. public void deliveryComplete(IMqttDeliveryToken token) {
  18. System.out.println("deliveryComplete---------" + token.isComplete());
  19. }
  20. @Override
  21. public void messageArrived(String topic, MqttMessage message) throws Exception {
  22. System.out.println("接收消息主题 : " + topic);
  23. System.out.println("接收消息Qos : " + message.getQos());
  24. System.out.println("接收消息内容 : " + new String(message.getPayload()));
  25. }
  26. }

MqttProducerTest

  1. package com.vipsoft.mqtt;
  2. import cn.hutool.core.date.DateUtil;
  3. import com.vipsoft.mqtt.utils.MqttUtil;
  4. import org.junit.jupiter.api.Test;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.boot.test.context.SpringBootTest;
  7. import java.util.concurrent.CountDownLatch;
  8. @SpringBootTest
  9. public class MqttProducerTest {
  10. @Autowired
  11. MqttUtil mqttUtil;
  12. @Test
  13. void pushMessateTest() throws Exception {
  14. for (int i = 0; i < 50; i++) {
  15. String topic = "VipSoft_MQTT";
  16. mqttUtil.publish(topic, "发送消息:" + DateUtil.now());
  17. Thread.sleep(3000);
  18. }
  19. new CountDownLatch(1).await();
  20. }
  21. }

MqttConsumerTest

  1. package com.vipsoft.mqtt;
  2. import com.vipsoft.mqtt.pool.MqttClientManager;
  3. import org.eclipse.paho.client.mqttv3.MqttClient;
  4. import org.junit.jupiter.api.Test;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.boot.test.context.SpringBootTest;
  7. @SpringBootTest
  8. public class MqttConsumerTest {
  9. @Autowired
  10. MqttClientManager mqttManager;
  11. @Test
  12. void subscribeTest() throws Exception {
  13. String topic = "VipSoft_MQTT";
  14. MqttClient mqttClient = mqttManager.getConnection().getMqttClient();
  15. //这里的setCallback需要新建一个Callback类并实现 MqttCallback 这个类
  16. mqttClient.setCallback(new PushCallback());
  17. while (true) {
  18. mqttClient.subscribe(topic);
  19. Thread.sleep(1000);
  20. }
  21. }
  22. }
运行方式
  1. MqttConsumerTest.subscribeTest()
  2. MqttProducerTest.pushMessateTest()

更多文章参考:

小程序mqtt实现聊天功能

Gitee 源码地址:https://gitee.com/VipSoft/VipBoot/

MQTT(EMQX) - SpringBoot 整合MQTT 连接池 Demo - 附源代码 + 在线客服聊天架构图的更多相关文章

  1. springboot整合druid连接池、mybatis实现多数据源动态切换

    demo环境: JDK 1.8 ,Spring boot 1.5.14 一 整合durid 1.添加druid连接池maven依赖 <dependency> <groupId> ...

  2. HttpClient实战三:Spring整合HttpClient连接池

    简介 在微服务架构或者REST API项目中,使用Spring管理Bean是很常见的,在项目中HttpClient使用的一种最常见方式就是:使用Spring容器XML配置方式代替Java编码方式进行H ...

  3. ASP.NET SignalR 与LayIM配合,轻松实现网站客服聊天室(二) 实现聊天室连接

    上一篇已经简单介绍了layim WebUI即时通讯组件和获取数据的后台方法.现在要讨论的是SingalR的内容,之前都是直接贴代码.那么在贴代码之前先分析一下业务模型,顺便简单讲一下SingalR里的 ...

  4. springboot使用lettuce连接池

    springboot对连接池的使用非常智能,配置文件中添加lettuce.pool相关配置,则会使用到lettuce连接池,并将相关配置设置为连接池相关参数,(前提是这些参数是springboot配置 ...

  5. SpringBoot 使用Hikaricp连接池

    1.添加pom.xml依赖 如果是SpringBoot2.0,那么默认的连接池就是Hikaricp,不需要配置 其他的,如果继承 <parent> <groupId>org.s ...

  6. springboot整合druid数据库连接池并开启监控

    简介 Druid是一个关系型数据库连接池,它是阿里巴巴的一个开源项目.Druid支持所有JDBC兼容的数据库,包括Oracle.MySQL.Derby.PostgreSQL.SQL Server.H2 ...

  7. springboot缓存及连接池配置

    参见https://coding.imooc.com/lesson/117.html#mid=6412 1.springboot的springweb自己默认以及配置好了缓存,只需要在主文件(XxxAp ...

  8. SpringBoot Beans定义 连接池

    SpringBoot Beans定义 原有Spring框架,定义Bean方法如下 xml配置 组件扫描.@Controller.@Service... 原有Spring框架,参数注入方法如下 常用的参 ...

  9. springboot使用druid连接池连接Oracle数据库的基本配置

    #阿里连接池配置 #spring.datasource.druid.driver-class-name=oracle.jdbc.driver.OracleDriver #可配可不配,阿里的数据库连接池 ...

  10. SpringBoot下Druid连接池的使用配置

    Druid是一个JDBC组件,druid 是阿里开源在 github 上面的数据库连接池,它包括三部分: * DruidDriver 代理Driver,能够提供基于Filter-Chain模式的插件体 ...

随机推荐

  1. Spring系列之面向切面编程-15

    目录 AOP 概念 AOP 代理 @AspectJ 支持 启用@AspectJ 支持 使用 Java 配置启用 @AspectJ 支持 通过 XML 配置启用 @AspectJ 支持 声明一个方面 声 ...

  2. 2月21日python程序设计

    Python变量 1.不需要事先声明变量名及其类型,直接赋值即可. 2.强类型编程语言,根据赋值或运算来推断变量类型. 3.动态类型语言,变量的类型也是可以随时变化的. >>>  x ...

  3. 记一次因为关键字OUT 导致的后台"sql injection violation" 报错的问题

    在navicat和mssm中执行用字段别名'out'均没有问题,但是在mybatis里使用就会报 "sql injection violation, syntax error: ERROR. ...

  4. Python爬虫之Scrapy制作爬虫

    前几天我有用过Scrapy架构编写了一篇爬虫的代码案例深受各位朋友们喜欢,今天趁着热乎在上一篇有关Scrapy制作的爬虫代码,相信有些基础的程序员应该能看的懂,很简单,废话不多说一起来看看. 前期准备 ...

  5. crontab命令加载和使用

    crontab命令用于设置周期性被执行的指令. 在Linux系统中,Linux任务调度的工作主要分为以下两类:1.系统执行的工作:系统周期性所要执行的工作,如备份系统数据.清理缓存2.个人执行的工作: ...

  6. PT的常用命令

    归纳常用的PT命令,便于工作中应用. 1. 报时序的命令 report_timing -from A -to  [get_clocks {clkA}]  -delay_type min report_ ...

  7. FreeType 矢量字体 测试移植(1)

    之前有做过 ascii 和汉字库的字体点阵在lcd上显示的例子,都是按照指定大小的字库的点阵来显示的,所以一但选定了字体文件后,就固定了大小,不可变化,当然也可以存放各种 大小的字体文件,但这样的话就 ...

  8. keycloak(转载)

    # 生成KEYSTORE.JKS keytool -genkeypair -alias keycloak.me -keyalg RSA -keystore keycloak.jks -validity ...

  9. vim指令大全

    Linux vi种 wq .wq!.x.q.q!区别   上面的命令只是在vi编辑命令中使用 wq:表示保存退出 wq!:表示强制保存退出 x:表示保存退出 wq和wq!的区别如下: 有些文件设置了只 ...

  10. linux查看mac地址

    1. ip addr show (ip address show .ip addr ) 查看本机ip和额外的一些信息 2.ifconfig -a  其中 HWaddr 就是mac地址 3.cat /s ...