Spring Boot是Spring框架的一个扩展,旨在简化Spring应用的初始搭建和开发过程。它提供了大量的自动配置和可插拔的组件,使得开发人员能够快速构建基于Java的应用程序。
- 快速集成:通过将EMQX客户端集成到Spring Boot应用中,可以快速地实现物联网消息的发布和订阅功能。这种集成方式利用了Spring Boot的自动配置和依赖注入特性,使得代码的编写和维护更加简单。
- 灵活性:Spring Boot和EMQX的集成提供了高度的灵活性。开发人员可以根据项目的需求选择合适的MQTT客户端库(如Paho MQTT客户端库),方便在应用程序中进行使用。
- 统一管理:统一管理EMQX链接配置和Maven版本是确保项目稳定性和可维护性的重要手段。
组件设计
代码结构
组件主要分为四部分
MQTT客户端上下文:
- 连接管理:管理MQTT连接及断开连接功能。
- 订阅列表:客户端订阅的所有主题及其对应的QoS(服务质量)级别。
- 推送消息:发送消息到EMQX中。
MQTT配置信息:
- 包括MQTT代理的地址、端口、客户端ID、用户名、密码、订阅topic等。
MQTT消息监听接口:
- 监听topic。
- 处理消息接口。
MQTT消息推送:
- 长链接的消息推送入口。
- Http方式消息推送入口。
代码解析
UML图
模块介绍
MQTT客户端上下文(IMqttClient):
客户端上下文主要管理MQTT连接信息、MQTT连接、断开连接、消息推送、主题订阅等。
public interface IMqttClient {
MqttClientApp connect();
boolean isConnect();
boolean disconnect();
MqttResp publish(MqttReq request);
MqttConfig getMqttConfig();
}
创建MQTT客户端:
选择使用Vert.x的MQTT客户端作为客户端,因为Vert.x提供了高性能的事件驱动编程模型,非常适合处理像MQTT这样的异步消息协议。
this.mqttClient = MqttClient.create(vertx, createMqttClientOptions());
private MqttClientOptions createMqttClientOptions() {
MqttClientOptions options = new MqttClientOptions();
options.setClientId("micro-client-" + RandomStringUtils.randomAlphanumeric(17));
options.setMaxMessageSize(100_000_000);
options.setKeepAliveInterval(60);
options.setPassword(mqttConfig.getPassword());
options.setUsername(mqttConfig.getUsername());
options.setSsl(mqttConfig.getListenerInfos().isSsl());
options.setReconnectInterval(RECONNECT_INTERVAL);
options.setReconnectAttempts(Integer.MAX_VALUE);
return options;
}
建立连接:
mqttClient.connect(mqttConfig.getListenerInfos().getPort(), mqttConfig.getListenerInfos().getHost(),
s -> {
if (s.succeeded()) {
log.info("IMqttClient connect success.");
subscribe();
countDownLatch.countDown();
} else {
log.error("IMqttClient connect fail: ", s.cause());
if (s.cause() != null) {
vertx.setTimer(RECONNECT_INTERVAL, handler -> this.start());
}
}
断开连接:
public boolean disconnect() {
if (!isConnect()) {
log.warn("IMqttClient no connect");
return false;
}
vertx.undeploy(deploymentID(), (handler) -> {
if (handler.succeeded()) {
log.info("undeploy success");
} else {
log.warn("undeploy fail, {}. ", handler.cause().getMessage(), handler.cause());
}
});
return true;
}
推送消息MqttResp publish(MqttReq request)
@Data
public class MqttReq extends Req {
private String messageContent;
}
@Data
public class Req {
protected String messageId = RandomStringUtils.randomNumeric(12);
protected final Long createdTime = System.currentTimeMillis();
protected String topic;
private Integer qos;
protected Map<String, Object> headers = Maps.newHashMap();
protected Map<String, Object> data = Maps.newHashMap();
}
MQTT配置信息(MqttConfig):
MQTT代理的地址、端口、客户端ID、用户名、密码、订阅topic等
字段 | 描述 | 类型 |
---|---|---|
address | 对外域名 | String |
host | 内网IP | String |
port | TCP端口 | Integer |
username | 名称 | String |
password | 密码 | String |
subscribeTopics | 订阅topic | List |
MQTT消息推送(IMqttApi):
提供服务统一推送消息到brocker接口。包含了Http短连接、TCP长连接两种方式推送消息。
public interface IMqttApi {
MqttResp httpPublish(MqttReq request);
MqttResp tcpPublish(MqttReq request);
}
推送消息MqttReq参数说明:
字段 | 描述 | 类型 | 是否必填 |
---|---|---|---|
topic | 消息主题 | String | 是 |
qos | 消息等级 | Integer | 是 |
messageContent | 消息体 | String | 不为空,则下面的参数不传 |
messageId | 消息唯一id | String | messageContent为空时 |
createdTime | 时间戳 | Long | messageContent为空时 |
headers | 请求头 | Map<String, Object> | messageContent为空时 |
data | 消息内容 | Map<String, Object> | messageContent为空时 |
MQTT消息监听接口(IMqttListener):
定义监听消息接口。
public interface IMqttListener {
String getTopic();
void onMessage(Message message);
}
提供两个方法:
getTopic():监听者的topic,具备路由功能。
void onMessage(Message message):MQTT消息处理接口
监听消息Message:
public interface Message {
//设备id
String getDeviceId();
//设备归属产品类
String getProductId();
long getTimestamp();
String getSn();
Map<String, Object> getData();
Map<String, Object> getHeaders();
Message addHeader(String var1, Object var2);
Message addHeaderIfAbsent(String var1, Object var2);
Message removeHeader(String var1);
//消息体
String getMessageContent();
}
其它:
InitMqttClientInfo MQTT客户端在启动前需要初始化内容:
public interface InitMqttClientInfo {
void initInfo();
}
MQTT客户端SpringBoot Start
在Spring Boot应用程序中集成MQTT客户端是一个常见的需求,特别是当你需要处理IoT(物联网)设备的数据交换时。Spring Boot并没有直接提供MQTT客户端的starter,但你可以很容易地通过添加MQTT客户端库(到你的Spring Boot项目中来实现这一功能。
@Slf4j
@Configuration
@ConditionalOnProperty(value = "mqtt.sdk.enable", havingValue = "true")
public class MqttClientAutoConfiguration {
}
创建并注入MQTT客户端配置
结合Spring Boot 注解@ConfigurationProperties 实现MQTT配置信息注入
@Bean
@ConfigurationProperties(prefix = "mqtt.sdk")
public MqttConfig mqttConfig() {
return new MqttConfig();
}
创建并注入一个默认消息监听器
@Bean
@ConditionalOnMissingBean
public IMqttListener mqttListener() {
return new IMqttListener() {
@Override
public String getTopic() {
return "#";
}
@Override
public void onMessage(Message message) {
LoggerFactory.getLogger(IMqttListener.class)
.info("[默认消息监听器]接收到消息. Message[{}]", JSON.toJSONString(message));
}
};
}
创建并注入MQTT客户端
现在,你可以在Spring Boot应用的任何地方注入MqttClient Bean,并使用它来订阅主题、发布消息等。
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean({MqttConfig.class, IMqttListener.class})
public IMqttClient mqttClient(MqttConfig emqXConfig, List<IMqttListener> IMqttListenerList, Environment environment) {
String serviceName = environment.getProperty("spring.application.name");
String env = environment.getProperty("spring.profiles.active");
IMqttClient mqttClient = new MqttClientApp(emqXConfig, IMqttListenerList,serviceName+"-"+env);
Executors.newSingleThreadExecutor().execute(() -> mqttClient.connect().sync());
Runtime.getRuntime().addShutdownHook(new Thread(mqttClient::mqttStop));
return mqttClient;
}
创建并注入MQTT对外推送消息类
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean({MqttConfig.class, IMqttClient.class})
public IMqttApi mqttApi(IMqttClient IMqttClient) {
return new MqttApiImpl(IMqttClient);
}
Spring Boot SPI机制来集成MQTT自动配置
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.gitee.xmhzzz.component.mqtt.client.MqttClientAutoConfiguration
(Spring Boot 3.0 以上版本)
其它
关注公众号【 java程序猿技术】获取EMQX实践系列文章