物联网实战|Spring Boot+Netty搭建MQTT核心服务层
源码(sample-00-iot-core)
https://gitee.com/kcnf-iot/mqtt-boot/tree/master/sample-00
模块概述
是物联网平台的核心业务服务层,承上启下连接消息接入层(access)与上层业务应用。模块职责涵盖设备管理、设备认证、设备影子、规则引擎、时序数据存储等核心能力 核心定位:不直接处理网络协议,仅接收经过接入层解析后的业务消息,执行规则匹配、状态同步、数据持久化等业务逻辑
目录结构
sample-00-iot-core/
├── src/main/java/com/jysemel/iot/core/
│ ├── auth/ # 认证模块
│ │ ├── JwtAuthenticator.java # JWT认证
│ │ ├── Authenticator.java # 认证接口
│ │ └── PasswordAuthenticator.java # 密码认证
│ │
│ ├── config/ # 配置类
│ │ └── CoreTraceIdTaskExecutorAsyncConfig.java # 异步任务TraceId传递
│ │
│ ├── device/ # 设备管理
│ │ ├── DeviceRegistry.java # 设备注册表
│ │ ├── DeviceService.java # 设备服务接口
│ │ ├── DeviceInfo.java # 设备信息模型
│ │ └── impl/
│ │ └── SqliteDeviceService.java # SQLite设备服务实现
│ │
│ ├── listener/ # 事件监听
│ │ ├── DeviceStatusEventListener.java # 设备状态变更监听
│ │ ├── RuleEventListener.java # 规则事件监听
│ │ └── MessageEventListener.java # 消息事件监听
│ │
│ ├── rule/ # 规则引擎
│ │ ├── RuleEngine.java # 规则引擎接口
│ │ ├── RuleRepository.java # 规则仓储
│ │ ├── DroolsRuleEngine.java # Drools规则引擎实现
│ │ ├── ActionExecutor.java # 动作执行器
│ │ ├── RuleConfig.java # 规则配置
│ │ └── model/
│ │ ├── Rule.java # 规则业务模型
│ │ └── RuleDO.java # 规则实体
│ │
│ ├── shadow/ # 设备影子
│ │ ├── DeviceShadowManager.java # 设备影子管理接口
│ │ ├── ShadowStore.java # 影子存储接口
│ │ ├── ShadowSyncService.java # 影子同步服务
│ │ └── impl/
│ │ └── SqliteShadowStore.java # SQLite实现
│ │
│ └── storage/ # 时序存储
│ ├── TimeSeriesWriter.java # 时序写入接口
│ ├── TimeSeriesReader.java # 时序读取接口
│ ├── TimeSeriesDataDO.java # 时序数据实体
│ ├── TimeSeriesDataRepository.java # JPA仓储
│ └── impl/
│ └── SqliteTimeSeriesWriter.java # SQLite写入实现
│
└── src/main/resources/
└── db/
├── schema.sql # 建表脚本
└── init-data.sql # 初始化数据
核心模块详解
认证模块 (auth)
JwtAuthenticator
基于 JWT 的设备身份认证,继承 Authenticator 接口。 核心方法:
| 方法 | 说明 |
|---|---|
authenticate(productKey, deviceKey, password) | 验证设备身份,生成JWT |
generateToken(deviceInfo) | 生成JWT令牌 |
validateToken(token) | 验证JWT有效性 |
extractDeviceInfo(token) | 从令牌中提取设备信息 |
JWT Claims 结构:
{
"sub": "{productKey}/{deviceKey}", // 设备唯一标识
"productKey": "product-001",
"deviceKey": "device-001",
"iat": 1750051200, // 签发时间
"exp": 1750137600, // 过期时间(1天后)
"scope": ["publish", "subscribe"] // 权限范围
}
Authenticator 接口
public interface Authenticator {
/**
* 认证设备
* @return 认证成功返回设备信息,否则返回null
*/
DeviceInfo authenticate(String productKey, String deviceKey, String password);
/**
* 验证Token
*/
boolean validateToken(String token);
}
设备管理模块 (device)
DeviceRegistry
基于本地 ConcurrentHashMap 的设备注册中心,提供设备在线状态管理。 核心方法:
| 方法 | 说明 |
|---|---|
registerOnline(productKey, deviceKey) | 标记设备上线 |
registerOffline(productKey, deviceKey) | 标记设备离线 |
isOnline(productKey, deviceKey) | 查询设备是否在线 |
getOnlineDevices() | 获取所有在线设备列表 |
数据结构:
// 设备在线状态缓存
ConcurrentHashMap<String, Boolean> deviceOnlineCache; // key: "productKey:deviceKey"
// 在线时间记录
ConcurrentHashMap<String, Long> deviceOnlineTimeCache; // key: "productKey:deviceKey"
特性:
- 线程安全
- O(1) 时间复杂度查询
- 自动清理过期记录
DeviceInfo
设备信息模型,包含设备元数据。
| 字段 | 类型 | 说明 |
|---|---|---|
| productKey | String | 产品标识 |
| deviceKey | String | 设备标识 |
| deviceSecret | String | 设备密钥(加密存储) |
| deviceName | String | 设备名称 |
| status | String | 状态:ONLINE/OFFLINE/DISABLED |
| createdTime | LocalDateTime | 创建时间 |
设备影子模块 (shadow)
设备影子用于解决设备与云端的状态同步问题,支持期望状态(Desired)与上报状态(Reported)的差异计算。
DeviceShadowManager 接口
public interface DeviceShadowManager {
void initializeShadow(String productKey, String deviceKey);
Map<String, Object> getShadow(String productKey, String deviceKey);
void updateDesired(String productKey, String deviceKey, Map<String, Object> desired);
void updateReported(String productKey, String deviceKey, Map<String, Object> reported);
Map<String, Object> syncShadow(String productKey, String deviceKey);
void deleteShadow(String productKey, String deviceKey);
}
ShadowSyncService
设备影子同步服务实现,核心功能是维护期望状态与上报状态的差异(Delta)。
影子数据结构:
{
"productKey": "product-001",
"deviceKey": "device-001",
"version": 1,
"timestamp": 1750051200000,
"desired": {
"temperature": 25,
"mode": "auto"
},
"reported": {
"temperature": 23,
"mode": "manual"
},
"delta": {
"temperature": 25,
"mode": "auto"
}
}
Delta 计算逻辑:
Delta = Desired - Reported
只有期望值与上报值不同时,才会被加入 Delta。这意味着设备需要同步更新这些属性。
规则引擎模块 (rule)
规则引擎是核心业务能力,支持对设备消息进行条件匹配和动作执行。
RuleEngine 接口
public interface RuleEngine {
/**
* 初始化规则引擎
*/
void initialize();
/**
* 评估规则
* @param topic 消息主题
* @param payload 消息内容
* @return 匹配结果
*/
EvaluationResult evaluate(String topic, byte[] payload);
/**
* 添加规则
*/
void addRule(Rule rule);
/**
* 移除规则
*/
void removeRule(String ruleId);
}
DroolsRuleEngine
基于 Drools 规则引擎的实现。
Drools 特性:
- Rete 算法实现高效的规则匹配
- 支持复杂的条件表达式
- 规则热加载(动态更新规则库)
- 透传 TraceId 到规则执行上下文
初始化流程:
DroolsRuleEngine.initialize()
│
├── RuleRepository.loadAllRules() 加载规则
│
├── buildKieModule() 构建Drools模块
│
└── startChangeScanner() 启动规则变更扫描(可选)
Rule 模型
@Data
public class Rule {
private String ruleId; // 规则ID
private String ruleName; // 规则名称
private String ruleType; // 规则类型
private String status; // 状态:ENABLED/DISABLED
private Condition condition; // 触发条件
private List<Action> actions; // 执行动作列表
@Data
public static class Condition {
private String topic; // 监听主题
private String expression; // 条件表达式
private Map<String, String> params; // 参数
}
@Data
public static class Action {
private String actionType; // 动作类型:PUBLISH/SAVE_DB/SMS/EMAIL
private String target; // 目标(主题/服务地址等)
private Map<String, Object> params; // 动作参数
}
}
规则示例:
{
"ruleId": "rule-001",
"ruleName": "温度告警规则",
"ruleType": "TEMPERATURE_ALARM",
"status": "ENABLED",
"condition": {
"topic": "device/{deviceKey}/telemetry",
"expression": "temperature > 50"
},
"actions": [
{
"actionType": "PUBLISH",
"target": "cloud/alarm",
"params": {"level": "HIGH"}
},
{
"actionType": "SAVE_DB",
"params": {}
}
]
}
ActionExecutor
动作执行器,负责执行匹配成功的规则对应的动作。
支持的動作類型:
| 动作类型 | 说明 | 用途 |
|---|---|---|
| PUBLISH | 发布消息 | 触发后续流程 |
| SAVE_DB | 存储数据库 | 数据持久化 |
| SMS | 短信通知 | 告警通知 |
| 邮件通知 | 告警通知 | |
| HTTP | HTTP调用 | 外部系统集成 |
时序存储模块 (storage)
TimeSeriesWriter 接口
public interface TimeSeriesWriter {
/**
* 写入时序数据
* @param metric 度量名称
* @param tags 标签(维度)
* @param fields 字段(指标值)
* @param timestamp 时间戳
*/
void write(String metric, Map<String, String> tags, Map<String, Object> fields, long timestamp);
}
TimeSeriesDataDO
时序数据实体,SQLite 持久化。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | Long | 主键 |
| metric | String | 度量名称(temperature/humidity) |
| tags | String | 标签,格式:k1=v1,k2=v2 |
| fields | String | 字段,格式:k1=v1,k2=v2 |
| timestamp | Long | 时间戳(毫秒) |
SqliteTimeSeriesWriter
SQLite 实现,通过 @ConditionalOnProperty 实现存储模式切换。
事件监听模块 (listener)
DeviceStatusEventListener
监听设备状态变更事件,触发设备影子初始化。
@Component
public class DeviceStatusEventListener {
@EventListener
public void onDeviceOnline(DeviceOnlineEvent event) {
// 设备上线时初始化影子
shadowService.initializeShadow(event.getProductKey(), event.getDeviceKey());
}
}
RuleEventListener
监听消息事件,触发规则引擎评估。
@Component
public class RuleEventListener {
@EventListener
public void onMessage(MessageEvent event) {
// 触发规则评估
ruleEngine.evaluate(event.getTopic(), event.getPayload());
}
}
配置说明
核心配置 (application-core.yml)
core:
rule:
enabled: true # 启用规则引擎
engine: drools # 规则引擎类型
scan-packages: com.jysemel.iot.core.rule.drools # Drools规则包
device:
cache:
enabled: true # 启用设备缓存
ttl: 3600 # 缓存TTL(秒)
shadow:
sync:
enabled: true # 启用影子同步
storage:
mode: sqlite # 存储模式:sqlite/mysql
异步任务 TraceId 配置
配置异步任务的 TraceId 透传,确保异步日志链路完整。
@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
TaskExecutor executor = new TaskExecutor();
executor.setTaskDecorator(new TraceIdTaskDecorator());
return executor;
}
}
数据库设计
建表脚本
-- 设备表
CREATE TABLE IF NOT EXISTS devices (
id INTEGER PRIMARY KEY AUTOINCREMENT,
product_key VARCHAR(100) NOT NULL,
device_key VARCHAR(100) NOT NULL,
device_name VARCHAR(200),
device_secret VARCHAR(500),
status VARCHAR(20) DEFAULT 'OFFLINE',
created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(product_key, device_key)
);
-- 规则表
CREATE TABLE IF NOT EXISTS rules (
rule_id VARCHAR(100) PRIMARY KEY,
rule_name VARCHAR(200) NOT NULL,
rule_type VARCHAR(50),
status VARCHAR(20) DEFAULT 'ENABLED',
condition_expression TEXT,
condition_topic VARCHAR(500),
condition_params TEXT,
actions TEXT,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 设备影子表
CREATE TABLE IF NOT EXISTS device_shadows (
id INTEGER PRIMARY KEY AUTOINCREMENT,
product_key VARCHAR(100) NOT NULL,
device_key VARCHAR(100) NOT NULL,
shadow_data TEXT,
version INTEGER DEFAULT 1,
created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(product_key, device_key)
);
-- 时序数据表
CREATE TABLE IF NOT EXISTS time_series_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
metric VARCHAR(100) NOT NULL,
tags VARCHAR(500),
fields TEXT,
timestamp BIGINT NOT NULL,
INDEX idx_metric_timestamp (metric, timestamp)
);
消息处理流程
消息接入层 (access)
│
▼ 发布 MessageEvent
│
核心服务层 (core)
│
├── DeviceStatusEventListener
│ └── 更新设备状态 → DeviceRegistry
│
├── RuleEventListener
│ └── 触发规则评估 → RuleEngine.evaluate()
│ │
│ ▼
│ DroolsRuleEngine(规则匹配)
│ │
│ ▼
│ ActionExecutor(执行动作)
│ │
│ ├── StorageWriter(存储数据)
│ ├── DeviceShadowManager(更新影子)
│ └── 其他Action
│
└── ShadowSyncService
└── 同步影子状态
扩展指南
添加新的认证方式
实现 Authenticator 接口即可:
@Component
public class LdapAuthenticator implements Authenticator {
@Override
public DeviceInfo authenticate(String productKey, String deviceKey, String password) {
// LDAP 认证逻辑
}
@Override
public boolean validateToken(String token) {
// Token 验证逻辑
}
}
添加新的规则动作
在 ActionExecutor 中添加新的动作处理分支:
@ActionHandler("CUSTOM_ACTION")
public void handleCustomAction(Action action, EvaluationResult result) {
// 自定义动作处理逻辑
}
切换存储后端
通过 @ConditionalOnProperty 添加新的存储实现:
@Component
@ConditionalOnProperty(name = "storage.mode", havingValue = "mysql")
public class MySqlTimeSeriesWriter implements TimeSeriesWriter {
// MySQL 实现
}
总结
sample-00-iot-core 模块作为物联网平台的核心服务层,具备以下核心能力:
| 能力 | 说明 |
|---|---|
| 设备管理 | 设备注册、状态管理、在线追踪 |
| 身份认证 | JWT/密码双认证机制 |
| 设备影子 | Desired/Reported 状态同步,Delta 计算 |
| 规则引擎 | Drools 实现,支持复杂条件匹配和动作执行 |
| 时序存储 | 支持多存储后端(SQLite/MySQL) |
| 事件驱动 | Spring Event 机制实现模块解耦 |
| 链路追踪 | TraceId 透传到异步任务 |
模块设计遵循以下原则:
- 可扩展:通过接口和策略模式支持多实现
- 可配置:通过
@ConditionalOnProperty实现功能开关 - 可追踪:完整的日志和 TraceId 链路
- 事件化:通过 Spring Event 实现松耦合
