first commit
This commit is contained in:
7
.codacy.yml
Normal file
7
.codacy.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
exclude_paths:
|
||||
- '.mvn/**'
|
||||
- '**.md'
|
||||
- '**/**.md'
|
||||
- 'mica-mqtt-example/**'
|
||||
- '**/test/**'
|
||||
21
.editorconfig
Normal file
21
.editorconfig
Normal file
@@ -0,0 +1,21 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
# 空格替代Tab缩进在各种编辑工具下效果一致
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.java]
|
||||
indent_style = tab
|
||||
|
||||
[*.{json,yml}]
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
insert_final_newline = false
|
||||
trim_trailing_whitespace = false
|
||||
22
.gitattributes
vendored
Normal file
22
.gitattributes
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# All text files should have the "lf" (Unix) line endings
|
||||
* text eol=lf
|
||||
# windows cmd shoud have the "crlf" (Win32) line endings
|
||||
*.cmd eol=crlf
|
||||
|
||||
# Explicitly declare text files you want to always be normalized and converted
|
||||
# to native line endings on checkout.
|
||||
*.java text
|
||||
*.js text
|
||||
*.css text
|
||||
*.html text
|
||||
*.properties text
|
||||
*.xml text
|
||||
*.yml text
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.ttf binary
|
||||
*.jar binary
|
||||
*.db binary
|
||||
*.xdb binary
|
||||
65
.gitignore
vendored
Normal file
65
.gitignore
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
### gradle ###
|
||||
.gradle
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
### STS ###
|
||||
.settings/
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
bin/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
!.idea/icon.png
|
||||
.idea
|
||||
out/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
nbproject/private/
|
||||
build/
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
|
||||
### vscode ###
|
||||
.vscode
|
||||
|
||||
### maven ###
|
||||
target/
|
||||
*.war
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar
|
||||
*.tar.gz
|
||||
|
||||
# logs #
|
||||
logs
|
||||
|
||||
# temp ignore
|
||||
*.log
|
||||
*.cache
|
||||
*.diff
|
||||
*.patch
|
||||
*.tmp
|
||||
*.java~
|
||||
*.properties~
|
||||
*.xml~
|
||||
|
||||
# system ignore
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
Servers
|
||||
.metadata
|
||||
upload
|
||||
gen_code
|
||||
|
||||
# Flattened pom
|
||||
.flattened-pom.xml
|
||||
/**/.flattened-pom.xml
|
||||
625
CHANGELOG.md
Normal file
625
CHANGELOG.md
Normal file
@@ -0,0 +1,625 @@
|
||||
# 变更记录
|
||||
|
||||
## 发行版本
|
||||
|
||||
### v2.5.9 - 2025-11-29
|
||||
|
||||
- :sparkles: mica-mqtt-client solon 和 spring 插件 MQTT 客户端订阅中的 beanName 支持占位符解析,感谢 `@tan90` 反馈(gitee #ID7PF6)
|
||||
- :sparkles: mica-mqtt-server ClientInfo 添加 SSL 和 WebSocket 标识。
|
||||
- :arrow_up: 升级到 mica-net 1.2.4,优化 sse,修复 jackson3 方法错误。
|
||||
|
||||
### v2.5.7 - 2025-11-07
|
||||
|
||||
- :sparkles: mica-mqtt-server 新增 `/api/v1/stats/sse` 接口,支持通过 SSE 实时获取服务器统计信息
|
||||
- :sparkles: example 升级到 solon 3.7.0 更改相关依赖命名规则
|
||||
- :arrow_up: 升级 mica-net 到 1.2.2 支持 snack4 json 序列化,内存优化和消息发送性能优化
|
||||
- :bug: mica-mqtt-server-solon-plugin 移除 MqttServerConfiguration bean MqttFunctionManager 的 static 修饰符
|
||||
|
||||
### v2.5.6 - 2025-10-27
|
||||
- :bug: 修复 MQTT 解码器中的缓冲区读取问题,修复解码异常重连后无法恢复的问题。(所有版本)
|
||||
- :arrow_up: 升级 mica-net 到 1.2.1,修改慢包读取 (gitee #ID3IAU),影响范围(2.5.5)
|
||||
|
||||
### v2.5.5 - 2025-10-10
|
||||
- :sparkles: mqtt-client 添加通过 `Consumer<MqttWillPublishProperties>` 函数式接口自定义遗嘱属性
|
||||
- :sparkles: mqtt-client 添加直接使用 MqttPublishBuilder 发布消息
|
||||
- :sparkles: mqtt-client 添加 disconnectBeforeStop 配置(默认 true),断开连接前是否发送 disconnect 消息,感谢 `@steven` 反馈(gitee #ICXY4A)
|
||||
- :sparkles: mica-mqtt-server 使用 ConcurrentHashMap 替代 IntObjectHashMap,优化内存会话管理
|
||||
- :sparkles: mica-mqtt-server-spring-boot-starter bean 加载顺序优化,避免出现提示
|
||||
- :arrow_up: 升级 mica-net 到 1.2.0,调整慢包攻击规则和支持 jackson3,感谢 `@well` 反馈(gitee #ICXF5N)
|
||||
|
||||
### v2.5.4 - 2025-08-29
|
||||
- :sparkles: mica-mqtt-server 使用前缀树管理 MQTT 订阅。
|
||||
- :sparkles: mica-mqtt-server 心跳超时小于等于0时,不开启心跳检测。(不建议这样使用)感谢 `@刘业兴` 反馈(gitee #ICTT2V)
|
||||
- :sparkles: mica-mqtt-server solon 和 spring 插件,将 `@MqttServerFunction` 统一到 mica-mqtt-common 包中,不兼容。
|
||||
- :sparkles: mica-mqtt-server solon 和 spring 插件,`@MqttServerFunction` 增加 topic 变量解析功能,支持解析 Map<String, String> 类型的 topic 中的 ${topicVars} 变量参数。
|
||||
- :sparkles: mica-mqtt-client solon 和 spring 插件,将 `@MqttClientSubscribe` 统一到 mica-mqtt-common 包中,MqttClientTemplate 中的 `DEFAULT_CLIENT_TEMPLATE_BEAN` 常量定义移动到了 `@MqttClientSubscribe`,不兼容。
|
||||
- :sparkles: mica-mqtt-client solon 和 spring 插件,`@MqttClientSubscribe` 注解订阅,增加 topic 变量解析功能,支持解析 Map<String, String> 类型的 topic 中的 ${topicVars} 变量参数。
|
||||
- :sparkles: mica-mqtt-codec 移除了 MqttCodecUtil 中的 `isValidPublishTopicName` 方法 ,直接使用 `isTopicFilter` 校验发布主题名称是否包含通配符。
|
||||
- :sparkles: mica-mqtt-codec 包调整,重命名类名、方法名,重构 MQTT 消息构建器类(为后续方便做准备),不兼容。
|
||||
- :sparkles: mica-mqtt-common `TopicUtil` 调整 validateTopicFilter 方法,移除了对空白字符的校验。(注意:emqx 支持使用空白字符,mosquitto 不支持)。
|
||||
- :sparkles: `TopicUtil` 和 `MqttCodecUtil` 增加对 topic 中空白符的日志提示。感谢 `@长草颜团子` 反馈(gitee #26)
|
||||
|
||||
### v2.5.3 - 2025-08-03
|
||||
- :sparkles: mica-mqtt-server-spring-boot-starter 支持注解 `@MqttServerFunction` 监听
|
||||
- :sparkles: mica-mqtt-server-solon-plugin 支持注解 `@MqttServerFunction` 监听
|
||||
- :sparkles: mica-mqtt-client-solon-plugin 更新 `solon-configuration-metadata.json`
|
||||
- :sparkles: mica-mqtt-codec `ReasonCode` 统一移动到 `codes` 包(不影响老用户升级)
|
||||
|
||||
### v2.5.2 - 2025-07-27
|
||||
- :bug: mica-mqtt-server 修复启动报错(影响范围 `2.5.0` ~ `2.5.1`),感谢 `CoderKK` 反馈(gitee #ICOQ3Q)
|
||||
|
||||
### v2.5.1 - 2025-07-24
|
||||
- :sparkles: mica-mqtt-server 优化 sse mcp,添加 sse 心跳
|
||||
- :sparkles: mica-mqtt-client 内置 ssl SNI 支持,感谢 `sword007`、`@TomatoLay` 反馈(gitee #ICKBAY #ICEANP)
|
||||
- :sparkles: mica-mqtt-client 支持多网卡下指定网卡 `bindIp`(网卡对应IP)和 `bindNetworkInterface`(网卡名) 配置(2个方法使用任意一个即可)。感谢 `@iovera` 反馈(gitee #ICO699)
|
||||
- :bug: mica-mqtt-client 高CPU下 packetId 生成超限,感谢 `@火焰之魂` 反馈(gitee #ICLXC3)
|
||||
|
||||
### v2.5.0 - 2025-07-12
|
||||
- :sparkles: mica-mqtt X AI,mica-mqtt-server 支持大模型 mcp
|
||||
- :sparkles: mica-mqtt-server 支持同时配置多协议支持,拆分 `http(默认端口18083)` 和 `websocket(默认端口8083)`,使统计更加准确
|
||||
- :sparkles: mica-mqtt-server spring、solon 插件删除不推荐使用的 `EventMqttMessageListener`
|
||||
- :sparkles: mica-mqtt-server 调整保留消息的规则,支持 `$retain` 带存储周期的保留消息 感谢 `@tan90` pr #ICB9I2 #23
|
||||
- :sparkles: mica-mqtt-client 删除 `IMqttClientMessageIdGenerator` 接口合入 `IMqttClientSession` 接口
|
||||
- :sparkles: mica-mqtt-client 默认 mqtt5.0,cleanSession 改为 cleanStart 感谢 `@tan90` 反馈 (gitee #IBKKAG)
|
||||
- :sparkles: mica-mqtt-client MqttClient 和 MqttClientTemplate 支持通过代理接口来进行Publish 感谢 `@galaxy-sea` pr (github #100)
|
||||
- :sparkles: mica-mqtt-codec 完全同步成私服版,将 MqttProperties 内部类拆解出来,方便使用
|
||||
- :sparkles: mica-mqtt client、server solon 插件添加对 [Solon IDEA](https://plugins.jetbrains.com/plugin/21380-solon) 插件配置提示支持。
|
||||
- :sparkles: 移除 mica-mqtt-broker,未来重构
|
||||
- :sparkles: 统一参数命名,userName 统一为 username。
|
||||
- :sparkles: 优化部分日志,使用中文,方便大家排查问题
|
||||
|
||||
### v2.4.9 - 2025-06-27
|
||||
- :bug: mqtt server 修复 gitee #ICID15
|
||||
|
||||
### v2.4.8 - 2025-06-20
|
||||
- :sparkles: mica-mqtt-client-spring-boot-starter `MqttClientSubscribeDetector` bean 配置改成 `static` 方法。
|
||||
- :sparkles: mica-mqtt-server 调整保留消息标志位的规则。
|
||||
|
||||
### v2.4.7 - 2025-06-02
|
||||
- :sparkles: mica-mqtt-client、mica-mqtt-server publish相关接口支持object发送 (github #98) 感谢 `@galaxy-sea` 贡献
|
||||
- :sparkles: mica-mqtt-client 调整 MqttClient#reconnect 策略,(gitee #IBY5LQ)感谢 `@拉风的CC` 反馈。
|
||||
- :sparkles: mica-mqtt-codec MqttCodecUtil#isTopicFilter 代码优化改为逆序循环
|
||||
- :sparkles: mica-mqtt-codec 代码优化详见: https://github.com/netty/netty/pull/15227
|
||||
- :sparkles: mica-net-http http api 响应头 name 不转换成小写
|
||||
- :wrench: mica-mqtt-common 更新 `module-info.java` 添加序列化模块
|
||||
- :bug: mica-net-utils DefaultThreadFactory 不应该共用。
|
||||
|
||||
### v2.4.6 - 2025-05-19
|
||||
- :sparkles: mica-mqtt-client-spring-boot-starter MqttDeserializer 接口重写,支持泛型调用 (github #95) 感谢 `@galaxy-sea` 贡献
|
||||
- :sparkles: mica-mqtt-client 批量订阅兼容 mqtt 3.1.1 部分 broker 只返回一个 reasonCode 的情况。感谢 `@Jacky` 反馈
|
||||
- :sparkles: mica-mqtt-server-solon-plugin 添加对 metrics 指标的支持
|
||||
- :sparkles: mica-mqtt-client-solon-plugin 注解订阅支持自定义序列化(默认 json 序列化)和泛型
|
||||
|
||||
### v2.4.5 - 2025-05-06
|
||||
- :sparkles: mica-mqtt-client-spring-boot-starter 的 `@MqttClientSubscribe` 注解支持自定义反序列化。
|
||||
- :sparkles: 优化代码 Spring Boot Client 可以自定义 MqttClientSubscribeDetector github #90 感谢 `@galaxy-sea` 贡献
|
||||
- :sparkles: 升级 mica-net 到 1.1.6,解决 eclipse paho mqtt websocket client 连接 mica-mqtt server 报错
|
||||
- :sparkles: 依赖调整,减少 example 示例项目的安全提示
|
||||
- :bug: 解决服务端重启时 client有消息发送,导致 client 无法正常重连 gitee #IC4DWT 感谢 `@wtjperi2003` 反馈
|
||||
- :bug: 同步 netty mqtt codec Fix the assignment error of maxQoS parameter in ConnAck Properties
|
||||
|
||||
### v2.4.4 - 2025-04-13
|
||||
- :sparkles: mica-mqtt-server,更好的兼容 Android 环境。github #81 感谢 `@KittenBall` 的联调测试。
|
||||
|
||||
### v2.4.3 - 2025-03-23
|
||||
- :sparkles: Central Portal 开始支持 Snapshots(仅存储90天,需尽快切到最新的正式版),dev 分支提交后 Github action 自动发布快照版。
|
||||
- :sparkles: 精简,删除没有用到的代码,下沉到 mica-net。
|
||||
- :sparkles: mica-mqtt-client 添加 heartbeatMode 和 heartbeatTimeoutStrategy,用于某些弱网场景 gitee #IBSMZ7 感谢 `@拉风的CC` 反馈。
|
||||
- :sparkles: mica-mqtt-server 默认依赖上 mica-net-http,不再需要手动添加依赖,简化使用。
|
||||
- :sparkles: mica-mqtt-server-spring-boot-starter MqttServerTemplate 暴露 `getMqttServer()` 方法,方便使用。
|
||||
- :sparkles: mica-mqtt-server-spring-boot-starter 兼容存在 `MeterRegistry` 类,但是 `MeterRegistry` bean 不存在的情况。gitee #IBLBCY 感谢 `@xxg` 反馈。
|
||||
|
||||
### v2.4.2 - 2025-01-24
|
||||
- :sparkles: mica-mqtt-client Spring Boot stater 和 solon 插件添加工作线程数配置 `bizThreadPoolSize` (默认:2,如果消息量大,业务复杂处理慢,例如做emqx消息转发处理,可调大此配置)。
|
||||
- :sparkles: mica-mqtt-client Spring Boot stater 和 solon 插件添加 MQTT5.0 的 `sessionExpiryIntervalSecs` 配置 gitee #IBIE27 感谢 `@cyber` 反馈。
|
||||
- :sparkles: mica-mqtt-client 调整重连重新订阅逻辑,Spring Boot stater 和 solon 插件 `@MqttClientSubscribe` 注解订阅,保留 session 重连时不丢失消息 gitee #IBIE27 感谢 `@cyber` 反馈。
|
||||
- :sparkles: mica-mqtt-client DefaultMqttClientSession 移除 `final` 修饰,方便继承自定义。
|
||||
- :sparkles: mica-mqtt-client 将 clientId 绑定到 context 上,可以使用 `context.getId()` 获取,方便多 mqtt client 实例下使用,gitee #IBHHB1 感谢 `@cv` 反馈。
|
||||
- :sparkles: mica-mqtt-server proxy 代理协议简化,已测底抽象到 mica-net。
|
||||
- :sparkles: mica-mqtt-common 调整 `TopicUtil`,支持原生 Android,gitee #IBJBFL 感谢 `@DeanNode` 反馈。
|
||||
- :sparkles: mica-mqtt-server 默认的 nodeName 改为随机 `nanoId`,支持原生 Android,gitee #IBJBFL 感谢 `@DeanNode` 反馈。
|
||||
- :sparkles: 将 MqttServerCustomizer 和 MqttClientCustomizer 抽到 mica-mqtt-server、mica-mqtt-client 方便组件封装,需要调整包名,请先将老的包导入删除,idea 会自动引入新的包。
|
||||
- :bug: mica-mqtt-client-spring-boot-starter 修复 Spring Boot 3.2 启动时出现警告 gitee #IBITP5 感谢 `@cyber` 反馈。
|
||||
|
||||
### v2.4.2-M2 - 2025-01-23
|
||||
- :sparkles: mica-mqtt-client Spring boot stater 和 solon 添加工作线程数配置 `bizThreadPoolSize` 。
|
||||
- :sparkles: mica-mqtt-server proxy 代理简化,已测底抽象到 mica-net。
|
||||
- :sparkles: mica-mqtt-common 调整 TopicUtil,支持原生 Android,gitee #IBJBFL 感谢 `@DeanNode` 反馈。
|
||||
- :sparkles: mica-mqtt-server nodeName 改为随机 nanoId ,支持原生 Android,gitee #IBJBFL 感谢 `@DeanNode` 反馈。
|
||||
- :bug: mica-mqtt-client-spring-boot-starter 修复 Spring Boot 3.2 启动时出现警告 gitee #IBITP5 感谢 `@cyber` 反馈。
|
||||
- :arrow_up: 升级 mica-net 到 1.0.12,tcp server 如果非 debug 不开启版本信息等打印,方便支持 Android(Android 下没有 ManagementFactory)
|
||||
|
||||
### v2.4.2-M1 - 2025-01-17
|
||||
- :sparkles: mica-mqtt client 调整重连重新订阅的逻辑 gitee #IBIE27 感谢 `@cyber` 反馈。
|
||||
- :sparkles: mica-mqtt client solon 和 spring boot 插件添加 MQTT5.0 的 sessionExpiryIntervalSecs 配置 gitee #IBIE27 感谢 `@cyber` 反馈。
|
||||
- :sparkles: mica-mqtt client DefaultMqttClientSession 移除 final 修饰,方便继承自定义。
|
||||
- :sparkles: mica-mqtt client 将 clientId 绑定到 context 上,可以使用 `context.getId()` 获取,gitee #IBHHB1 感谢 `@cv` 反馈。
|
||||
- :sparkles: 将 MqttServerCustomizer 和 MqttClientCustomizer 抽到 mica-mqtt-server、mica-mqtt-client 方便组件封装 gitee #IBIJDF 感谢 `@cyber` 反馈
|
||||
|
||||
### v2.4.1 - 2025-01-04
|
||||
- :sparkles: mqtt server 统一 topic 订阅、发布认证日志方便排查问题。
|
||||
- :sparkles: mqtt server 添加 PROXY protocol v1 支持,nginx 可开启 tcp proxy_protocol on; 时转发源 ip 信息。
|
||||
- :memo: 修复文档 maven 坐标错误。
|
||||
- :bug: 修复 spring boot 项目使用全局懒加载 topic无法订阅 gitee #IBFIV8 感谢 `@xixuanhao` 反馈
|
||||
|
||||
### v2.4.0 - 2024-12-07
|
||||
- :sparkles: http api 添加 `stats`、`clients` 列表和 `client详情` 接口。
|
||||
- :sparkles: MqttServer 和 MqttServerTemplate 添加 `getClientInfo` `getClients` 系列客户端信息接口。
|
||||
- :sparkles: MqttServer 和 MqttServerTemplate 添加 `getSubscriptions` 获取客户端订阅列表接口。
|
||||
- :sparkles: MqttServer 和 MqttServerTemplate 添加 `getStat` 统计接口。
|
||||
- :truck: 调整 maven groupId `net.dreamlu` 到新的 `org.dromara.mica-mqtt`。
|
||||
- :truck: 调整包名 `net.dreamlu.iot.mqtt` 到新的 `org.dromara.mica.mqtt`,其他均保持不变。
|
||||
- :truck: 切换到 central sonatype,central sonatype 不支持快照版,mica-mqtt 不再发布快照版。
|
||||
- :bug: 修复订阅发送时机问题 gitee #IB72L6 感谢 `@江上烽` 反馈
|
||||
|
||||
### v2.4.0-M2 - 2024-12-01
|
||||
- :sparkles: mica-mqtt-server 暴露获取 clientId 和状态统计接口
|
||||
- :bug: 修复订阅发送时机问题 gitee #IB72L6 感谢 `@江上烽` 反馈
|
||||
|
||||
### v2.4.0-M1 - 2024-11-24
|
||||
- :sparkles: 调整 groupId 到 org.dromara.mica-mqtt
|
||||
- :truck: 切换包名到 org.dromara
|
||||
- :truck: 切换到 central sonatype, central sonatype 不支持快照版
|
||||
|
||||
### v2.3.9 - 2024-11-16
|
||||
- :sparkles: ssl 支持 **PKCS12** 证书,根据文件后缀自动判断 `.jks、.keystore` 识别为 **JKS证书**,`.p12、.pfx` 识别成 **PKCS12证书**。其他默认成**JKS**
|
||||
- :sparkles: 优化 **Solon** 版本依赖(仅编译依赖),兼容 `2.8.0` 和 `2.8.0` 以上版本。
|
||||
|
||||
### v2.3.8 - 2024-09-26
|
||||
- :sparkles: 升级到 solon v3, 调整 solon 版本兼容
|
||||
- :bug: mica-net 心跳支持了 `keepAliveBackoff`,mica-mqtt 漏改规则(影响范围:mica-mqtt server 开源版,私服版无此问题。) gitee #IAW9FC 感谢 `tan90` 反馈。
|
||||
|
||||
### v2.3.7 - 2024-09-22
|
||||
- :sparkles: 优化 Mqtt server starter 添加 schedule 系列方法。
|
||||
- :sparkles: MqttClient schedule 系列方法下层到底层,方法改造。
|
||||
|
||||
### v2.3.6 - 2024-09-14
|
||||
- :sparkles: mica-mqtt server 和 client 优化 stop,支持 stop 后重新配置再启动(注意:需要重新配置,因为老的线程池已经停止)。
|
||||
- :sparkles: mica-mqtt server 和 client Spring boot starter 支持 Spring boot devtools 热启动。
|
||||
- :sparkles: `FastJsonMessageSerializer` 重构为 `JsonMessageSerializer`。
|
||||
- :sparkles: 添加 `module-info.java` 方便模块化。
|
||||
|
||||
### v2.3.5 - 2024-09-01
|
||||
- :sparkles: 新增 `SSLEngineCustomizer`,用于自定义 tls 协议版本和加密套件。
|
||||
- :sparkles: !20 修改了 solon 插件的默认配置数值,感谢 `@peigen` pr。
|
||||
|
||||
### v2.3.4 - 2024-08-10
|
||||
- :sparkles: mica-mqtt 合入 `mica-mqtt-client-solon-plugin` 和 `mica-mqtt-server-solon-plugin` 感谢 `@peigenlpy`
|
||||
- :sparkles: jfinal 插件重命名为 `mica-mqtt-client-jfinal-plugin` 和 `mica-mqtt-server-jfinal-plugin`
|
||||
- :bug: mica-mqtt-server 修复分组订阅删除,感谢 `@tangjj` 反馈。
|
||||
|
||||
### v2.3.3 - 2024-07-22
|
||||
- :sparkles: mica-mqtt-server 可停止,同步捐助版。
|
||||
- :sparkles: mica-mqtt-server 添加 schedule 系列方法,同步捐助版。
|
||||
- :sparkles: mica-mqtt 代码优化 TopicUtil 优化 getTopicFilter 方法。
|
||||
- :sparkles: mica-mqtt 优化 AckTimerTask 和 retry 重发日志。gitee #IABQ7L 感谢 `@tan90` 反馈。
|
||||
- :sparkles: mica-mqtt-client-spring-boot-starter 更加方便自定义 MqttClientTemplate。
|
||||
- :sparkles: mica-mqtt-client-spring-boot-starter MqttClientTemplate 暴露更多方法,方便使用。
|
||||
- :sparkles: mica-mqtt-example 添加 ssl 测试代码
|
||||
- :bug: mica-mqtt-client 修复 ssl 服务端重启问题 gitee #IA9FFW #IAEHOD 感谢 `@geekerstar` `@hangrj` 反馈。
|
||||
|
||||
### v2.3.1 - 2024-06-25
|
||||
- :sparkles: mica-mqtt-server 重构心跳,心跳检测模式默认为:最后接收的数据时间。gitee #I9R0SN #IA69SM 感谢 `@HY` `@tan90` 反馈。
|
||||
- :sparkles: mica-mqtt-server 优化端口占用的异常提示,方便排查。
|
||||
- :sparkles: mica-mqtt client 使用 mica-net 内置的心跳检测,内置心跳已重构。
|
||||
- :sparkles: mica-mqtt-client 重连不管服务端是否存在 session 都发送订阅。gitee #I9VIUV 感谢 `@xiaochonzi` 反馈。
|
||||
- :sparkles: 快照版也打 source jar 方便使用。
|
||||
- :sparkles: 添加 renovate bot 方便更新依赖和插件版本。
|
||||
- :sparkles: 优化 issue.yml 和 github action。
|
||||
|
||||
### v2.3.0 - 2024-05-26
|
||||
- :sparkles: mica-mqtt 优化 MqttQoS 枚举,改为 `MqttQoS.QOS0`,方便使用(不兼容)。
|
||||
- :sparkles: mica-mqtt-client 同步私服部分功能,支持 stop 完全停止。
|
||||
- :sparkles: mica-mqtt-client 同步私服部分功能,MqttClient 都添加了 `schedule`、`scheduleOnce` 方法,(**耗时任务,请务必自定义线程池**)
|
||||
- :sparkles: mica-mqtt-server 优化设备离线,简化代码。
|
||||
- :sparkles: mica-mqtt-server 用户绑定使用 tio 内置 `Tio.bindUser(context, username)`。
|
||||
- :bug: 修复 @MqttClientSubscribe 类型错误时的异常提示。
|
||||
- :bug: mica-mqtt-client 修复重连可能失败的问题 gitee #I9RI8E 感谢 `@YYGuo` 反馈。
|
||||
|
||||
### v2.2.13 - 2024-05-12
|
||||
- :sparkles: mica-mqtt-codec MqttVersion 添加版本全名。
|
||||
- :sparkles: mica-mqtt-codec MqttConnectReasonCode 添加中文说明。
|
||||
- :bug: mica-mqtt-server 保留消息下发时没有订阅也应该先存储 gitee #I9IYX1。
|
||||
|
||||
### v2.2.12 - 2024-04-16
|
||||
- :bug: mica-mqtt-server 遗嘱消息发送判断
|
||||
|
||||
### v2.2.11 - 2024-04-13
|
||||
- :sparkles: mica-mqtt-client-spring-boot-starter 简化 MqttClientTemplate 构造,方便自定义。
|
||||
- :sparkles: mica-mqtt-client-spring-boot-starter 优化 spring event mqtt client 连接监听。
|
||||
- :sparkles: mica-mqtt-client-spring-boot-starter 优化注解订阅。
|
||||
- :bug: mqtt-client 修复 mqtt5 props 和遗嘱同时配置时连接编码问题。
|
||||
|
||||
### v2.2.10 - 2024-03-23
|
||||
- :sparkles: mica-mqtt-client 优化 client publish 时还没有认证的情况。
|
||||
- :sparkles: mica-mqtt-client-spring-boot-starter 优化注解订阅,支持 clean session false 重启接收消息。
|
||||
|
||||
### v2.2.9 - 2024-02-25
|
||||
- :sparkles: mica-mqtt-server 拦截器 IMqttMessageInterceptor 添加 onAfterConnected 方法,方便在连接时做黑名单等功能。
|
||||
- :sparkles: mica-mqtt-client 添加私服版客户端全局订阅功能和添加使用文档。
|
||||
- :boom: mica-mqtt-common 删除弃用的 ThreadUtil。
|
||||
|
||||
### v2.2.8 - 2024-01-19
|
||||
- :sparkles: jfinal-mica-mqtt-client 启动改为同步连接。
|
||||
- :bug: mica-mqtt-client 修复 `isConnected` 判断。`2.2.7` 中存在此问题。
|
||||
- :arrow_up: 依赖升级
|
||||
|
||||
### v2.2.7 - 2024-01-03
|
||||
- :sparkles: mica-mqtt-server mqttws开启了ssl后,使用mqtt.js去连接,多刷新几次就会超时 gitee #I8LCMY
|
||||
- :sparkles: mica-mqtt-example 优化 graalvm 配置,感谢 github `@litongjava` 反馈
|
||||
|
||||
### v2.2.6 - 2023-11-26
|
||||
- :sparkles: mica-mqtt-server 添加 `webConfigCustomize` 支持自定义 http 和 ws 配置,可用于 gitee #I8HF7P
|
||||
- :sparkles: mica-mqtt-client 添加连接测试功能 connectTest gitee #I8J35M 感谢 `@彭蕾` 反馈
|
||||
- :sparkles: mica-mqtt-example 更新 graalvm 配置
|
||||
|
||||
### v2.2.5.1 - 2023-11-01
|
||||
- :poop: mica-mqtt-client mqttExecutor 方法参数类型漏改。
|
||||
|
||||
### v2.2.5 - 2023-10-05
|
||||
- :sparkles: mqtt 业务线程池支持自定义设置为 java21虚拟线程。
|
||||
- :sparkles: 更新 GitHub action,java17 改为 java21。
|
||||
- :sparkles: ThreadUtil 弃用(暂时未删),切换到 mica-net 中的 ThreadUtils。
|
||||
|
||||
### v2.2.4 - 2023-09-02
|
||||
- :sparkles: 合并去年开源之夏的服务端共享订阅和完善(捐助VIP版采用 topic 树存储,跟 topic 数无关,百万 topic 性能依旧)。
|
||||
- :sparkles: 优化 topic 检验
|
||||
- :bug: 相同 clientId 订阅相同 匹配 topic 应该取最大的qos gitee #I7WWPN
|
||||
|
||||
### v2.2.3 - 2023-07-23
|
||||
- :sparkles: mqtt server http api publish 不按 clientId 进行路由(无实际意义),而是按 topic,规则改为同 emqx。
|
||||
- :sparkles: mqtt server http api publish 触发 onMessage 消息监听。
|
||||
- :arrow_up: 依赖升级
|
||||
|
||||
### v2.2.2 - 2023-06-17
|
||||
- :sparkles: mica-mqtt-client 心跳包日志受 debug 控制
|
||||
- :sparkles: mica-mqtt-broker 的集群改为 redis stream 实现。
|
||||
- :bug: 修复 starter ssl truststorePass 配置,github #6 感谢 `@zkname` 反馈
|
||||
|
||||
### v2.2.1 - 2023-05-28
|
||||
- :zap: mica-mqtt-client 共享订阅更好的兼容 emqx 高版本,gitee #I786GU
|
||||
- :arrow_up: 依赖升级
|
||||
|
||||
### v2.2.0 - 2023-05-14
|
||||
- :sparkles: MqttPublishMessage payload 参数均由 `ByteBuffer` 改为 `byte[]`,简化代码,方便使用。
|
||||
- :bug: 修复 高并发场景下取消订阅时报 ConcurrentModificationException github #5 感谢 `@yinyuncan` 反馈
|
||||
|
||||
### v2.1.2 - 2023-04-26
|
||||
- :sparkles: mica-mqtt-client 支持 `reconnect(String ip, int port)` 转移到其他服务,订阅保留,连接成功时自动重新订阅。感谢 `@powerxie` 反馈
|
||||
- :sparkles: 优化 `TopicUtil#getTopicFilter()` topic 占位符替换。
|
||||
- :sparkles: 调整 mica-mqtt-client-spring-boot-starter 启动时机。`MqttClientCustomizer` 支持从数据库中获取配置。感谢 `@powerxie` 反馈
|
||||
- :memo: 修复迁移指南**ssl配置**文档错误
|
||||
- :bug: 修复包长度计算错误,压测下协议解析异常 gitee #I6YOMD 感谢 `@powerxie` 反馈
|
||||
|
||||
### v2.1.1 - 2023-04-08
|
||||
- :sparkles: mica-mqtt-server http-api 不再强制依赖 `fastjson` 还支持 `Jackson`、`Fastjson2`、`Gson`、`hutool-json` 和自定义, `@皮球` 反馈 gitee #I6O49D。
|
||||
- :sparkles: mica-mqtt-codec 删除 `org.dromara.mica.mqtt.codec.ByteBufferUtil`,2.1.0 漏删。
|
||||
- :sparkles: mica-mqtt-codec 兼容 qos大于0,messageId == 0,做 qos 降级处理,`@那一刹的容颜` 反馈,详见 gitee #I6PFIH
|
||||
- :sparkles: mica-mqtt-codec maxClientIdLength 默认改为 64,gitee #I6P2CG
|
||||
- :sparkles: mica-mqtt-client 优化链接时的遗嘱消息构建,默认为 qos0。`@tan90` 反馈 gitee #I6BRBV
|
||||
- :bug: mqtt-server 修复 mqtt.js websocket 空包问题,感谢群友反馈。
|
||||
- :bug: mqtt-server 修复 websocket mqtt 包长度判断问题。
|
||||
- :arrow_up: 依赖升级
|
||||
|
||||
### v2.1.0 - 2023-03-05
|
||||
- :sparkles: 【不兼容】调整接口参数,方便使用
|
||||
- :sparkles: 【不兼容】底层重构调整
|
||||
- :sparkles: 兼容更多 Spring boot 版本,支持 `2.1.0.RELEASE` 以上版本。
|
||||
- :sparkles: ssl 支持双向认证 gitee #I61AHJ 感谢 @DoubleH 反馈
|
||||
- :bug: 修复遗嘱消息判断 gitee #I6BRBV 感谢 @tan90 反馈。
|
||||
- :bug: 修复错别字 gitee #I6F2PA 感谢 @hpz 反馈
|
||||
- :arrow_up: 依赖升级
|
||||
|
||||
### v2.0.3 - 2022-09-18
|
||||
- :sparkles: 完善 ssl 方法,方便使用。
|
||||
- :arrow_up: 依赖升级,避免依赖导致的 bug。
|
||||
|
||||
### v2.0.2 - 2022-09-13
|
||||
- :bug: 彻底修复解码异常: `BufferUnderflowException`。
|
||||
|
||||
### v2.0.1 - 2022-09-12
|
||||
- :sparkles: 优化 MqttWebServer 配置。
|
||||
- :sparkles: mica-mqtt-example 添加华为云iot连接示例。
|
||||
- :sparkles: mica-mqtt-example 改为使用 tinylog。
|
||||
- :bug: 修复解码异常: `BufferUnderflowException`。
|
||||
|
||||
### v2.0.0 - 2022-09-04
|
||||
- :sparkles: mica mqtt server 完善方法,方便使用。
|
||||
- :sparkles: 切换到自维护的 java8 t-io,注意:升级了 t-io 部分类名变更。
|
||||
|
||||
### v1.3.9 - 2022-08-28
|
||||
- :sparkles: mica-mqtt server 添加消息拦截器,gitee #I5KLST
|
||||
- :sparkles: mica-mqtt client、server ack 优化和完善,可自定义 ackService。
|
||||
- :sparkles: mica-mqtt client stater MqttClientTemplate 完善,统一调整客户端示例。
|
||||
- :sparkles: mica-mqtt client 优化客户端心跳和心跳日志优化。
|
||||
- :sparkles: mica-mqtt client 订阅代码优化。
|
||||
- :sparkles: mica-mqtt codec 代码优化。
|
||||
- :sparkles: test 代码优化,更加符合 junit5 规范。
|
||||
- :bug: mqtt client Qos2 修复。
|
||||
|
||||
### v1.3.8 - 2022-08-11
|
||||
- :sparkles: mica-mqtt codec 代码优化。
|
||||
- :sparkles: mica-mqtt server 使用 Spring event 解耦消息监听。
|
||||
- :sparkles: mica-mqtt client stater,@MqttClientSubscribe topic 支持其他变量 ${productKey} 自动替换成 +。
|
||||
- :memo: 添加演示地址
|
||||
- :bug: 修复 mica-mqtt client 心跳发送问题。gitee #I5LQXV 感谢 `@iTong` 反馈。
|
||||
|
||||
### v1.3.7 - 2022-07-24
|
||||
- :sparkles: 添加 mica-mqtt jfinal client 和 server 插件。
|
||||
- :sparkles: mica-mqtt server 代码优化,useQueueDecode 默认为 true。
|
||||
- :sparkles: mica-mqtt client 监听回调代码优化。
|
||||
- :memo: 添加赞助,让你我走的更远!!!
|
||||
- :arrow_up: 依赖升级。
|
||||
|
||||
### v1.3.6 - 2022-06-25
|
||||
- :sparkles: mica-mqtt 统一调整最大的消息体和一次读取的字节数。
|
||||
- :sparkles: mica-mqtt client 简化 ssl 开启。
|
||||
- :sparkles: mica-mqtt server 添加默认的账号密码配置。
|
||||
- :arrow_up: 依赖升级
|
||||
|
||||
### v1.3.4 - 2022-06-06
|
||||
- :sparkles: mica-mqtt starter 使用 Spring event 解耦 mqtt client 断连事件。
|
||||
- :sparkles: mica-mqtt server `IMqttConnectStatusListener#offline` 方法添加 `reason` 断开原因字段。
|
||||
- :sparkles: 添加赞助计划。**捐助共勉,让你我走的更远!!!**
|
||||
- :bug: 修复 http api 响应问题。
|
||||
|
||||
### v1.3.3 - 2022-05-28
|
||||
- :sparkles: mica-mqtt 优化线程池。
|
||||
- :sparkles: mica-mqtt 添加 Compression 压缩接口。
|
||||
- :sparkles: mica-mqtt 添加 kafka TimingWheel 重构 ack。
|
||||
- :sparkles: mica-mqtt server 添加 `MqttClusterMessageListener` 方便集群消息处理。
|
||||
- :sparkles: mica-mqtt client 优化客户端取消订阅逻辑,gitee #I5779A 感谢 `@杨钊` 同学反馈。
|
||||
- :arrow_up: 升级 fastjson 到 1.2.83。
|
||||
|
||||
### v1.3.2 - 2022-05-09
|
||||
- :sparkles: mica-mqtt topic 匹配完善。
|
||||
- :sparkles: mica-mqtt 订阅、发布时添加 topicFilter、topicName 校验。
|
||||
|
||||
### v1.3.1 - 2022-05-08
|
||||
- :sparkles: mica-mqtt-broker 默认开启 http 和 basic auth。
|
||||
- :sparkles: mica-mqtt server 添加服务端共享订阅接口,方便开源之夏学生参与。
|
||||
- :sparkles: mica-mqtt server 添加 IMqttSessionListener。
|
||||
- :sparkles: mica-mqtt server publish 保留消息存储。
|
||||
- :sparkles: mica-mqtt server 统一 http 响应模型、优化 http 请求判断。
|
||||
- :sparkles: mica-mqtt server 优化 MqttHttpRoutes,添加获取所有路由的方法。
|
||||
- :sparkles: mica-mqtt server 完善 Result 和 http api。
|
||||
- :sparkles: mica-mqtt server http api 添加 endpoints 列表接口。
|
||||
- :sparkles: mica-mqtt client 添加同步连接 connectSync 方法。
|
||||
- :sparkles: mica-mqtt client 优化 bean 依赖,减少循环依赖可能性。
|
||||
- :bug: 重构 mqtt topic 匹配规则,提升性能减少内存占用,修复 gitee #I56BTC
|
||||
- :arrow_up: spring boot、mica 版本升级
|
||||
|
||||
### v1.3.0 - 2022-04-17
|
||||
- :sparkles: mica-mqtt mqtt-server 简化,默认多设备可以直接互相订阅和处理消息。
|
||||
- :sparkles: mica-mqtt server、client 添加 `tioConfigCustomize` 方法,方便更大程度的自定义 TioConfig。
|
||||
- :sparkles: 拆分 mica-mqtt-client-spring-boot-starter 和 mica-mqtt-server-spring-boot-starter gitee #I4OTC5
|
||||
- :sparkles: mica-mqtt-client-spring-boot-example 添加重连动态更新 clientId、username、password 示例。
|
||||
- :sparkles: mica-mqtt server 添加根据踢出指定 clientId 的 http api 接口。
|
||||
- :sparkles: mica-mqtt server IMqttConnectStatusListener api 调整,添加用户名字段。
|
||||
- :sparkles: mica-mqtt server IMqttMessageListener 不再强制要求实现。
|
||||
- :sparkles: 使用 netty IntObjectHashMap 优化默认 session 存储。
|
||||
- :sparkles: 添加 github action,用于自动构建开发阶段的 SNAPSHOT 版本。
|
||||
- :sparkles: 示例项目拆分到 example 目录,mica-mqtt client、server starter 拆分到 starter 目录。
|
||||
- :arrow_up: 依赖升级.
|
||||
|
||||
### v1.2.10 - 2022-03-20
|
||||
- :sparkles: mica-mqtt server 添加 MQTT 客户端 keepalive 系数 `keepalive-backoff`。
|
||||
- :sparkles: mica-mqtt client、server 调整发布的日志级别为 debug。
|
||||
- :sparkles: mica-mqtt client 优化 javadoc。
|
||||
- :sparkles: mica-mqtt client 重连时,支持重新设置新的鉴权密码。
|
||||
|
||||
### v1.2.9 - 2022-03-06
|
||||
- :sparkles: mqttServer#publishAll() 日志级别调整 gitee #I4W4IS
|
||||
- :sparkles: @MqttClientSubscribe 支持 springboot 配置 gitee #I4UOR3
|
||||
- :sparkles: mica-mqtt client 代码优化
|
||||
- :sparkles: mica-mqtt-spring-boot-example 拆分
|
||||
|
||||
### v1.2.8 - 2022-02-20
|
||||
- :sparkles: mica-mqtt server 优化连接 connect 日志。
|
||||
- :sparkles: mica-mqtt server 代码优化。
|
||||
- :sparkles: mica-mqtt server 添加 statEnable 配置,默认关闭,开启 Prometheus 监控,需要设置为 true。
|
||||
- :sparkles: mica-mqtt client 添加 statEnable 配置,默认关闭。
|
||||
- :sparkles: mica-mqtt client 优化默认线程池。
|
||||
|
||||
### v1.2.7 - 2022-02-13
|
||||
- :sparkles: mica-mqtt-spring-boot-starter 完善。
|
||||
- :sparkles: mica-mqtt client 考虑一开始就没有连接上服务端的情况。
|
||||
- :sparkles: mica-mqtt client 添加 isConnected 方法
|
||||
- :sparkles: mica-mqtt client、server connectListener 改为异步
|
||||
- :sparkles: mica-mqtt server ChannelContext 添加用户名,使用 (String) context.get(MqttConst.USER_NAME_KEY) 获取。
|
||||
- :sparkles: websocket ssl 配置
|
||||
- :sparkles: 尝试新版 graalvm
|
||||
- :bug: 修复多个 mica mqtt client 消息id生成器隔离。
|
||||
|
||||
### v1.2.6 - 2022-01-19
|
||||
- :sparkles: mica-mqtt-client 支持 `$share`、`$queue` 共享订阅
|
||||
|
||||
### v1.2.5 - 2022-01-16
|
||||
- :sparkles: mica mqtt server 调整发布权限规则。
|
||||
- :sparkles: mica mqtt server 自定义接口的异常处理。
|
||||
- :sparkles: mica mqtt server 放开 tio 队列配置。
|
||||
- :sparkles: mica mqtt client publish 添加一批 byte[] payload 参数方法。
|
||||
- :sparkles: mica-mqtt-model DefaultMessageSerializer 重构,**不兼容**。
|
||||
- :memo: 添加日志,避免遗忘。
|
||||
- :bug: http websocket 都不开启并排除 tio-websocket-server 依赖时 gitee #I4Q3CP
|
||||
|
||||
### v1.2.4 - 2022-01-09
|
||||
- :fire: mica-mqtt-core 排除一些不需要的依赖。
|
||||
- :fire: mica-mqtt-core http websocket 都不开启时,可以排除 tio-websocket-server 依赖。
|
||||
- :sparkles: mica-mqtt-core MqttTopicUtil 改名为 TopicUtil。
|
||||
- :sparkles: mica-mqtt-spring-boot-starter `@MqttClientSubscribe` 支持 IMqttClientMessageListener bean。
|
||||
- :sparkles: mica-mqtt-spring-boot-starter `@MqttClientSubscribe` 支持自定义 MqttClientTemplate Bean。
|
||||
- :sparkles: mica-mqtt-spring-boot-starter 完善。
|
||||
- :sparkles: mica-mqtt-codec 缩短 mqtt 版本 key。
|
||||
- :bug: mica-mqtt-codec 修复 will message。
|
||||
|
||||
### v1.2.3 - 2022-01-03
|
||||
- :sparkles: mica-mqtt-spring-boot-starter `@MqttClientSubscribe` value 改为数组,支持同时订阅多 topic。
|
||||
- :sparkles: mica-mqtt-core 缓存 TopicFilter Pattern。
|
||||
- :sparkles: mica-mqtt-core 优化客户端和服务端订阅逻辑 `IMqttServerSubscribeValidator` 接口调整。
|
||||
- :sparkles: mica-mqtt client 添加批量订阅。
|
||||
- :sparkles: mica-mqtt client 添加批量取消订阅。
|
||||
- :sparkles: mica-mqtt client 添加客户端是否断开连接。
|
||||
- :sparkles: mica-mqtt client 客户端断开重新订阅时支持配置批次大小。
|
||||
- :bookmark: mica-mqtt client 订阅 `IMqttClientMessageListener` 添加 `onSubscribed` 默认方法。
|
||||
- :arrow_up: mica-mqtt-example 升级 log4j2 到 2.17.1
|
||||
|
||||
### v1.2.2 - 2021-12-26
|
||||
- :sparkles: mica-mqtt server 添加发布权限接口,无权限直接断开连接,避免高级别 qos 重试浪费资源。
|
||||
- :sparkles: mica-mqtt-broker 优化节点信息存储
|
||||
- :sparkles: mica-mqtt client 重复订阅优化。感谢 `@一片小雨滴`
|
||||
- :sparkles: mica-mqtt client 抽象 IMqttClientSession 接口。
|
||||
- :bug: 修复重构 AbstractMqttMessageDispatcher 保持跟 mica-mqtt-broker 逻辑一致 gitee #I4MA6A 感谢 `@胡萝博`
|
||||
- :arrow_up: mica-mqtt-example 升级 log4j2 到 2.17.0
|
||||
|
||||
### v1.2.1 - 2021-12-11
|
||||
- :sparkles: mica-mqtt 优化 topic 匹配。
|
||||
- :sparkles: mica-mqtt client disconnect 不再自动重连 gitee #I4L0WK 感谢 `@willianfu`。
|
||||
- :sparkles: mica-mqtt client 添加 retryCount 配置 gitee #I4L0WK 感谢 `@willianfu`。
|
||||
- :sparkles: mica-mqtt-model message 添加 json 序列化。
|
||||
- :sparkles: mica-mqtt-broker 重新梳理逻辑。
|
||||
- :bug: mica-mqtt-spring-boot-starter 在 boot 2.6.x 下 bean 循环依赖 gitee #I4LUZP 感谢 `@hongfeng11`。
|
||||
- :bug: mica-mqtt server 同一个 clientId 踢出时清除老的 session。
|
||||
- :bug: mica-mqtt server 集群下一个 clientId 只允许连接到一台服务器。
|
||||
- :bug: mica-mqtt client 修复 IMqttClientConnectListener onDisconnect 空指针。
|
||||
- :memo: mica-mqtt-model 添加 README.md
|
||||
|
||||
### v1.2.0 - 2021-11-28
|
||||
- :sparkles: mqtt-mqtt-core client IMqttClientConnectListener 添加 onDisconnect 方法。gitee #I4JT1D 感谢 `@willianfu` 同学反馈。
|
||||
- :sparkles: mica-mqtt-core server IMqttMessageListener 接口调整,不兼容老版本。
|
||||
- :sparkles: mica-mqtt-broker 调整上下行消息通道。
|
||||
- :sparkles: mica-mqtt-broker 添加节点管理。
|
||||
- :sparkles: mica-mqtt-broker 调整默认的 Message 序列化方式,不兼容老版本。
|
||||
- :sparkles: mica-mqtt-broker 优化设备上下线,处理节点停机的情况。
|
||||
- :sparkles: 抽取 mica-mqtt-model 模块,方便后期支持消息桥接,Message 添加默认的消息序列化。 gitee #I4ECEO
|
||||
- :sparkles: mica-mqtt-model 完善 Message 消息模型,方便集群。
|
||||
- :bug: mica-mqtt-core MqttClient 修复 ssl 没有设置,感谢 `@hjkJOJO` 同学反馈。
|
||||
- :bug: 修复 websocket mqtt.js 需要拆包 gitee #I4JYJX 感谢 `@Symous` 同学反馈。
|
||||
- :memo: 完善 mica-mqtt-broker README.md,添加二开说明。
|
||||
- :memo: 统一 mica-mqtt server ip 文档。
|
||||
- :memo: 更新 README.md
|
||||
- :arrow_up: 升级 tio 到 3.7.5.v20211028-RELEASE AioDecodeException 改为 TioDecodeException,
|
||||
|
||||
### v1.1.4 - 2021-10-16
|
||||
- :sparkles: 添加 uniqueId 概念,用来处理 clientId 不唯一的场景。详见:gitee #I4DXQU
|
||||
- :sparkles: 微调 `IMqttServerAuthHandler` 认证,添加 uniqueId 参数。
|
||||
|
||||
### v1.1.3 - 2021-10-13
|
||||
- :sparkles: 状态事件接口 `IMqttConnectStatusListener` 添加 ChannelContext 参数。
|
||||
- :sparkles: 从认证中拆分 `IMqttServerSubscribeValidator` 订阅校验接口,添加 ChannelContext、clientId 参数。
|
||||
- :sparkles: 认证 `IMqttServerAuthHandler` 调整包、添加 ChannelContext 参数。
|
||||
- :sparkles: 完善文档和示例,添加默认端口号说明。
|
||||
- :arrow_up: 依赖升级
|
||||
|
||||
### v1.1.2 - 2021-09-12
|
||||
- :sparkles: 添加 mica-mqtt-broker 模块,基于 redis pub/sub 实现 mqtt 集群。
|
||||
- :sparkles: mica-mqtt-broker 基于 redis 实现客户端状态存储。
|
||||
- :sparkles: mica-mqtt-broker 基于 redis 实现遗嘱、保留消息存储。
|
||||
- :sparkles: mqtt-server http api 调整订阅和取消订阅,方便集群处理。
|
||||
- :sparkles: mica-mqtt-spring-boot-example 添加 mqtt 和 http api 认证示例。
|
||||
- :sparkles: 添加 mqtt 5 所有 ReasonCode。
|
||||
- :sparkles: 优化解码 PacketNeededLength 计算。
|
||||
- :bug: 修复遗嘱消息,添加消息类型。
|
||||
- :bug: 修复 mqtt-server 保留消息匹配规则。
|
||||
|
||||
### v1.1.1 - 2021-09-05
|
||||
- :sparkles: mqtt-server 优化连接关闭日志。
|
||||
- :sparkles: mqtt-server 优化订阅,相同 topicFilter 订阅对 qos 判断。
|
||||
- :sparkles: mqtt-server 监听器添加 try catch,避免因业务问题导致连接断开。
|
||||
- :sparkles: mqtt-server 优化 topicFilters 校验。
|
||||
- :sparkles: mqtt-client 优化订阅 reasonCodes 判断。
|
||||
- :sparkles: mqtt-client 监听器添加 try catch,避免因业务问题导致连接断开。
|
||||
- :sparkles: mqtt-client 添加 session 有效期。
|
||||
- :sparkles: 代码优化,减少 codacy 上的问题。
|
||||
- :bug: mqtt-server 修复心跳时间问题。
|
||||
- :bug: 修复 mqtt-server 多个订阅同时匹配时消息重复的问题。
|
||||
- :bug: mqtt-client 优化连接处理的逻辑,mqtt 连接之后再订阅。
|
||||
- :bug: 修复 MqttProperties 潜在的一个空指针。
|
||||
|
||||
### v1.1.0 - 2021-08-29
|
||||
- :sparkles: 重构,内置 http,http 和 websocket 公用端口。
|
||||
- :sparkles: 添加 mica-core 中的 HexUtil。
|
||||
- :sparkles: 添加 PayloadEncode 工具。
|
||||
- :sparkles: ServerTioConfig#share 方法添加 groupStat。
|
||||
- :sparkles: 考虑使用 udp 多播做集群。
|
||||
- :sparkles: MqttServer、MqttServerTemplate 添加 close、getChannelContext 等方法。
|
||||
- :sparkles: 重构 MqttServerConfiguration 简化代码。
|
||||
- :sparkles: 配置项 `mqtt.server.websocket-port` 改为 `mqtt.server.web-port`。
|
||||
- :memo: 添加 JetBrains 连接。
|
||||
- :bug: 修复默认的消息转发器逻辑。
|
||||
- :bug: 修复 websocket 下线无法触发offline gitee #I47K13 感谢 `@willianfu` 同学反馈。
|
||||
|
||||
### v1.0.6 - 2021-08-21
|
||||
- :sparkles: 添加订阅 topicFilter 校验。
|
||||
- :sparkles: 优化压测工具,更新压测说明,添加 tcp 连接数更改文档地址。
|
||||
- :sparkles: mica-mqtt-example 添加多设备交互示例。
|
||||
- :sparkles: 优化 mica-mqtt-spring-boot-example。
|
||||
- :sparkles: 优化 deploy.sh 脚本。
|
||||
- :bug: 优化解码异常处理。
|
||||
- :bug: 修复心跳超时处理。
|
||||
- :arrow_up: 升级 spring boot 到 2.5.4
|
||||
|
||||
### v1.0.5 - 2021-08-15
|
||||
- :bug: 修复编译导致的 java8 运行期间的部分问题,NoSuchMethodError: java.nio.ByteBuffer.xxx
|
||||
|
||||
### v1.0.3 - 2021-08-15
|
||||
- :sparkles: mica-mqtt server 添加 websocket mqtt 子协议支持(支持 mqtt.js)。
|
||||
- :sparkles: mica-mqtt server ip,默认为空,可不设置。
|
||||
- :sparkles: mica-mqtt client去除 CountDownLatch 避免启动时未连接上服务端卡住。
|
||||
- :sparkles: mica-mqtt client 添加最大包体长度字段,避免超过 8092 长度的包体导致解析异常。
|
||||
- :sparkles: mica-mqtt client 添加连接监听 IMqttClientConnectListener。
|
||||
- :sparkles: mica-mqtt 3.1 协议会校验 clientId 长度,添加配置项 maxClientIdLength。
|
||||
- :sparkles: mica-mqtt 优化 mqtt 解码异常处理。
|
||||
- :sparkles: mica-mqtt 日志优化,方便查询。
|
||||
- :sparkles: mica-mqtt 代码优化,部分 Tio.close 改为 Tio.remove。
|
||||
- :sparkles: mica-mqtt-spring-boot-example 添加 Dockerfile,支持 `spring-boot:build-image`。
|
||||
- :sparkles: 完善 mica-mqtt-spring-boot-starter,添加遗嘱消息配置。
|
||||
- :arrow_up: 升级 t-io 到 3.7.4。
|
||||
|
||||
### v1.0.3-RC - 2021-08-12
|
||||
- :sparkles: 添加 websocket mqtt 子协议支持(支持 mqtt.js)。
|
||||
- :sparkles: mqtt 客户端去除 CountDownLatch 避免启动时未连接上服务端卡住。
|
||||
- :sparkles: mica-mqtt 服务端 ip,默认为空,可不设置。
|
||||
- :sparkles: 完善 mica-mqtt-spring-boot-starter,添加遗嘱消息配置。
|
||||
- :sparkles: mqtt 3.1 协议会校验 clientId 长度,添加设置。
|
||||
- :sparkles: mqtt 日志优化,方便查询。
|
||||
- :sparkles: 代码优化,部分 Tio.close 改为 Tio.remove。
|
||||
- :arrow_up: 升级 t-io 到 3.7.4。
|
||||
|
||||
### v1.0.2 - 2021-08-08
|
||||
- :memo: 文档添加集群处理步骤说明,添加遗嘱消息、保留消息的使用场景。
|
||||
- :sparkles: 去除演示中的 qos2 参数,性能损耗大避免误用。
|
||||
- :sparkles: 遗嘱、保留消息内部消息转发抽象。
|
||||
- :sparkles: mqtt server 连接时先判断 clientId 是否存在连接关系,有则先关闭已有连接。
|
||||
- :sparkles: 添加 mica-mqtt-spring-boot-example 。感谢 wsq( @冷月宫主 )pr。
|
||||
- :sparkles: mica-mqtt-spring-boot-starter 支持客户端接入和服务端优化。感谢 wsq( @冷月宫主 )pr。
|
||||
- :sparkles: mica-mqtt-spring-boot-starter 服务端支持指标收集。可对接 `Prometheus + Grafana` 监控。
|
||||
- :sparkles: mqtt server 接受连接时,先判断该 clientId 是否存在其它连接,有则解绑并关闭其他连接。
|
||||
- :arrow_up: 升级 mica-auto 到 2.1.3 修复 ide 多模块增量编译问题。
|
||||
|
||||
### v1.0.2-RC - 2021-08-04
|
||||
- :sparkles: 添加 mica-mqtt-spring-boot-example 。感谢 wsq( @冷月宫主 )pr。
|
||||
- :sparkles: mica-mqtt-spring-boot-starter 支持客户端接入和服务端优化。感谢 wsq( @冷月宫主 )pr。
|
||||
- :sparkles: mica-mqtt-spring-boot-starter 服务端支持指标收集。可对接 `Prometheus + Grafana` 监控。
|
||||
- :sparkles: mqtt server 接受连接时,先判断该 clientId 是否存在其它连接,有则解绑并关闭其他连接。
|
||||
|
||||
### v1.0.1 - 2021-08-02
|
||||
- :sparkles: 订阅管理集成到 session 管理中。
|
||||
- :sparkles: MqttProperties.MqttPropertyType 添加注释,考虑 mqtt V5.0 新特性处理。
|
||||
- :sparkles: 添加 Spring boot starter 方便接入,兼容低版本 Spring boot。
|
||||
- :sparkles: 调研 t-io websocket 子协议。
|
||||
- :bug: 修复 java8 运行期间的部分问题,NoSuchMethodError: java.nio.ByteBuffer.xxx
|
||||
|
||||
### v1.0.1-RC - 2021-07-31
|
||||
- :sparkles: 添加 Spring boot starter 方便接入。
|
||||
- :sparkles: 调研 t-io websocket 子协议。
|
||||
|
||||
### v1.0.0 - 2021-07-29
|
||||
- :sparkles: 基于低延迟高性能的 t-io AIO 框架。
|
||||
- :sparkles: 支持 MQTT v3.1、v3.1.1 以及 v5.0 协议。
|
||||
- :sparkles: 支持 MQTT client 客户端。
|
||||
- :sparkles: 支持 MQTT server 服务端。
|
||||
- :sparkles: 支持 MQTT 遗嘱消息。
|
||||
- :sparkles: 支持 MQTT 保留消息。
|
||||
- :sparkles: 支持自定义消息(mq)处理转发实现集群。
|
||||
- :sparkles: 支持 GraalVM 编译成本机可执行程序。
|
||||
191
LICENSE
Normal file
191
LICENSE
Normal file
@@ -0,0 +1,191 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, "control" means (i) the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution.
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions.
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
6. Trademarks.
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
8. Limitation of Liability.
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability.
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets "{}" replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same "printed page" as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
129
README.en.md
Normal file
129
README.en.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# 🌐 Dromara mica mqtt
|
||||
[](https://github.com/dromara/mica-mqtt/actions)
|
||||

|
||||
[](https://central.sonatype.com/artifact/org.dromara.mica-mqtt/mica-mqtt-codec/versions)
|
||||

|
||||
[](https://github.com/dromara/mica-mqtt/blob/master/LICENSE)
|
||||
|
||||
[](https://gitcode.com/dromara/mica-mqtt)
|
||||
[](https://gitee.com/dromara/mica-mqtt/stargazers)
|
||||
[](https://github.com/dromara/mica-mqtt)
|
||||
|
||||
---
|
||||
|
||||
📖English | [📖简体中文](README.md)
|
||||
|
||||
Dromara `mica-mqtt` is a **low-latency, high-performance** MQTT IoT component designed for seamless integration and scalability. For detailed usage guides, refer to the **mica-mqtt-example** module.
|
||||
|
||||
## 🍱 Use Cases
|
||||
|
||||
- Internet of Things (cloud-based MQTT broker)
|
||||
- Internet of Things (edge messaging communication)
|
||||
- Group IM
|
||||
- Message push
|
||||
- Easy-to-use MQTT client
|
||||
|
||||
## 🚀 Key Advantages
|
||||
- **Simplicity & Flexibility**: Intuitive design for easy integration while retaining extensibility for advanced use cases.
|
||||
- **Manual Control**: Explicit API design to facilitate secondary development and customization.
|
||||
- **Future-ready**: Built with scalability in mind, supporting evolving IoT requirements.
|
||||
|
||||
## ✨ Features
|
||||
- [x] Supports MQTT v3.1, v3.1.1, and v5.0 protocols.
|
||||
- [x] Supports WebSocket MQTT sub-protocol (fully compatible with mqtt.js).
|
||||
- [x] Supports HTTP REST API - see [HTTP API Documentation](docs/http-api.md) for details.
|
||||
- [x] Support for MQTT client, support Android native.
|
||||
- [x] Support for MQTT server, support Android native.
|
||||
- [x] Support for MQTT Will messages.
|
||||
- [x] Support for MQTT Retained messages.
|
||||
- [x] Support for custom message (MQ) processing and forwarding to achieve clustering.
|
||||
- [x] MQTT client **Alibaba Cloud MQTT**、**HuaWei MQTT** connection demo.
|
||||
- [x] Support for GraalVM compilation into native executable programs.
|
||||
- [x] Support for rapid access to Spring Boot、Solon and JFinal projects.
|
||||
- [x] Spring boot and Solon client plugins support session retention.
|
||||
- [x] Support for integration with Prometheus + Grafana for monitoring.
|
||||
- [x] Cluster implementation based on Redis pub/sub, see [mica-mqtt-broker module](mica-mqtt-broker) for details.
|
||||
|
||||
## 🌱 To-do
|
||||
|
||||
- [ ] Optimize the handling of MQTT server sessions and simplify the use of MQTT v5.0.
|
||||
- [ ] Implement rule engine based on easy-rule + druid sql parsing.
|
||||
|
||||
## 🚨 Default Ports
|
||||
|
||||
| Port | Protocol | Description |
|
||||
|-------|---------------|-------------------------|
|
||||
| 1883 | tcp | mqtt tcp port |
|
||||
| 8883 | tcp ssl | mqtt tcp ssl port |
|
||||
| 8083 | websocket | websocket mqtt port |
|
||||
| 8084 | websocket ssl | websocket ssl mqtt port |
|
||||
| 18083 | http | http、MCP api port |
|
||||
|
||||
**Demo Address**: mqtt.dreamlu.net, same ports,username: mica password: mica
|
||||
|
||||
## 📦️ Dependencies
|
||||
|
||||
### Spring Boot Project
|
||||
**Client:**
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-client-spring-boot-starter</artifactId>
|
||||
<version>${mica-mqtt.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
**Configuration Details**: [mica-mqtt-client-spring-boot-starter Documentation](starter/mica-mqtt-client-spring-boot-starter/README.md)
|
||||
|
||||
**Server:**
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-server-spring-boot-starter</artifactId>
|
||||
<version>${mica-mqtt.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
**Configuration Details**: [mica-mqtt-server-spring-boot-starter Documentation](starter/mica-mqtt-server-spring-boot-starter/README.md)
|
||||
|
||||
### Non-Spring Boot Project
|
||||
|
||||
**Client:**
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-client</artifactId>
|
||||
<version>${mica-mqtt.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
**Configuration Details**: [mica-mqtt-client Documentation](mica-mqtt-client/README.md)
|
||||
|
||||
**Server:**
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-server</artifactId>
|
||||
<version>${mica-mqtt.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
**Configuration Details**: [mica-mqtt-server Documentation](mica-mqtt-server/README.md)
|
||||
|
||||
## 📝 Documentation
|
||||
- [Introduction to MQTT, mqttx, and mica-mqtt **Video**](https://www.bilibili.com/video/BV1wv4y1F7Av/)
|
||||
- [Getting Started with mica-mqtt](example/README.md)
|
||||
- [mica-mqtt HTTP API Documentation](docs/http-api.md)
|
||||
- [Frequently Asked Questions about mica-mqtt Usage](https://mica-mqtt.dreamlu.net/faq/faq.html)
|
||||
- [mica-mqtt Release Versions](CHANGELOG.md)
|
||||
|
||||
## 🍻 Open Source Recommendations
|
||||
- `Avue`: A Vue-based configurable front-end framework: [https://gitcode.com/superwei/avue](https://gitcode.com/superwei/avue)
|
||||
- `Pig`: Microservice framework featured on CCTV (architectural essential): [https://gitcode.com/pig-mesh/pig](https://gitcode.com/pig-mesh/pig)
|
||||
- `SpringBlade`: Enterprise-level solution (essential for enterprise development): [https://gitcode.com/bladex/SpringBlade](https://gitcode.com/bladex/SpringBlade)
|
||||
|
||||
## 📱 WeChat
|
||||
|
||||

|
||||
|
||||
**JAVA Architecture Diary**, daily recommended exciting content!
|
||||
180
README.md
Normal file
180
README.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# 🌐 Dromara mica mqtt 组件
|
||||
[](https://github.com/dromara/mica-mqtt/actions)
|
||||

|
||||
[](https://central.sonatype.com/artifact/org.dromara.mica-mqtt/mica-mqtt-codec/versions)
|
||||

|
||||
[](https://github.com/dromara/mica-mqtt/blob/master/LICENSE)
|
||||
|
||||
[](https://gitcode.com/dromara/mica-mqtt)
|
||||
[](https://gitee.com/dromara/mica-mqtt/stargazers)
|
||||
[](https://github.com/dromara/mica-mqtt)
|
||||
|
||||
---
|
||||
|
||||
📖简体中文 | [📖English](README.en.md)
|
||||
|
||||
Dromara `mica-mqtt` **低延迟**、**高性能**的 `mqtt` 物联网组件。更多使用方式详见: **mica-mqtt-example** 模块。
|
||||
|
||||
✨✨✨**最佳实践**✨✨✨ [**BladeX 物联网平台(「mica-mqtt加强版」+「EMQX+Kafka插件」双架构)**](https://iot.bladex.cn?from=mica-mqtt)
|
||||
|
||||
## 🍱 使用场景
|
||||
|
||||
- 物联网(云端 mqtt broker)
|
||||
- 物联网(边缘端消息通信)
|
||||
- 群组类 IM
|
||||
- 消息推送
|
||||
- 简单易用的 mqtt 客户端
|
||||
|
||||
## 🚀 优势
|
||||
- ✓ 轻如蝉翼 - 核心依赖仅 500KB
|
||||
- ✓ 新手友好 - 5 分钟极速上手
|
||||
- ✓ 自由操控 - 手动档设计,扩展随心
|
||||
- ✓ 潜力无限 - 小而强大,未来可期
|
||||
|
||||
## ✨ 功能
|
||||
- [x] 支持 MQTT v3.1、v3.1.1 以及 v5.0 协议。
|
||||
- [x] 支持 websocket mqtt 子协议(支持 mqtt.js)。
|
||||
- [x] 支持 http rest api,[http api 文档详见](docs/http-api.md)。
|
||||
- [x] 支持 MQTT client 客户端,支持 **Android** 最低要求 API 26(Android 8.0)。
|
||||
- [x] 支持 MQTT server 服务端,支持 **Android** 最低要求 API 26(Android 8.0)。
|
||||
- [x] 支持 MQTT client、server 共享订阅支持。
|
||||
- [x] 支持 MQTT 遗嘱消息。
|
||||
- [x] 支持 MQTT 保留消息。
|
||||
- [x] 支持自定义消息(mq)处理转发实现集群。
|
||||
- [x] MQTT 客户端 **阿里云 mqtt**、**华为云 mqtt** 连接 demo 示例。
|
||||
- [x] 支持 GraalVM 编译成本机可执行程序。
|
||||
- [x] 支持 Spring boot、Solon 和 JFinal 项目快速接入。
|
||||
- [x] Spring boot、Solon client 插件支持保留 session。
|
||||
- [x] 支持对接 Prometheus + Grafana 实现监控。
|
||||
- [x] 基于 redis stream 实现集群,详见 [mica-mqtt-broker 模块](https://gitee.com/dromara/mica-mqtt/tree/2.4.x/mica-mqtt-broker)。
|
||||
- [x] [mica mqtt 控制台](https://gitee.com/dreamlu/mica-mqtt-dashboard)
|
||||
|
||||
## 🌱 待办
|
||||
|
||||
- [ ] 优化处理 mqtt 服务端 session,以及简化 mqtt v5.0 使用。
|
||||
- [ ] 基于 easy-rule + druid sql 解析,实现规则引擎。
|
||||
|
||||
## 🚨 默认端口
|
||||
|
||||
| 端口号 | 协议 | 说明 |
|
||||
|-------|---------------|--------------------------|
|
||||
| 1883 | tcp | mqtt tcp 端口 |
|
||||
| 8883 | tcp ssl | mqtt tcp ssl 端口 |
|
||||
| 8083 | websocket | websocket mqtt 子协议端口 |
|
||||
| 8084 | websocket ssl | websocket ssl mqtt 子协议端口 |
|
||||
| 18083 | http | http、大模型 MCP 接口端口 |
|
||||
|
||||
**演示地址**:mqtt.dreamlu.net 端口同上,账号:mica 密码:mica
|
||||
|
||||
## 📦️ 依赖
|
||||
|
||||
### Spring boot 项目
|
||||
**客户端:**
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-client-spring-boot-starter</artifactId>
|
||||
<version>${mica-mqtt.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
**配置详见**:[mica-mqtt-client-spring-boot-starter 使用文档](starter/mica-mqtt-client-spring-boot-starter/README.md)
|
||||
|
||||
**服务端:**
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-server-spring-boot-starter</artifactId>
|
||||
<version>${mica-mqtt.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
**配置详见**:[mica-mqtt-server-spring-boot-starter 使用文档](starter/mica-mqtt-server-spring-boot-starter/README.md)
|
||||
|
||||
### Solon 项目
|
||||
**客户端:**
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-client-solon-plugin</artifactId>
|
||||
<version>${mica-mqtt.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
**配置详见**:[mica-mqtt-client-solon-plugin 使用文档](starter/mica-mqtt-client-solon-plugin/README.md)
|
||||
|
||||
**服务端:**
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-server-solon-plugin</artifactId>
|
||||
<version>${mica-mqtt.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
**配置详见**:[mica-mqtt-server-solon-plugin 使用文档](starter/mica-mqtt-server-solon-plugin/README.md)
|
||||
|
||||
### JFinal 项目
|
||||
**客户端:**
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-client-jfinal-plugin</artifactId>
|
||||
<version>${mica-mqtt.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
**配置详见**:[mica-mqtt-client-jfinal-plugin 使用文档](starter/mica-mqtt-client-jfinal-plugin/README.md)
|
||||
|
||||
**服务端:**
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-server-jfinal-plugin</artifactId>
|
||||
<version>${mica-mqtt.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
**配置详见**:[mica-mqtt-server-jfinal-plugin 使用文档](starter/mica-mqtt-server-jfinal-plugin/README.md)
|
||||
|
||||
### 其他项目
|
||||
|
||||
**客户端:**
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-client</artifactId>
|
||||
<version>${mica-mqtt.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
**配置详见**:[mica-mqtt-client 使用文档](mica-mqtt-client/README.md)
|
||||
|
||||
**服务端:**
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-server</artifactId>
|
||||
<version>${mica-mqtt.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
**配置详见**:[mica-mqtt-server 使用文档](mica-mqtt-server/README.md)
|
||||
|
||||
## 📝 文档
|
||||
- [mqtt科普、mqttx、mica-mqtt的使用**视频**](https://www.bilibili.com/video/BV1wv4y1F7Av/)
|
||||
- [mica-mqtt 快速开始](https://mica-mqtt.dreamlu.net/guide/)
|
||||
- [mica-mqtt 使用常见问题汇总](https://mica-mqtt.dreamlu.net/faq/faq.html)
|
||||
- [mica-mqtt 发行版本](https://mica-mqtt.dreamlu.net/version/changelog.html)
|
||||
- [mica-mqtt 老版本迁移指南](https://mica-mqtt.dreamlu.net/version/update.html)
|
||||
|
||||
## 🍻 开源推荐
|
||||
- `Avue` 基于 vue 可配置化的前端框架:[https://gitcode.com/superwei/avue](https://gitcode.com/superwei/avue)
|
||||
- `pig` 上央视的微服务框架(架构必备):[https://gitcode.com/pig-mesh/pig](https://gitcode.com/pig-mesh/pig)
|
||||
- `SpringBlade` 企业级解决方案(企业开发必备):[https://gitcode.com/bladex/SpringBlade](https://gitcode.com/bladex/SpringBlade)
|
||||
|
||||
## 📱 微信
|
||||
|
||||

|
||||
|
||||
**JAVA架构日记**,精彩内容每日推荐!
|
||||
14
SECURITY.md
Normal file
14
SECURITY.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Security Policy(安全策略)
|
||||
|
||||
## Supported Versions(支持的版本)
|
||||
|
||||
| Version | Supported |
|
||||
|---------|--------------------|
|
||||
| 2.4.x | :white_check_mark: |
|
||||
| < 2.4.x | :x: |
|
||||
|
||||
## Reporting a Vulnerability(报告漏洞)
|
||||
|
||||
如果你发现有安全问题或漏洞,请发送邮件到 `596392912@qq.com`。
|
||||
|
||||
To report any found security issues or vulnerabilities, please send a mail to `596392912@qq.com`.
|
||||
43
deploy.sh
Normal file
43
deploy.sh
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
## 0. java
|
||||
if command -v vfox >/dev/null 2>&1; then
|
||||
vfox use java@8.0.342+7
|
||||
else
|
||||
echo "Warning: vfox command not found, skipping Java version switch"
|
||||
fi
|
||||
|
||||
## 1. java version
|
||||
java -version
|
||||
printf "\n"
|
||||
|
||||
## 2. mvn version
|
||||
mvn -version
|
||||
printf "\n"
|
||||
|
||||
## 3. 环境
|
||||
if [ -z $1 ]; then
|
||||
profile="release"
|
||||
else
|
||||
profile="$1"
|
||||
fi
|
||||
printf "profile [%s] \n" "$profile"
|
||||
|
||||
## 4. modules
|
||||
modules="mica-mqtt-codec,mica-mqtt-common,"
|
||||
modules="$modules mica-mqtt-client,mica-mqtt-server,"
|
||||
modules="$modules starter/mica-mqtt-client-spring-boot-starter,"
|
||||
modules="$modules starter/mica-mqtt-server-spring-boot-starter,"
|
||||
modules="$modules starter/mica-mqtt-client-solon-plugin,"
|
||||
modules="$modules starter/mica-mqtt-server-solon-plugin,"
|
||||
modules="$modules starter/mica-mqtt-client-jfinal-plugin,"
|
||||
modules="$modules starter/mica-mqtt-server-jfinal-plugin"
|
||||
printf "modules [%s] \n" "$modules"
|
||||
|
||||
## 5. deploy
|
||||
if [ "$profile" = "snapshot" ]; then
|
||||
mvn clean deploy -U -P!develop,snapshot -pl "$modules"
|
||||
else
|
||||
mvn clean deploy -Prelease -pl "$modules"
|
||||
fi
|
||||
2
docs/graalvm.md
Normal file
2
docs/graalvm.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# mica-mqtt 之 GraalVM native-image 编译成本机可执行程序
|
||||
|
||||
426
docs/http-api.md
Normal file
426
docs/http-api.md
Normal file
@@ -0,0 +1,426 @@
|
||||
# Http Api 接口
|
||||
|
||||
注意:mica-mqtt 2.5.x 开始,http api 端口默认 18083(老版本~~8083~~)。
|
||||
|
||||
### HTTP 状态码 (status codes)
|
||||
|
||||
接口在调用成功时总是返回 200 OK,响应内容则以 JSON 格式返回。
|
||||
|
||||
可能的状态码如下:
|
||||
|
||||
| Status Code | Description |
|
||||
| ----------- | -------------------------------------------------------- |
|
||||
| 200 | 成功,返回的 JSON 数据将提供更多信息 |
|
||||
| 400 | 客户端请求无效,例如请求体或参数错误 |
|
||||
| 401 | 客户端未通过服务端认证,使用无效的身份验证凭据可能会发生 |
|
||||
| 404 | 找不到请求的路径或者请求的对象不存在 |
|
||||
| 405 | 请求方法错误 |
|
||||
| 500 | 服务端处理请求时发生内部错误 |
|
||||
|
||||
### 返回码 (result codes)
|
||||
|
||||
接口的响应消息体为 JSON 格式,其中总是包含返回码 `code`。
|
||||
|
||||
可能的返回码如下:
|
||||
|
||||
| Return Code | Description |
|
||||
| ----------- | -------------------------- |
|
||||
| 1 | 成功 |
|
||||
| 101 | 关键请求参数缺失 |
|
||||
| 102 | 请求参数错误 |
|
||||
| 103 | 用户名或密码错误 |
|
||||
| 104 | 请求方法错误 |
|
||||
| 105 | 未知错误 |
|
||||
|
||||
## 获取所有 api 接口列表
|
||||
|
||||
### GET /api/v1/endpoints
|
||||
|
||||
**Success Response Body (JSON):**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- |---------|-------------|
|
||||
| code | Integer | 1 |
|
||||
| data | Array | 接口列表 |
|
||||
| method | String | 方法名 |
|
||||
| path | String | 路径 |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
$ curl -i --basic -u mica:mica "http://localhost:18083/api/v1/endpoints"
|
||||
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/api/v1/endpoints"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/api/v1/clients"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/api/v1/mqtt/unsubscribe"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/api/v1/clients/info"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/api/v1/stats"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/api/v1/stats/sse"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/api/v1/mqtt/publish/batch"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/api/v1/mqtt/subscribe/batch"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/api/v1/client/subscriptions"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/api/v1/mqtt/publish"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/api/v1/mqtt/unsubscribe/batch"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/api/v1/mqtt/subscribe"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/api/v1/clients/delete"
|
||||
}
|
||||
],
|
||||
"code": 1
|
||||
}
|
||||
```
|
||||
|
||||
## 消息发布
|
||||
|
||||
### POST /api/v1/mqtt/publish
|
||||
|
||||
发布 MQTT 消息。
|
||||
|
||||
**Parameters (json):**
|
||||
|
||||
| Name | Type | Required | Default | Description |
|
||||
| -------- | ------- | -------- | ------- |------------------------------------------------|
|
||||
| topic | String | Required | | 主题,消息会按 topic 订阅投递 |
|
||||
| clientId | String | Required | | 客户端标识符,不为空参数即可,无实际意义,建议可以取名 httpApi |
|
||||
| payload | String | Required | | 消息正文 |
|
||||
| encoding | String | Optional | plain | 消息正文使用的编码方式,目前仅支持 目前仅支持 `plain`、`hex`、`base64` |
|
||||
| qos | Integer | Optional | 0 | QoS 等级 |
|
||||
| retain | Boolean | Optional | false | 是否为保留消息 |
|
||||
|
||||
**Success Response Body (JSON):**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ------- | ----------- |
|
||||
| code | Integer | 0 |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
$ curl -i --basic -u mica:mica -X POST "http://localhost:18083/api/v1/mqtt/publish" -d '{"topic":"a/b/c","payload":"Hello World","qos":1,"retain":false,"clientId":"example"}'
|
||||
|
||||
{"code":1}
|
||||
```
|
||||
|
||||
## 主题订阅
|
||||
|
||||
### POST /api/v1/mqtt/subscribe
|
||||
|
||||
订阅 MQTT 主题。
|
||||
|
||||
**Parameters (json):**
|
||||
|
||||
| Name | Type | Required | Default | Description |
|
||||
| -------- | ------- | -------- | ------- | ----------------------------------------------------- |
|
||||
| topic | String | Required | | 主题 |
|
||||
| clientId | String | Required | | 客户端标识符 |
|
||||
| qos | Integer | Optional | 0 | QoS 等级 |
|
||||
|
||||
**Success Response Body (JSON):**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ------- | ----------- |
|
||||
| code | Integer | 0 |
|
||||
|
||||
**Examples:**
|
||||
|
||||
同时订阅 `a`, `b`, `c` 三个主题
|
||||
|
||||
```bash
|
||||
$ curl -i --basic -u mica:mica -X POST "http://localhost:18083/api/v1/mqtt/subscribe" -d '{"topic":"a/b/c","qos":1,"clientId":"example"}'
|
||||
|
||||
{"code":1}
|
||||
```
|
||||
|
||||
### POST /api/v1/mqtt/unsubscribe
|
||||
|
||||
取消订阅。
|
||||
|
||||
**Parameters (json):**
|
||||
|
||||
| Name | Type | Required | Default | Description |
|
||||
| -------- | ------ | -------- | ------- | ------------ |
|
||||
| topic | String | Required | | 主题 |
|
||||
| clientId | String | Required | | 客户端标识符 |
|
||||
|
||||
**Success Response Body (JSON):**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ------- | ----------- |
|
||||
| code | Integer | 0 |
|
||||
|
||||
**Examples:**
|
||||
|
||||
取消订阅 `a` 主题
|
||||
|
||||
```bash
|
||||
$ curl -i --basic -u mica:mica -X POST "http://localhost:18083/api/v1/mqtt/unsubscribe" -d '{"topic":"a","clientId":"example"}'
|
||||
|
||||
{"code":1}
|
||||
```
|
||||
|
||||
## 消息批量发布
|
||||
|
||||
### POST /api/v1/mqtt/publish/batch
|
||||
|
||||
批量发布 MQTT 消息。
|
||||
|
||||
**Parameters (json):**
|
||||
|
||||
| Name | Type | Required | Default | Description |
|
||||
| ------------ | ------- | -------- | ------- |------------------------------------------|
|
||||
| [0].topic | String | Required | | 主题,消息按订阅投递 |
|
||||
| [0].clientId | String | Required | | 客户端标识符,不为空参数即可,无实际意义,建议可以取名 httpApi |
|
||||
| [0].payload | String | Required | | 消息正文 |
|
||||
| [0].encoding | String | Optional | plain | 消息正文使用的编码方式,目前仅支持 `plain`、`hex`、`base64` |
|
||||
| [0].qos | Integer | Optional | 0 | QoS 等级 |
|
||||
| [0].retain | Boolean | Optional | false | 是否为保留消息 |
|
||||
|
||||
**Success Response Body (JSON):**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ------- | ----------- |
|
||||
| code | Integer | 0 |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
$ curl -i --basic -u mica:mica -X POST "http://localhost:18083/api/v1/mqtt/publish/batch" -d '[{"topic":"a/b/c","payload":"Hello World","qos":1,"retain":false,"clientId":"example"},{"topic":"a/b/c","payload":"Hello World Again","qos":0,"retain":false,"clientId":"example"}]'
|
||||
|
||||
{"code":1}
|
||||
```
|
||||
|
||||
## 主题批量订阅
|
||||
|
||||
### POST /api/v1/mqtt/subscribe/batch
|
||||
|
||||
批量订阅 MQTT 主题。
|
||||
|
||||
**Parameters (json):**
|
||||
|
||||
| Name | Type | Required | Default | Description |
|
||||
| ------------ | ------- | -------- | ------- | ----------------------------------------------------- |
|
||||
| [0].topic | String | Required | | 主题 |
|
||||
| [0].clientId | String | Required | | 客户端标识符 |
|
||||
| [0].qos | Integer | Optional | 0 | QoS 等级 |
|
||||
|
||||
**Success Response Body (JSON):**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ------- | ----------- |
|
||||
| code | Integer | 0 |
|
||||
|
||||
**Examples:**
|
||||
|
||||
一次性订阅 `a`, `b`, `c` 三个主题
|
||||
|
||||
```bash
|
||||
$ curl -i --basic -u mica:mica -X POST "http://localhost:18083/api/v1/mqtt/subscribe/batch" -d '[{"topic":"a","qos":1,"clientId":"example"},{"topic":"b","qos":1,"clientId":"example"},{"topic":"c","qos":1,"clientId":"example"}]'
|
||||
|
||||
{"code":1}
|
||||
```
|
||||
|
||||
### POST /api/v1/mqtt/unsubscribe/batch
|
||||
|
||||
批量取消订阅。
|
||||
|
||||
**Parameters (json):**
|
||||
|
||||
| Name | Type | Required | Default | Description |
|
||||
| ------------ | ------ | -------- | ------- | ------------ |
|
||||
| [0].topic | String | Required | | 主题 |
|
||||
| [0].clientId | String | Required | | 客户端标识符 |
|
||||
|
||||
**Success Response Body (JSON):**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ------- | ----------- |
|
||||
| code | Integer | 0 |
|
||||
|
||||
**Examples:**
|
||||
|
||||
一次性取消订阅 `a`, `b` 主题
|
||||
|
||||
```bash
|
||||
$ curl -i --basic -u mica:mica -X POST "http://localhost:18083/api/v1/mqtt/unsubscribe/batch" -d '[{"topic":"a","clientId":"example"},{"topic":"b","clientId":"example"}]'
|
||||
|
||||
{"code":1}
|
||||
```
|
||||
|
||||
## 获取客户端详情
|
||||
|
||||
### GET /api/v1/clients/info
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| -------- | ------ | -------- | ----------- |
|
||||
| clientId | String | True | ClientID |
|
||||
|
||||
**Success Response Body (JSON):**
|
||||
|
||||
| Name | Type | Description |
|
||||
|-----------|---------|-------------|
|
||||
| code | Integer | 0 |
|
||||
| clientId | String | clientId |
|
||||
| username | String | 用户名 |
|
||||
| connected | Boolen | 是否已经连接 |
|
||||
| createdAt | Long | 连接的时间 |
|
||||
| connectedAt | Long | 连接成功时间 |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
$ curl -i --basic -u mica:mica -X POST "http://localhost:18083/api/v1/clients/info?clientId=mqttx_5fe4cfcf"
|
||||
|
||||
{"code":1,"data":{"clientId":"mqttx_5fe4cfcf","connected":true,"connectedAt":1681792417835,"createdAt":1681792417835,"ipAddress":"127.0.0.1","port":11852,"protoName":"MQTT","protoVer":5}}
|
||||
```
|
||||
|
||||
## 分页获取客户端
|
||||
|
||||
### GET /api/v1/clients
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|--------|------|----------|--------------|
|
||||
| _page | int | False | Page 默认1 |
|
||||
| _limit | int | False | 分页大小 默认10000 |
|
||||
|
||||
**Success Response Body (JSON):**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ------- | ----------- |
|
||||
| code | Integer | 0 |
|
||||
|
||||
**Success Response Body (JSON):**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ------- |-------------|
|
||||
| code | Integer | 0 |
|
||||
| pageNumber | Integer | 当前页码 |
|
||||
| pageSize | Integer | 分页大小 |
|
||||
| totalRow | Integer | 分页数 |
|
||||
| clientId | String | clientId |
|
||||
| username | String | 用户名 |
|
||||
| connected | Boolen | 是否已经连接 |
|
||||
| createdAt | Long | 连接的时间 |
|
||||
| connectedAt | Long | 连接成功时间 |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
$ curl -i --basic -u mica:mica -X POST "http://localhost:18083/api/v1/clients?_page=1&_limit=100"
|
||||
|
||||
{"data":{"list":[{"clientId":"mqttx_5fe4cfcf","connected":true,"protoName":"MQTT","protoVer":5,"ipAddress":"127.0.0.1","port":11852,"connectedAt":1681792417835,"createdAt":1681792417835}],"pageNumber":1,"pageSize":1,"totalRow":1},"code":1}
|
||||
```
|
||||
|
||||
## 踢出指定客户端
|
||||
|
||||
### POST /api/v1/clients/delete
|
||||
|
||||
踢出指定客户端。注意踢出客户端操作会将连接与会话一并终结。
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| -------- | ------ | -------- | ----------- |
|
||||
| clientId | String | True | ClientID |
|
||||
|
||||
**Success Response Body (JSON):**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ------- | ----------- |
|
||||
| code | Integer | 0 |
|
||||
|
||||
**Examples:**
|
||||
|
||||
由于客户端可能会重连,所以还会连上了。如果需要永久踢出需要自行开发黑名单。
|
||||
|
||||
```bash
|
||||
$ curl -i --basic -u mica:mica -X POST "http://localhost:18083/api/v1/clients/delete?clientId=123"
|
||||
|
||||
{"code":1}
|
||||
```
|
||||
|
||||
## 获取客户端订阅情况
|
||||
|
||||
### GET /api/v1/client/subscriptions
|
||||
|
||||
获取指定客户端订阅详情。
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
| -------- | ------ | -------- | ----------- |
|
||||
| clientId | String | True | ClientID |
|
||||
|
||||
**Success Response Body (JSON):**
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- |---------|-------------|
|
||||
| code | Integer | 0 |
|
||||
| data | Array | [] |
|
||||
| topicFilter | String | |
|
||||
| clientId | String | |
|
||||
| mqttQoS | Integer | 0 |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
$ curl -i --basic -u mica:mica "http://127.0.0.1:8083/api/v1/client/subscriptions?clientId=123"
|
||||
|
||||
{
|
||||
"code": 1,
|
||||
"data": [
|
||||
{
|
||||
"clientId": "123",
|
||||
"mqttQoS": 0,
|
||||
"topicFilter": "#"
|
||||
},
|
||||
{
|
||||
"clientId": "123",
|
||||
"mqttQoS": 0,
|
||||
"topicFilter": "testtopic/#"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
7
docs/http/http-client.env.json
Normal file
7
docs/http/http-client.env.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"local": {
|
||||
"host": "127.0.0.1:18083",
|
||||
"username": "mica",
|
||||
"password": "mica"
|
||||
}
|
||||
}
|
||||
132
docs/http/mica-mqtt-api.http
Normal file
132
docs/http/mica-mqtt-api.http
Normal file
@@ -0,0 +1,132 @@
|
||||
### mqtt endpoints
|
||||
GET http://{{host}}/api/v1/endpoints
|
||||
Content-Type: application/json
|
||||
Authorization: Basic {{username}} {{password}}
|
||||
|
||||
### mqtt clients
|
||||
GET http://{{host}}/api/v1/clients
|
||||
Content-Type: application/json
|
||||
Authorization: Basic {{username}} {{password}}
|
||||
|
||||
### mqtt client info
|
||||
GET http://{{host}}/api/v1/clients/info?clientId=123
|
||||
Content-Type: application/json
|
||||
Authorization: Basic {{username}} {{password}}
|
||||
|
||||
### mqtt stats
|
||||
GET http://{{host}}/api/v1/stats
|
||||
Content-Type: application/json
|
||||
Authorization: Basic {{username}} {{password}}
|
||||
|
||||
### mqtt stats sse
|
||||
GET http://{{host}}/api/v1/stats/sse
|
||||
Authorization: Basic {{username}} {{password}}
|
||||
|
||||
### mqtt publish
|
||||
POST http://{{host}}/api/v1/mqtt/publish
|
||||
Content-Type: application/json
|
||||
Authorization: Basic {{username}} {{password}}
|
||||
|
||||
{
|
||||
"topic":"a/b/c",
|
||||
"payload":"Hello World",
|
||||
"qos":1,
|
||||
"retain":false,
|
||||
"clientId":"example"
|
||||
}
|
||||
|
||||
### mqtt subscribe
|
||||
POST http://{{host}}/api/v1/mqtt/subscribe
|
||||
Content-Type: application/json
|
||||
Authorization: Basic {{username}} {{password}}
|
||||
|
||||
{
|
||||
"topic":"a/b/c",
|
||||
"qos":1,
|
||||
"clientId":"example"
|
||||
}
|
||||
|
||||
### mqtt unsubscribe
|
||||
POST http://{{host}}/api/v1/mqtt/unsubscribe
|
||||
Content-Type: application/json
|
||||
Authorization: Basic {{username}} {{password}}
|
||||
|
||||
{
|
||||
"topic":"a/b/c",
|
||||
"clientId":"example"
|
||||
}
|
||||
|
||||
### mqtt publish batch
|
||||
POST http://{{host}}/api/v1/mqtt/publish/batch
|
||||
Content-Type: application/json
|
||||
Authorization: Basic {{username}} {{password}}
|
||||
|
||||
[
|
||||
{
|
||||
"topic":"a/b/c",
|
||||
"payload":"Hello World",
|
||||
"qos":1,
|
||||
"retain":false,
|
||||
"clientId":"example"
|
||||
},
|
||||
{
|
||||
"topic":"a/b/c",
|
||||
"payload":"Hello World Again",
|
||||
"qos":0,
|
||||
"retain":false,
|
||||
"clientId":"example"
|
||||
}
|
||||
]
|
||||
|
||||
### mqtt subscribe batch
|
||||
POST http://{{host}}/api/v1/mqtt/subscribe/batch
|
||||
Content-Type: application/json
|
||||
Authorization: Basic {{username}} {{password}}
|
||||
|
||||
[
|
||||
{
|
||||
"topic":"a",
|
||||
"qos":1,
|
||||
"clientId":"example"
|
||||
},
|
||||
{
|
||||
"topic":"b",
|
||||
"qos":1,
|
||||
"clientId":"example"
|
||||
},
|
||||
{
|
||||
"topic":"c",
|
||||
"qos":1,
|
||||
"clientId":"example"
|
||||
}
|
||||
]
|
||||
|
||||
### mqtt unsubscribe batch
|
||||
POST http://{{host}}/api/v1/mqtt/unsubscribe/batch
|
||||
Content-Type: application/json
|
||||
Authorization: Basic {{username}} {{password}}
|
||||
|
||||
[
|
||||
{
|
||||
"topic":"a",
|
||||
"clientId":"example"
|
||||
},
|
||||
{
|
||||
"topic":"b",
|
||||
"clientId":"example"
|
||||
}
|
||||
]
|
||||
|
||||
### mqtt delete clients
|
||||
POST http://{{host}}/api/v1/clients/delete
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Authorization: Basic {{username}} {{password}}
|
||||
|
||||
clientId=123
|
||||
|
||||
### mqtt client subscriptions
|
||||
GET http://{{host}}/api/v1/client/subscriptions
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Authorization: Basic {{username}} {{password}}
|
||||
|
||||
clientId=123
|
||||
BIN
docs/img/dreamlu-weixin.jpg
Normal file
BIN
docs/img/dreamlu-weixin.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
BIN
docs/img/mica-mqtt.jpg
Normal file
BIN
docs/img/mica-mqtt.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
91
docs/update.md
Normal file
91
docs/update.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# 升级指南
|
||||
|
||||
**mica-mqtt** 尽量减少对 api 的改动已保证老版本的平滑升级,但是有些大版本不得不改动。希望此文档对大家有所帮助。
|
||||
|
||||
## 迁移到 mica-mqtt 2.4.2
|
||||
|
||||
注意:2.4.2 将 MqttServerCustomizer 和 MqttClientCustomizer 抽到 mica-mqtt-server、mica-mqtt-client。Spring Boot 和 Solon 插入如果有使用到,请先将老的包导入删除,idea 会自动引入新的包。
|
||||
|
||||
**客户端替换包导入:**
|
||||
- 替换成 `import org.dromara.mica.mqtt.core.client.MqttClientCustomizer;`
|
||||
|
||||
**服务端替换包导入:**
|
||||
- 替换成 `import org.dromara.mica.mqtt.core.server.MqttServerCustomizer;`
|
||||
|
||||
## 迁移到 mica-mqtt 2.4.x 以上版本
|
||||
|
||||
- :truck: 调整 maven groupId `net.dreamlu` 到新的 `org.dromara.mica-mqtt`。
|
||||
- :truck: 调整包名 `net.dreamlu.iot.mqtt` 到新的 `org.dromara.mica.mqtt`,其他均保持不变。
|
||||
- :truck: 切换到 central sonatype,central sonatype 不支持快照版,mica-mqtt 不再发布快照版。
|
||||
|
||||
## 迁移到 mica-mqtt 2.1.x
|
||||
|
||||
- `mica-mqtt-core` 拆分成了 `mica-mqtt-client` 和 `mica-mqtt-server`,避免一些依赖引用问题。
|
||||
- `ByteBufferUtil` 由 `org.dromara.mica.mqtt.codec.ByteBufferUtil` 移动到了 `org.tio.utils.buffer.ByteBufferUtil`。
|
||||
- `HexUtil` 由 `org.dromara.mica.mqtt.core.util.HexUtil` 移动到了 `org.tio.utils.mica.HexUtils`。
|
||||
|
||||
### 1. 客户端
|
||||
|
||||
#### 1.1 订阅回调接口调整
|
||||
注意:`mica-mqtt-client-spring-boot-starter` 使用注解订阅可以直升。
|
||||
|
||||
`IMqttClientMessageListener#onMessage(ChannelContext context, String topic, MqttPublishMessage message, ByteBuffer payload)` 方法统一添加 `context`、`message` 参数。
|
||||
|
||||
订阅系列方法需要调整:
|
||||
```java
|
||||
// 消息订阅,同类方法 subxxx
|
||||
client.subQos0("/test/#", (context, topic, message, payload) -> {
|
||||
logger.info(topic + '\t' + ByteBufferUtil.toString(payload));
|
||||
});
|
||||
```
|
||||
|
||||
#### 1.2 SSL 双向认证支持
|
||||
```yaml
|
||||
mica:
|
||||
client:
|
||||
ssl:
|
||||
enabled: false # 是否开启 ssl 认证,2.1.0 开始支持双向认证
|
||||
keystore-path: # 可选参数:ssl 双向认证 keystore 目录,支持 classpath:/ 路径。
|
||||
keystore-pass: # 可选参数:ssl 双向认证 keystore 密码
|
||||
truststore-path: # 可选参数:ssl 双向认证 truststore 目录,支持 classpath:/ 路径。
|
||||
truststore-pass: # 可选参数:ssl 双向认证 truststore 密码
|
||||
```
|
||||
|
||||
注意: ssl 存在三种情况
|
||||
|
||||
| 服务端开启ssl | 客户端 |
|
||||
| ---------------------------------------- | --------------------------------------------- |
|
||||
| ClientAuth 为 NONE(不需要客户端验证) | 仅仅需要开启 ssl 即可不用配置证书 |
|
||||
| ClientAuth 为 OPTIONAL(与客户端协商) | 需开启 ssl 并且配置 truststore 证书 |
|
||||
| ClientAuth 为 REQUIRE (必须的客户端验证) | 需开启 ssl 并且配置 truststore、 keystore证书 |
|
||||
|
||||
### 2. 服务端
|
||||
|
||||
#### 2.1 IMqttMessageListener 调整
|
||||
|
||||
`IMqttMessageListener` onMessage 参数也做了调整,添加了 topic、qoS,message 改为了原始 MqttPublishMessage,方便自行获取 mqtt5.x 的属性。
|
||||
```java
|
||||
/**
|
||||
* 监听到消息
|
||||
*
|
||||
* @param context ChannelContext
|
||||
* @param clientId clientId
|
||||
* @param topic topic
|
||||
* @param qoS MqttQoS
|
||||
* @param message Message
|
||||
*/
|
||||
void onMessage(ChannelContext context, String clientId, String topic, MqttQoS qoS, MqttPublishMessage message);
|
||||
```
|
||||
|
||||
#### 2.2 ssl 双向认证支持
|
||||
```yaml
|
||||
mica:
|
||||
server:
|
||||
ssl: # mqtt tcp ssl 认证
|
||||
enabled: false # 是否开启 ssl 认证,2.1.0 开始支持双向认证
|
||||
keystore-path: # 必须参数:ssl keystore 目录,支持 classpath:/ 路径。
|
||||
keystore-pass: # 必选参数:ssl keystore 密码
|
||||
truststore-path: # 可选参数:ssl 双向认证 truststore 目录,支持 classpath:/ 路径。
|
||||
truststore-pass: # 可选参数:ssl 双向认证 truststore 密码
|
||||
client-auth: none # 是否需要客户端认证(双向认证),默认:NONE(不需要)
|
||||
```
|
||||
113
example/README.md
Normal file
113
example/README.md
Normal file
@@ -0,0 +1,113 @@
|
||||
## 快速开始
|
||||
|
||||
**example** 中有 `mqtt` 服务端和客户端演示代码。
|
||||
|
||||
也可以只启动**Server**端,**Client**端用客户端工具测试。
|
||||
|
||||
### 1. 启动 Server 端
|
||||
|
||||
运行 `example/mica-mqtt-server-spring-boot-example/src/main/java/net/dreamlu/iot/mqtt/server/MqttServerApplication.java` 的 `main` 方法
|
||||
|
||||
控制台打印如下内容:
|
||||
|
||||
```text
|
||||
2021-07-05 20:42:36,869 INFO server.TioServer -
|
||||
|----------------------------------------------------------------------------------------|
|
||||
| t-io site | https://www.tiocloud.com |
|
||||
| t-io on gitee | https://gitee.com/tywo45/t-io |
|
||||
| t-io on github | https://github.com/tywo45/t-io |
|
||||
| t-io version | 3.7.3.v20210706-RELEASE |
|
||||
| ---------------------------------------------------------------------------------------|
|
||||
| TioConfig name | Mica-Mqtt-Server |
|
||||
| Started at | 2021-07-05 20:42:36 |
|
||||
| Listen on | 127.0.0.1:1883 |
|
||||
| Main Class | org.dromara.mica.mqtt.server.MqttServerTest |
|
||||
| Jvm start time | 2715ms |
|
||||
| Tio start time | 16ms |
|
||||
| Pid | 3588 |
|
||||
|----------------------------------------------------------------------------------------|
|
||||
|
||||
2021-07-05 20:42:37,884 WARN server.MqttServer - Mqtt publish to all ChannelContext is empty.
|
||||
```
|
||||
|
||||
`Mqtt publish to all ChannelContext is empty.` 通道上下文为空,即没有客户端。
|
||||
|
||||
```text
|
||||
Mica-Mqtt-Server
|
||||
├ 当前时间:1625489086843
|
||||
├ 连接统计
|
||||
│ ├ 共接受过连接数 :0
|
||||
│ ├ 当前连接数 :0
|
||||
│ ├ 异IP连接数 :0
|
||||
│ └ 关闭过的连接数 :0
|
||||
├ 消息统计
|
||||
│ ├ 已处理消息 :0
|
||||
│ ├ 已接收消息(packet/byte):0/0
|
||||
│ ├ 已发送消息(packet/byte):0/0b
|
||||
│ ├ 平均每次TCP包接收的字节数 :0.0
|
||||
│ └ 平均每次TCP包接收的业务包 :0.0
|
||||
└ IP统计时段
|
||||
└ 没有设置ip统计时间
|
||||
├ 节点统计
|
||||
│ ├ clientNodes :0
|
||||
│ ├ 所有连接 :0
|
||||
│ ├ 绑定user数 :0
|
||||
│ ├ 绑定token数 :0
|
||||
│ └ 等待同步消息响应 :0
|
||||
├ 群组
|
||||
│ └ groupmap:0
|
||||
└ 拉黑IP
|
||||
└ []
|
||||
2021-07-05 20:44:46,925 WARN server.ServerTioConfig - Mica-Mqtt-Server, 检查心跳, 共0个连接, 取锁耗时0ms, 循环耗时71ms, 心跳超时时间:120000ms
|
||||
```
|
||||
|
||||
### 2. 启动 Client 端
|
||||
|
||||
运行 `example/mica-mqtt-client-spring-boot-example/src/main/java/net/dreamlu/iot/mqtt/client/MqttClientApplication.java` 的 `main` 方法
|
||||
|
||||
控制台打印如下内容,表示客户端连接成功:
|
||||
```text
|
||||
2021-07-05 20:46:10,972 ERROR client.TioClient - closeds:0, connections:0
|
||||
2021-07-05 20:46:10,972 INFO client.TioClient - [1]: curr:0, closed:0, received:(0p)(0b), handled:0, sent:(0p)(0b)
|
||||
2021-07-05 20:46:12,566 INFO client.ConnectionCompletionHandler - connected to 127.0.0.1:1883
|
||||
2021-07-05 20:46:12,586 INFO client.MqttClient - MqttClient reconnect send connect result:true
|
||||
2021-07-05 20:46:12,630 INFO client.DefaultMqttClientProcessor - MqttClient connection succeeded!
|
||||
2021-07-05 20:46:13,932 INFO client.MqttClientTest - /test/123 mica最牛皮
|
||||
```
|
||||
|
||||
此时的 Server 端会打印出如下内容:
|
||||
|
||||
```text
|
||||
2021-07-05 20:46:45,654 INFO server.MqttServerTest - subscribe: /test/client mica最牛皮
|
||||
2021-07-05 20:46:46,926 WARN server.ServerTioConfig -
|
||||
Mica-Mqtt-Server
|
||||
├ 当前时间:1625489206923
|
||||
├ 连接统计
|
||||
│ ├ 共接受过连接数 :1
|
||||
│ ├ 当前连接数 :1
|
||||
│ ├ 异IP连接数 :1
|
||||
│ └ 关闭过的连接数 :0
|
||||
├ 消息统计
|
||||
│ ├ 已处理消息 :20
|
||||
│ ├ 已接收消息(packet/byte):20/584
|
||||
│ ├ 已发送消息(packet/byte):37/935b
|
||||
│ ├ 平均每次TCP包接收的字节数 :29.2
|
||||
│ └ 平均每次TCP包接收的业务包 :1.0
|
||||
└ IP统计时段
|
||||
└ 没有设置ip统计时间
|
||||
├ 节点统计
|
||||
│ ├ clientNodes :1
|
||||
│ ├ 所有连接 :1
|
||||
│ ├ 绑定user数 :0
|
||||
│ ├ 绑定token数 :0
|
||||
│ └ 等待同步消息响应 :0
|
||||
├ 群组
|
||||
│ └ groupmap:0
|
||||
└ 拉黑IP
|
||||
└ []
|
||||
2021-07-05 20:46:46,926 WARN server.ServerTioConfig - Mica-Mqtt-Server, 检查心跳, 共1个连接, 取锁耗时0ms, 循环耗时0ms, 心跳超时时间:120000ms
|
||||
```
|
||||
|
||||
### 3. Client 接入 Aliyun MQTT 服务(示例)
|
||||
|
||||
详见 `example/mica-mqtt-example/src/main/java/net/dreamlu/iot/mqtt/aliyun/MqttClientTest.java`
|
||||
33
example/mica-mqtt-client-solon-plugin-example/pom.xml
Normal file
33
example/mica-mqtt-client-solon-plugin-example/pom.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>example</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<artifactId>mica-mqtt-client-solon-plugin-example</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-client-solon-plugin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon-logging-simple</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.dromara.mica.mqtt.client.solon;
|
||||
|
||||
import org.noear.solon.Solon;
|
||||
import org.noear.solon.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author wsq
|
||||
*/
|
||||
@Configuration
|
||||
public class MqttClientApplication {
|
||||
|
||||
/**
|
||||
* 先启动 mica-mqtt-server-spring-boot-example 再启动本项目,进行测试
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
Solon.start(MqttClientApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.dromara.mica.mqtt.client.solon.controller;
|
||||
|
||||
|
||||
import org.dromara.mica.mqtt.client.solon.service.ClientService;
|
||||
import org.noear.solon.annotation.*;
|
||||
|
||||
@Mapping("/mqtt/client")
|
||||
@Controller
|
||||
public class ClientController {
|
||||
|
||||
@Inject
|
||||
private ClientService service;
|
||||
|
||||
@Post
|
||||
@Mapping("/publish")
|
||||
public boolean publish(String body) {
|
||||
return service.publish(body);
|
||||
}
|
||||
|
||||
@Get
|
||||
@Mapping("/sub")
|
||||
public boolean sub() {
|
||||
return service.sub();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.client.solon.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.client.solon.event.MqttConnectedEvent;
|
||||
import org.dromara.mica.mqtt.core.client.MqttClientCreator;
|
||||
import org.noear.solon.annotation.Component;
|
||||
import org.noear.solon.annotation.Inject;
|
||||
import org.noear.solon.core.event.EventListener;
|
||||
|
||||
/**
|
||||
* 客户端连接状态监听
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MqttClientConnectedListener implements EventListener<MqttConnectedEvent> {
|
||||
@Inject
|
||||
private MqttClientCreator mqttClientCreator;
|
||||
|
||||
@Override
|
||||
public void onEvent(MqttConnectedEvent mqttConnectedEvent) throws Throwable {
|
||||
log.info("MqttConnectedEvent:{}", mqttConnectedEvent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.client.solon.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.client.solon.event.MqttDisconnectEvent;
|
||||
import org.dromara.mica.mqtt.core.client.MqttClientCreator;
|
||||
import org.noear.solon.annotation.Component;
|
||||
import org.noear.solon.annotation.Inject;
|
||||
import org.noear.solon.core.event.EventListener;
|
||||
|
||||
/**
|
||||
* 客户端连接状态监听
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MqttClientDisconnectListener implements EventListener<MqttDisconnectEvent> {
|
||||
@Inject
|
||||
private MqttClientCreator mqttClientCreator;
|
||||
|
||||
@Override
|
||||
public void onEvent(MqttDisconnectEvent mqttDisconnectEvent) throws Throwable {
|
||||
log.info("MqttDisconnectEvent:{}", mqttDisconnectEvent);
|
||||
// 在断线时更新 clientId、username、password,只能改这 3 个,不可调用其他方法。
|
||||
// mqttClientCreator.clientId("newClient" + System.currentTimeMillis())
|
||||
// .username("newUserName")
|
||||
// .password("newPassword");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.dromara.mica.mqtt.client.solon.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.core.annotation.MqttClientSubscribe;
|
||||
import org.dromara.mica.mqtt.codec.message.MqttPublishMessage;
|
||||
import org.dromara.mica.mqtt.core.client.IMqttClientMessageListener;
|
||||
import org.tio.core.ChannelContext;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 客户端消息监听的另一种方式
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
@MqttClientSubscribe("${topic1}")
|
||||
public class MqttClientMessageListener implements IMqttClientMessageListener {
|
||||
|
||||
@Override
|
||||
public void onMessage(ChannelContext context, String topic, MqttPublishMessage message, byte[] payload) {
|
||||
log.info("MqttClientMessageListener,topic:{} payload:{}", topic, new String(payload, StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.dromara.mica.mqtt.client.solon.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.core.annotation.MqttClientSubscribe;
|
||||
import org.dromara.mica.mqtt.client.solon.pojo.User;
|
||||
import org.dromara.mica.mqtt.codec.MqttQoS;
|
||||
import org.dromara.mica.mqtt.codec.message.MqttPublishMessage;
|
||||
import org.dromara.mica.mqtt.core.deserialize.MqttJsonDeserializer;
|
||||
import org.noear.solon.annotation.Component;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 客户端消息监听
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MqttClientSubscribeListener {
|
||||
|
||||
@MqttClientSubscribe("/test/#")
|
||||
public void subQos0(String topic, byte[] payload) {
|
||||
log.info("MqttClientSubscribeListener.subQos0,topic:{} payload:{}", topic, new String(payload, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@MqttClientSubscribe(value = "/qos1/#", qos = MqttQoS.QOS1)
|
||||
public void subQos1(String topic, byte[] payload) {
|
||||
log.info("topic:{} payload:{}", topic, new String(payload, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@MqttClientSubscribe(
|
||||
value = "/test/json",
|
||||
deserialize = MqttJsonDeserializer.class // 2.4.5 开始支持 自定义序列化,默认 json 序列化
|
||||
)
|
||||
public void testJson(String topic, MqttPublishMessage message, Map<String, Object> data) {
|
||||
// solon 插件为 2.4.6 开始支持,支持 2 到 3 个参数,字段类型映射规则(顺序)如下
|
||||
// String 字符串会默认映射到 topic,
|
||||
// Map<String, String> topicVars 会默认映射到 topic 中的变量解析(v2.5.4支持),注意:别跟消息序列化的冲突,消息反序列化不要用 Map<String, String>
|
||||
// MqttPublishMessage 会默认映射到 原始的消息,可以拿到 mqtt5 的 props 参数
|
||||
// byte[] 会映射到 mqtt 消息内容 payload
|
||||
// ByteBuffer 会映射到 mqtt 消息内容 payload
|
||||
// 其他类型会走序列化,确保消息能够序列化,默认为 json 序列化
|
||||
log.info("topic:{} json data:{}", topic, data);
|
||||
}
|
||||
|
||||
@MqttClientSubscribe(value = "/test/object")
|
||||
public void testJson(String topic, MqttPublishMessage message, User<User> data) {
|
||||
log.info("topic:{} json data:{}", topic, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅,参数为可选,但是参数数量必须大于 2,
|
||||
*
|
||||
* @param topic topic 参数,可选参数
|
||||
* @param topicVars 订阅 topic 模板 ${productKey} 中的变量解析(v2.5.4支持),可选参数,注意:类型必须为 Map<String, String>
|
||||
* @param payload 消息内容
|
||||
*/
|
||||
@MqttClientSubscribe("/sys/${productKey}/${deviceName}/thing/sub/register")
|
||||
public void thingSubRegister(String topic, Map<String, String> topicVars, byte[] payload) {
|
||||
// 1.3.8 开始支持,@MqttClientSubscribe 注解支持 ${} 变量替换,会默认替换成 +
|
||||
// 注意:mica-mqtt 会先从 Spring boot 配置中替换参数 ${},如果存在配置会优先被替换。
|
||||
log.info("topic:{} payload:{}", topic, new String(payload, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.dromara.mica.mqtt.client.solon.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class User<T> {
|
||||
private String name;
|
||||
private T girlfriend;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.dromara.mica.mqtt.client.solon.service;
|
||||
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.client.solon.MqttClientTemplate;
|
||||
import org.noear.solon.annotation.Component;
|
||||
import org.noear.solon.annotation.Inject;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author wsq
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ClientService {
|
||||
@Inject
|
||||
private MqttClientTemplate client;
|
||||
|
||||
public boolean publish(String body) {
|
||||
client.publish("/test/client", body.getBytes(StandardCharsets.UTF_8));
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean sub() {
|
||||
client.subQos0("/test/#", (context, topic, message, payload) -> {
|
||||
log.info("{}\t{}", topic, new String(payload, StandardCharsets.UTF_8));
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
server:
|
||||
port: 30303
|
||||
# solon 配置
|
||||
solon:
|
||||
logging:
|
||||
appender:
|
||||
console:
|
||||
level: INFO
|
||||
# mqtt-client 配置
|
||||
mqtt:
|
||||
client:
|
||||
enabled: true # 是否开启客户端,默认:true
|
||||
ip: 127.0.0.1 # 连接的服务端 ip ,默认:127.0.0.1
|
||||
port: 1883 # 端口:默认:1883
|
||||
name: Mica-Mqtt-Client # 名称,默认:Mica-Mqtt-Client
|
||||
clientId: 000001 # 客户端Id(非常重要,一般为设备 sn,不可重复)
|
||||
username: mica # 认证的用户名,注意:2.5.x 开始将 user-name 改成了 username
|
||||
password: mica # 认证的密码
|
||||
timeout: 5 # 超时时间,单位:秒,默认:5秒
|
||||
reconnect: true # 是否重连,默认:true
|
||||
re-interval: 5000 # 重连时间,默认 5000 毫秒
|
||||
version: mqtt_3_1_1 # mqtt 协议版本,可选 MQTT_3_1、mqtt_3_1_1、mqtt_5,默认:mqtt_3_1_1
|
||||
read-buffer-size: 8KB # 接收数据的 buffer size,默认:8k
|
||||
max-bytes-in-message: 10MB # 消息解析最大 bytes 长度,默认:10M
|
||||
keep-alive-secs: 60 # keep-alive 时间,单位:秒
|
||||
heartbeat-mode: LAST_REQ # 心跳模式,支持最后发送或接收心跳时间来计算心跳,默认:最后发送心跳的时间。(2.4.3 开始支持)
|
||||
heartbeat-timeout-strategy: PING # 心跳超时策略,支持发送 PING 和 CLOSE 断开连接,默认:最大努力发送 PING。(2.4.3 开始支持)
|
||||
clean-start: true # session 保留 2.5.x 使用 clean-start,老版本用 clean-session,默认:true
|
||||
ssl:
|
||||
enabled: false # 是否开启 ssl 认证,2.1.0 开始支持双向认证
|
||||
keystore-path: # 可选参数:ssl 双向认证 keystore 目录,支持 classpath:/ 路径。
|
||||
keystore-pass: # 可选参数:ssl 双向认证 keystore 密码
|
||||
truststore-path: # 可选参数:ssl 双向认证 truststore 目录,支持 classpath:/ 路径。
|
||||
truststore-pass: # 可选参数:ssl 双向认证 truststore 密码
|
||||
|
||||
topic1: /test/#
|
||||
6
example/mica-mqtt-client-spring-boot-example/README.md
Normal file
6
example/mica-mqtt-client-spring-boot-example/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
## SpringBoot + mica-mqtt-client 应用演示
|
||||
|
||||
## 启动步骤
|
||||
1. 先启动 mica-mqtt-server-spring-boot-example
|
||||
|
||||
2. 再启动 mica-mqtt-client-spring-boot-example
|
||||
62
example/mica-mqtt-client-spring-boot-example/pom.xml
Normal file
62
example/mica-mqtt-client-spring-boot-example/pom.xml
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>mica-mqtt-client-spring-boot-example</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<url>https://www.dreamlu.net</url>
|
||||
|
||||
<parent>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>example</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-client-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.dreamlu</groupId>
|
||||
<artifactId>mica-lite</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.dreamlu</groupId>
|
||||
<artifactId>mica-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.dreamlu</groupId>
|
||||
<artifactId>mica-openapi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.dromara.mica.mqtt.client;
|
||||
|
||||
import org.dromara.mica.mqtt.spring.client.annotation.EnableMqttClients;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @author wsq
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableMqttClients
|
||||
public class MqttClientApplication {
|
||||
|
||||
/**
|
||||
* 先启动 mica-mqtt-server-spring-boot-example 再启动本项目,进行测试
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MqttClientApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.dromara.mica.mqtt.client.config;
|
||||
|
||||
import org.dromara.mica.mqtt.core.client.DefaultMqttClientSession;
|
||||
import org.dromara.mica.mqtt.core.client.MqttClientCreator;
|
||||
import org.dromara.mica.mqtt.spring.client.MqttClientTemplate;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 示例多个 mqtt client
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Configuration
|
||||
public class OtherMqttClientConfiguration {
|
||||
|
||||
@Bean("mqttClientTemplate1")
|
||||
public MqttClientTemplate mqttClientTemplate1() {
|
||||
// 基于 clientCreator 的配置构建一个新的
|
||||
MqttClientCreator mqttClientCreator1 = new MqttClientCreator()
|
||||
// 修改不同的配置
|
||||
// .ip("mqtt.dreamlu.net")
|
||||
.port(1884)
|
||||
.username("mica")
|
||||
.password("mica")
|
||||
// 避免 client session 冲突
|
||||
.clientSession(new DefaultMqttClientSession());
|
||||
return new MqttClientTemplate(mqttClientCreator1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.dromara.mica.mqtt.client.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.dromara.mica.mqtt.client.service.ClientService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Tag(name = "Mqtt::客户端")
|
||||
@RequestMapping("/mqtt/client")
|
||||
@RestController
|
||||
public class ClientController {
|
||||
|
||||
@Autowired
|
||||
private ClientService service;
|
||||
|
||||
@Operation(summary = "publish")
|
||||
@PostMapping("/publish")
|
||||
public boolean publish(@RequestBody String body) {
|
||||
service.publish(body);
|
||||
service.publishHelloInterfaceA(body);
|
||||
service.publishHelloInterfaceB(body);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Operation(summary = "sub")
|
||||
@GetMapping("/sub")
|
||||
public boolean sub() {
|
||||
return service.sub();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.client.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.core.client.MqttClientCreator;
|
||||
import org.dromara.mica.mqtt.spring.client.event.MqttConnectedEvent;
|
||||
import org.dromara.mica.mqtt.spring.client.event.MqttDisconnectEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 客户端连接状态监听
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class MqttClientConnectListener {
|
||||
|
||||
@Autowired
|
||||
private MqttClientCreator mqttClientCreator;
|
||||
|
||||
@EventListener
|
||||
public void onConnected(MqttConnectedEvent event) {
|
||||
log.info("MqttConnectedEvent:{}", event);
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void onDisconnect(MqttDisconnectEvent event) {
|
||||
log.info("MqttDisconnectEvent:{}", event);
|
||||
// 在断线时更新 clientId、username、password,只能改这 3 个,不可调用其他方法。
|
||||
// mqttClientCreator.clientId("newClient" + System.currentTimeMillis())
|
||||
// .username("newUserName")
|
||||
// .password("newPassword");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.dromara.mica.mqtt.client.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.codec.message.MqttPublishMessage;
|
||||
import org.dromara.mica.mqtt.core.client.IMqttClientMessageListener;
|
||||
import org.dromara.mica.mqtt.core.annotation.MqttClientSubscribe;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.tio.core.ChannelContext;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 客户端消息监听的另一种方式
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@MqttClientSubscribe("${topic1}")
|
||||
public class MqttClientMessageListener implements IMqttClientMessageListener {
|
||||
|
||||
@Override
|
||||
public void onMessage(ChannelContext context, String topic, MqttPublishMessage message, byte[] payload) {
|
||||
log.info("topic:{} payload:{}", topic, new String(payload, StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.dromara.mica.mqtt.client.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.client.pojo.User;
|
||||
import org.dromara.mica.mqtt.codec.message.MqttPublishMessage;
|
||||
import org.dromara.mica.mqtt.codec.MqttQoS;
|
||||
import org.dromara.mica.mqtt.core.deserialize.MqttJsonDeserializer;
|
||||
import org.dromara.mica.mqtt.core.annotation.MqttClientSubscribe;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 客户端消息监听
|
||||
*
|
||||
* @author L.cm
|
||||
* @author ChangJin Wei(魏昌进)
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class MqttClientSubscribeListener {
|
||||
|
||||
@MqttClientSubscribe("/test/#")
|
||||
public void subQos0(String topic, byte[] payload) {
|
||||
log.info("topic:{} payload:{}", topic, new String(payload, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@MqttClientSubscribe(value = "/qos1/#", qos = MqttQoS.QOS1)
|
||||
public void subQos1(String topic, byte[] payload) {
|
||||
log.info("topic:{} payload:{}", topic, new String(payload, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@MqttClientSubscribe(
|
||||
value = "/test/json",
|
||||
deserialize = MqttJsonDeserializer.class // 2.4.5 开始支持 自定义序列化,默认 json 序列化
|
||||
)
|
||||
public void testJson(String topic, MqttPublishMessage message, Map<String, Object> data) {
|
||||
// 2.4.5 开始支持,支持 2 到 3 个参数,字段类型映射规则(顺序)如下:
|
||||
// String 字符串会默认映射到 topic,
|
||||
// Map<String, String> topicVars 会默认映射到 topic 中的变量解析(v2.5.4支持),注意:别跟消息序列化的冲突,消息反序列化不要用 Map<String, String>
|
||||
// MqttPublishMessage 会默认映射到 原始的消息,可以拿到 mqtt5 的 props 参数
|
||||
// byte[] 会映射到 mqtt 消息内容 payload
|
||||
// ByteBuffer 会映射到 mqtt 消息内容 payload
|
||||
// 其他类型会走序列化,确保消息能够序列化,默认为 json 序列化
|
||||
log.info("topic:{} json data:{}", topic, data);
|
||||
}
|
||||
|
||||
@MqttClientSubscribe(value = "/test/object")
|
||||
public void testJson(String topic, MqttPublishMessage message, User<User> data) {
|
||||
log.info("topic:{} json data:{}", topic, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅,参数为可选,但是参数数量必须大于 2,
|
||||
*
|
||||
* @param topic topic 参数,可选参数
|
||||
* @param topicVars 订阅 topic 模板 ${productKey} 中的变量解析(v2.5.4支持),可选参数,注意:类型必须为 Map<String, String>
|
||||
* @param payload 消息内容,以字节数组形式提供,可选参数,也可支持对象形式,默认 json 序列化
|
||||
*/
|
||||
@MqttClientSubscribe("/sys/${productKey}/${deviceName}/thing/sub/register")
|
||||
public void thingSubRegister(String topic, Map<String, String> topicVars, byte[] payload) {
|
||||
// 1.3.8 开始支持,@MqttClientSubscribe 注解支持 ${} 变量替换,会默认替换成 +
|
||||
// 注意:mica-mqtt 会先从 Spring boot 配置中替换参数 ${},如果存在配置会优先被替换。
|
||||
log.info("topic:{} payload:{}", topic, new String(payload, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.dromara.mica.mqtt.client.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.codec.message.MqttPublishMessage;
|
||||
import org.dromara.mica.mqtt.core.annotation.MqttClientSubscribe;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 客户端消息监听,注解在方法上
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class OtherMqttClientSubscribeListener {
|
||||
|
||||
@MqttClientSubscribe(
|
||||
value = {
|
||||
"$share/iothub/test/${a}",
|
||||
"/test/${arg1}/${arg2}/${arg3}/${arg4}"
|
||||
},
|
||||
clientTemplateBean = "mqttClientTemplate1"
|
||||
)
|
||||
public void sub(String topic, MqttPublishMessage message, byte[] payload) {
|
||||
log.info("topic:{} payload:{}", topic, new String(payload));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.dromara.mica.mqtt.client.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class User<T> {
|
||||
private String name;
|
||||
private T girlfriend;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.dromara.mica.mqtt.client.service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.spring.client.MqttClientTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author wsq
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ClientService {
|
||||
/**
|
||||
* 使用 默认的 mqtt client
|
||||
*/
|
||||
@Autowired
|
||||
@Qualifier(MqttClientTemplate.DEFAULT_CLIENT_TEMPLATE_BEAN)
|
||||
private MqttClientTemplate client;
|
||||
|
||||
@Autowired
|
||||
private HelloInterfaceA helloInterfaceA;
|
||||
|
||||
@Autowired
|
||||
private HelloInterfaceB helloInterfaceB;
|
||||
|
||||
public boolean publish(String body) {
|
||||
client.publish("/test/client", body.getBytes(StandardCharsets.UTF_8));
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean publishHelloInterfaceA(String body) {
|
||||
helloInterfaceA.sayHello(body.getBytes(StandardCharsets.UTF_8));
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean publishHelloInterfaceB(String body) {
|
||||
helloInterfaceB.sayHello(body.getBytes(StandardCharsets.UTF_8));
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean sub() {
|
||||
client.subQos0("/test/#", (context, topic, message, payload) -> {
|
||||
log.info("{}\t{}", topic, new String(payload, StandardCharsets.UTF_8));
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.client.service;
|
||||
|
||||
import org.dromara.mica.mqtt.core.annotation.MqttClientPublish;
|
||||
import org.dromara.mica.mqtt.core.annotation.MqttPayload;
|
||||
import org.dromara.mica.mqtt.spring.client.annotation.MqttClient;
|
||||
|
||||
/**
|
||||
* @author ChangJin Wei (魏昌进)
|
||||
*/
|
||||
@MqttClient
|
||||
public interface HelloInterfaceA {
|
||||
|
||||
@MqttClientPublish("/test/HelloInterfaceA")
|
||||
void sayHello(@MqttPayload Object payload);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.client.service;
|
||||
|
||||
import org.dromara.mica.mqtt.core.annotation.MqttClientPublish;
|
||||
import org.dromara.mica.mqtt.core.annotation.MqttPayload;
|
||||
import org.dromara.mica.mqtt.spring.client.annotation.MqttClient;
|
||||
|
||||
/**
|
||||
* @author ChangJin Wei (魏昌进)
|
||||
*/
|
||||
@MqttClient
|
||||
public interface HelloInterfaceB {
|
||||
|
||||
@MqttClientPublish("/test/HelloInterfaceB")
|
||||
void sayHello(@MqttPayload Object payload);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
server:
|
||||
port: 30012
|
||||
spring:
|
||||
application:
|
||||
name: mica-mqtt-client
|
||||
# mqtt-client 配置
|
||||
mqtt:
|
||||
client:
|
||||
enabled: true # 是否开启客户端,默认:true
|
||||
ip: 127.0.0.1 # 连接的服务端 ip ,默认:127.0.0.1
|
||||
port: 1883 # 端口:默认:1883
|
||||
name: Mica-Mqtt-Client # 名称,默认:Mica-Mqtt-Client
|
||||
clientId: "000001" # 客户端Id(非常重要,一般为设备 sn,不可重复)
|
||||
username: mica # 认证的用户名,注意:2.5.x 开始将 user-name 改成了 username
|
||||
password: 123456 # 认证的密码
|
||||
timeout: 5 # 超时时间,单位:秒,默认:5秒
|
||||
reconnect: true # 是否重连,默认:true
|
||||
re-interval: 5000 # 重连时间,默认 5000 毫秒
|
||||
version: mqtt_3_1_1 # mqtt 协议版本,可选 MQTT_3_1、mqtt_3_1_1、mqtt_5,默认:mqtt_3_1_1
|
||||
read-buffer-size: 8KB # 接收数据的 buffer size,默认:8k
|
||||
max-bytes-in-message: 10MB # 消息解析最大 bytes 长度,默认:10M
|
||||
keep-alive-secs: 60 # keep-alive 时间,单位:秒
|
||||
heartbeat-mode: LAST_REQ # 心跳模式,支持最后发送或接收心跳时间来计算心跳,默认:最后发送心跳的时间。(2.4.3 开始支持)
|
||||
heartbeat-timeout-strategy: PING # 心跳超时策略,支持发送 PING 和 CLOSE 断开连接,默认:最大努力发送 PING。(2.4.3 开始支持)
|
||||
clean-start: true # session 保留 2.5.x 使用 clean-start,老版本用 clean-session,默认:true
|
||||
will-message:
|
||||
topic: /test/offline
|
||||
message: down
|
||||
ssl:
|
||||
enabled: false # 是否开启 ssl 认证,2.1.0 开始支持双向认证
|
||||
keystore-path: # 可选参数:ssl 双向认证 keystore 目录,支持 classpath:/ 路径。
|
||||
keystore-pass: # 可选参数:ssl 双向认证 keystore 密码
|
||||
truststore-path: # 可选参数:ssl 双向认证 truststore 目录,支持 classpath:/ 路径。
|
||||
truststore-pass: # 可选参数:ssl 双向认证 truststore 密码
|
||||
topic1: /test2/#
|
||||
springdoc:
|
||||
swagger-ui:
|
||||
urls:
|
||||
- name: swagger
|
||||
url: /v3/api-docs
|
||||
logging:
|
||||
level:
|
||||
root: info
|
||||
server: info # t-io 服务端默认日志
|
||||
org.tio: info # t-io 服务端默认日志
|
||||
org.dromara.mica.mqtt: info # mica-mqtt 日志
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
${AnsiColor.BRIGHT_BLUE}## ## #### ###### ### ${AnsiColor.RED} ## ## ####### ######## ########
|
||||
${AnsiColor.BRIGHT_BLUE}### ### ## ## ## ## ## ${AnsiColor.RED} ### ### ## ## ## ##
|
||||
${AnsiColor.BRIGHT_BLUE}#### #### ## ## ## ## ${AnsiColor.RED} #### #### ## ## ## ##
|
||||
${AnsiColor.BRIGHT_BLUE}## ### ## ## ## ## ##${AnsiColor.RED} ## ### ## ## ## ## ##
|
||||
${AnsiColor.BRIGHT_BLUE}## ## ## ## #########${AnsiColor.RED} ## ## ## ## ## ## ##
|
||||
${AnsiColor.BRIGHT_BLUE}## ## ## ## ## ## ##${AnsiColor.RED} ## ## ## ## ## ##
|
||||
${AnsiColor.BRIGHT_BLUE}## ## #### ###### ## ##${AnsiColor.RED} ## ## ##### ## ## ##
|
||||
|
||||
https://www.dreamlu.net
|
||||
|
||||
${AnsiColor.BRIGHT_BLUE}:: ${spring.application.name} :: Running Spring Boot ${spring-boot.version} 🏃🏃🏃 ${AnsiColor.DEFAULT}
|
||||
183
example/mica-mqtt-example/pom.xml
Normal file
183
example/mica-mqtt-example/pom.xml
Normal file
@@ -0,0 +1,183 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>mica-mqtt-example</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
|
||||
<parent>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>example</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<graalvm.version>25.0.1</graalvm.version>
|
||||
<mainClass.server>org.dromara.mica.mqtt.server.MqttServerTest</mainClass.server>
|
||||
<mainClass.client>org.dromara.mica.mqtt.client.MqttClientTest</mainClass.client>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-server</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>jar</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<!-- 非 GraalVM 环境用 tinylog -->
|
||||
<dependency>
|
||||
<groupId>org.tinylog</groupId>
|
||||
<artifactId>slf4j-tinylog</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.tinylog</groupId>
|
||||
<artifactId>tinylog-impl</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>${mainClass.server}</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>mqtt-server-graal</id>
|
||||
<dependencies>
|
||||
<!-- GraalVM 环境使用 jdk log -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
</dependency>
|
||||
<!-- GraalVM -->
|
||||
<dependency>
|
||||
<groupId>org.graalvm.sdk</groupId>
|
||||
<artifactId>graal-sdk</artifactId>
|
||||
<version>${graalvm.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<finalName>mqtt-server-graal</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.graalvm.nativeimage</groupId>
|
||||
<artifactId>native-image-maven-plugin</artifactId>
|
||||
<version>21.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>native-image</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<skip>false</skip>
|
||||
<imageName>mqtt-server-graalvm</imageName>
|
||||
<mainClass>${mainClass.server}</mainClass>
|
||||
<buildArgs>
|
||||
-H:+RemoveSaturatedTypeFlows
|
||||
--allow-incomplete-classpath
|
||||
--no-fallback
|
||||
</buildArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>mqtt-client-graal</id>
|
||||
<dependencies>
|
||||
<!-- GraalVM 环境使用 jdk log -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
</dependency>
|
||||
<!-- GraalVM -->
|
||||
<dependency>
|
||||
<groupId>org.graalvm.sdk</groupId>
|
||||
<artifactId>graal-sdk</artifactId>
|
||||
<version>${graalvm.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<finalName>mqtt-client-graal</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.graalvm.nativeimage</groupId>
|
||||
<artifactId>native-image-maven-plugin</artifactId>
|
||||
<version>21.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>native-image</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<skip>false</skip>
|
||||
<imageName>mqtt-client-graalvm</imageName>
|
||||
<mainClass>${mainClass.client}</mainClass>
|
||||
<buildArgs>
|
||||
-H:+RemoveSaturatedTypeFlows
|
||||
--allow-incomplete-classpath
|
||||
--no-fallback
|
||||
</buildArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.aliyun;
|
||||
|
||||
import org.dromara.mica.mqtt.core.client.MqttClient;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* 客户端测试
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttClientTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
String productKey = "g27jB42P9hm";
|
||||
String deviceName = "3dbc1cb4";
|
||||
String deviceSecret = "";
|
||||
// 计算MQTT连接参数。
|
||||
MqttSign sign = new MqttSign(productKey, deviceName, deviceSecret);
|
||||
|
||||
String username = sign.getUsername();
|
||||
String password = sign.getPassword();
|
||||
String clientId = sign.getClientId();
|
||||
System.out.println("username: " + username);
|
||||
System.out.println("password: " + password);
|
||||
System.out.println("clientid: " + clientId);
|
||||
|
||||
// 初始化 mqtt 客户端
|
||||
MqttClient client = MqttClient.create()
|
||||
.ip(productKey + ".iot-as-mqtt.cn-shanghai.aliyuncs.com")
|
||||
.port(443)
|
||||
.username(username)
|
||||
.password(password)
|
||||
.clientId(clientId)
|
||||
.connectSync();
|
||||
|
||||
client.subQos0("/sys/" + productKey + '/' + deviceName + "/thing/event/property/post_reply", (context, topic, message, payload) -> {
|
||||
System.out.println(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
|
||||
});
|
||||
|
||||
client.schedule(() -> {
|
||||
int LightSwitch = ThreadLocalRandom.current().nextBoolean() ? 0 : 1;
|
||||
String content = "{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":" + LightSwitch + "}}";
|
||||
client.publish("/sys/" + productKey + "/" + deviceName + "/thing/event/property/post", content.getBytes(StandardCharsets.UTF_8));
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.aliyun;
|
||||
|
||||
import org.tio.utils.mica.DigestUtils;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 阿里云 mqtt 签名方式
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttSign {
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private final String username;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private final String password;
|
||||
/**
|
||||
* 客户端id
|
||||
*/
|
||||
private final String clientId;
|
||||
|
||||
public MqttSign(String productKey, String deviceName, String deviceSecret) {
|
||||
Objects.requireNonNull(productKey, "productKey is null");
|
||||
Objects.requireNonNull(deviceName, "deviceName is null");
|
||||
Objects.requireNonNull(deviceSecret, "deviceSecret is null");
|
||||
this.username = deviceName + '&' + productKey;
|
||||
String timestamp = Long.toString(System.currentTimeMillis());
|
||||
this.password = getPassword(productKey, deviceName, deviceSecret, timestamp);
|
||||
this.clientId = getClientId(productKey, deviceName, timestamp);
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
private static String getPassword(String productKey, String deviceName, String deviceSecret, String timestamp) {
|
||||
String plainPwd = "clientId" + productKey + '.' + deviceName + "deviceName" +
|
||||
deviceName + "productKey" + productKey + "timestamp" + timestamp;
|
||||
return hmacSha256(plainPwd, deviceSecret);
|
||||
}
|
||||
|
||||
private static String getClientId(String productKey, String deviceName, String timestamp) {
|
||||
return productKey + '.' + deviceName + "|timestamp=" + timestamp + ",_v=paho-java-1.0.0,securemode=2,signmethod=hmacsha256|";
|
||||
}
|
||||
|
||||
private static String hmacSha256(String plainText, String key) {
|
||||
if (plainText == null || key == null) {
|
||||
return null;
|
||||
}
|
||||
return DigestUtils.hmacSha256Hex(plainText, key);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.benchmark;
|
||||
|
||||
import org.dromara.mica.mqtt.core.client.MqttClient;
|
||||
import org.tio.utils.hutool.StrUtil;
|
||||
import org.tio.utils.thread.ThreadUtils;
|
||||
import org.tio.utils.thread.pool.SynThreadPoolExecutor;
|
||||
import org.tio.utils.timer.DefaultTimerTaskService;
|
||||
import org.tio.utils.timer.TimerTaskService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
/**
|
||||
* mqtt 压力测试
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttBenchmark {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 注意: windows 上需要修改最大的 Tcp 连接数,不然超不过 2W。
|
||||
// 《修改Windows服务器最大的Tcp连接数》:https://www.jianshu.com/p/00136a97d2d8
|
||||
int connCount = 5_0000;
|
||||
String ip = "127.0.0.1";
|
||||
// 优化:使用ArrayList+预分配容量,性能比CopyOnWriteArrayList好很多
|
||||
// 对于压测场景,不需要线程安全的写操作(因为是单线程创建客户端)
|
||||
final List<MqttClient> clientList = new ArrayList<>(connCount);
|
||||
SynThreadPoolExecutor tioExecutor = ThreadUtils.getTioExecutor();
|
||||
ExecutorService groupExecutor = ThreadUtils.getGroupExecutor();
|
||||
// 自定义全局 taskService,避免每个 client new,创建过多线程
|
||||
TimerTaskService taskService = new DefaultTimerTaskService(200L, 60);
|
||||
for (int i = 0; i < connCount; i++) {
|
||||
newClient(ip, i, clientList, tioExecutor, groupExecutor, taskService);
|
||||
}
|
||||
}
|
||||
|
||||
private static void newClient(String ip, int i, final List<MqttClient> clientList,
|
||||
SynThreadPoolExecutor tioExecutor,
|
||||
ExecutorService groupExecutor,
|
||||
TimerTaskService taskService) {
|
||||
MqttClient client = MqttClient.create()
|
||||
.ip(ip)
|
||||
.clientId(StrUtil.getNanoId() + i)
|
||||
.readBufferSize(128)
|
||||
// 取消自动重连
|
||||
.reconnect(false)
|
||||
.tioExecutor(tioExecutor)
|
||||
.groupExecutor(groupExecutor)
|
||||
.mqttExecutor(tioExecutor)
|
||||
.taskService(taskService)
|
||||
.connect();
|
||||
clientList.add(client);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.dromara.mica.mqtt.benchmark;
|
||||
|
||||
import org.dromara.mica.mqtt.codec.MqttQoS;
|
||||
import org.dromara.mica.mqtt.core.client.MqttClient;
|
||||
import org.tio.utils.hutool.StrUtil;
|
||||
import org.tio.utils.thread.ThreadUtils;
|
||||
import org.tio.utils.thread.pool.SynThreadPoolExecutor;
|
||||
import org.tio.utils.timer.DefaultTimerTaskService;
|
||||
import org.tio.utils.timer.TimerTaskService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* mqtt 发布端测试
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttPublishBench {
|
||||
|
||||
public static void main(String[] args) {
|
||||
int clientCount = 10;
|
||||
int publishCount = 10000;
|
||||
MqttQoS qos = MqttQoS.QOS0;
|
||||
List<MqttClient> clients = getClient(clientCount);
|
||||
Executors.newScheduledThreadPool(ThreadUtils.AVAILABLE_PROCESSORS).scheduleWithFixedDelay(() -> {
|
||||
for (MqttClient mqttClient : clients) {
|
||||
for (int j = 0; j < publishCount; j++) {
|
||||
byte[] payload = new byte[1024 + j];
|
||||
Arrays.fill(payload, (byte) -1);
|
||||
mqttClient.publish("/topic/" + j, payload, qos);
|
||||
}
|
||||
}
|
||||
}, 1L, 1L, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public static List<MqttClient> getClient(int clientCount) {
|
||||
SynThreadPoolExecutor tioExecutor = ThreadUtils.getTioExecutor();
|
||||
ExecutorService groupExecutor = ThreadUtils.getGroupExecutor();
|
||||
TimerTaskService taskService = new DefaultTimerTaskService();
|
||||
List<MqttClient> clients = new ArrayList<>();
|
||||
for (int i = 0; i < clientCount; i++) {
|
||||
MqttClient client = MqttClient.create()
|
||||
.clientId(StrUtil.getNanoId())
|
||||
.tioExecutor(tioExecutor)
|
||||
.groupExecutor(groupExecutor)
|
||||
.mqttExecutor(groupExecutor)
|
||||
.taskService(taskService)
|
||||
.connectSync();
|
||||
clients.add(client);
|
||||
}
|
||||
return clients;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.benchmark;
|
||||
|
||||
import org.dromara.mica.mqtt.core.server.MqttServer;
|
||||
|
||||
/**
|
||||
* mqtt 服务端测试
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttServerBench {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 设定日志级别为 error
|
||||
System.setProperty("tinylog.writer.level", "error");
|
||||
// 启动 mqtt 服务
|
||||
MqttServer.create()
|
||||
.enableMqtt()
|
||||
.statEnable(false)
|
||||
.start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.broker;
|
||||
|
||||
import org.dromara.mica.mqtt.core.client.MqttClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 设备 A,这里默认 APP 应用端
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class DeviceA {
|
||||
private static final Logger logger = LoggerFactory.getLogger(DeviceA.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 初始化 mqtt 客户端
|
||||
MqttClient client = MqttClient.create()
|
||||
.ip("127.0.0.1")
|
||||
.port(1883)
|
||||
.username("admin")
|
||||
.password("123456")
|
||||
.connectSync();
|
||||
|
||||
client.subQos0("/a/door/open", (context, topic, message, payload) -> {
|
||||
logger.info(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.broker;
|
||||
|
||||
import org.dromara.mica.mqtt.core.client.MqttClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 设备 B,这里默认 web 端
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class DeviceB {
|
||||
private static final Logger logger = LoggerFactory.getLogger(DeviceB.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 初始化 mqtt 客户端
|
||||
MqttClient client = MqttClient.create()
|
||||
.ip("127.0.0.1")
|
||||
.port(1883)
|
||||
.username("admin")
|
||||
.password("123456")
|
||||
.connectSync();
|
||||
|
||||
client.subQos0("/a/door/open", (context, topic, message, payload) -> {
|
||||
logger.info(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.broker;
|
||||
|
||||
import org.dromara.mica.mqtt.core.client.MqttClient;
|
||||
|
||||
/**
|
||||
* 设备 C,每 5 秒上报一个数据
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class DeviceC {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 初始化 mqtt 客户端
|
||||
MqttClient client = MqttClient.create()
|
||||
.ip("127.0.0.1")
|
||||
.port(1883)
|
||||
.username("admin")
|
||||
.password("123456")
|
||||
.connectSync();
|
||||
|
||||
client.schedule(() -> {
|
||||
client.publish("/a/door/open", null);
|
||||
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.dromara.mica.mqtt.broker;
|
||||
|
||||
import org.dromara.mica.mqtt.codec.MqttQoS;
|
||||
import org.dromara.mica.mqtt.core.annotation.MqttRetain;
|
||||
import org.dromara.mica.mqtt.core.client.MqttClient;
|
||||
import org.dromara.mica.mqtt.core.annotation.MqttClientPublish;
|
||||
import org.dromara.mica.mqtt.core.annotation.MqttPayload;
|
||||
|
||||
/**
|
||||
* @author ChangJin Wei (魏昌进)
|
||||
*/
|
||||
public class DeviceD {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 初始化 mqtt 客户端
|
||||
MqttClient client = MqttClient.create()
|
||||
.ip("127.0.0.1")
|
||||
.port(1883)
|
||||
.username("admin")
|
||||
.password("123456")
|
||||
.connectSync();
|
||||
|
||||
|
||||
DoorClient doorClient = client.getInterface(DoorClient.class);
|
||||
|
||||
client.schedule(() -> {
|
||||
doorClient.sendMessage("open", false);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
public interface DoorClient {
|
||||
|
||||
@MqttClientPublish(value = "/a/door/open", qos = MqttQoS.QOS0)
|
||||
void sendMessage(@MqttPayload String message, @MqttRetain boolean retain);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.broker;
|
||||
|
||||
import org.dromara.mica.mqtt.core.server.MqttServer;
|
||||
|
||||
/**
|
||||
* 服务端,单纯的做消息转发
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class Server {
|
||||
|
||||
/**
|
||||
* 客户端 A 模拟 APP 端订阅 `/a/door/open`,
|
||||
* 客户端 B 模拟 web 网页端 mqtt.js 订阅 `/a/door/open`,
|
||||
* Mqtt 服务端实现 `IMqttMessageListener`,将消息转交给 `AbstractMqttMessageDispatcher`(自定义实现)处理。
|
||||
* 客户端 C 定时上报转态给 `/a/door/open`
|
||||
* 结果:A 和 B 将收到 C 或 D 发布的消息,并完成相应的效果展示。
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// 启动服务,mica-mqtt 1.3.x 已经默认为 broker 模式
|
||||
MqttServer.create()
|
||||
.enableMqtt()
|
||||
.debug()
|
||||
.start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.client;
|
||||
|
||||
import org.dromara.mica.mqtt.codec.message.MqttPublishMessage;
|
||||
import org.dromara.mica.mqtt.codec.MqttQoS;
|
||||
import org.dromara.mica.mqtt.codec.MqttVersion;
|
||||
import org.dromara.mica.mqtt.core.client.IMqttClientMessageListener;
|
||||
import org.dromara.mica.mqtt.core.client.MqttClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tio.core.ChannelContext;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 客户端测试
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class Mqtt5ClientTest {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Mqtt5ClientTest.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 初始化 mqtt 客户端
|
||||
MqttClient client = MqttClient.create()
|
||||
.ip("127.0.0.1")
|
||||
.port(1883)
|
||||
.username("mica")
|
||||
.password("mica")
|
||||
.version(MqttVersion.MQTT_5)
|
||||
.cleanStart(false)
|
||||
.sessionExpiryIntervalSecs(7200)
|
||||
.connectListener(new MqttClientConnectListener())
|
||||
.willMessage(builder -> {
|
||||
builder.topic("/test/offline")
|
||||
.messageText("down")
|
||||
.retain(false)
|
||||
.qos(MqttQoS.QOS0); // 遗嘱消息
|
||||
})
|
||||
// 同步连接,也可以使用 connect() 异步(可以避免 broker 没启动照成启动卡住),但是下面的订阅和发布可能还没连接成功。
|
||||
.connectSync();
|
||||
|
||||
client.subQos0("/test/123", new IMqttClientMessageListener() {
|
||||
@Override
|
||||
public void onSubscribed(ChannelContext context, String topicFilter, MqttQoS mqttQoS) {
|
||||
// 订阅成功之后触发,可在此处做一些业务逻辑
|
||||
logger.info("topicFilter:{} MqttQoS:{} 订阅成功!!!", topicFilter, mqttQoS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(ChannelContext context, String topic, MqttPublishMessage message, byte[] payload) {
|
||||
logger.info(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
|
||||
}
|
||||
});
|
||||
|
||||
client.publish("/test/client", "mica最牛皮1".getBytes(StandardCharsets.UTF_8));
|
||||
client.publish("/test/client", "mica最牛皮2".getBytes(StandardCharsets.UTF_8));
|
||||
client.publish("/test/client", "mica最牛皮3".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
client.schedule(() -> {
|
||||
client.publish("/test/client", "mica最牛皮".getBytes(StandardCharsets.UTF_8));
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.client;
|
||||
|
||||
import org.dromara.mica.mqtt.codec.codes.MqttConnectReasonCode;
|
||||
import org.dromara.mica.mqtt.core.client.MqttClient;
|
||||
|
||||
/**
|
||||
* 客户端测试
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttClientConnTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 初始化 mqtt 客户端
|
||||
MqttConnectReasonCode reasonCode = MqttClient.create()
|
||||
// .ip("127.0.0.1")
|
||||
.ip("mqtt.dreamlu.net")
|
||||
.port(1883)
|
||||
.username("mica")
|
||||
.password("mica1")
|
||||
.connectTest();
|
||||
System.out.println(reasonCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.client;
|
||||
|
||||
import org.dromara.mica.mqtt.core.client.IMqttClientConnectListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tio.core.ChannelContext;
|
||||
|
||||
/**
|
||||
* 客户端连接状态监听
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttClientConnectListener implements IMqttClientConnectListener {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MqttClientConnectListener.class);
|
||||
|
||||
@Override
|
||||
public void onConnected(ChannelContext context, boolean isReconnect) {
|
||||
String clientId = context.getId();
|
||||
if (isReconnect) {
|
||||
logger.info("重连 mqtt 服务器重连成功... clientId:{}", clientId);
|
||||
} else {
|
||||
logger.info("连接 mqtt 服务器成功... clientId:{}", clientId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnect(ChannelContext context, Throwable throwable, String remark, boolean isRemove) {
|
||||
String clientId = context.getId();
|
||||
logger.error("mqtt 链接断开 remark:{} isRemove:{} clientId:{}", remark, isRemove, clientId, throwable);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.client;
|
||||
|
||||
import org.dromara.mica.mqtt.core.client.MqttClient;
|
||||
import org.tio.utils.buffer.ByteBufferUtil;
|
||||
|
||||
/**
|
||||
* 客户端全局订阅测试
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttClientGlobalTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 初始化 mqtt 客户端
|
||||
MqttClient.create()
|
||||
.ip("127.0.0.1")
|
||||
.port(1883)
|
||||
.username("admin")
|
||||
.password("123456")
|
||||
// 采用 globalSubscribe,保留 session 停机重启后,可以接受到离线消息,注意:clientId 要不能变化。
|
||||
.clientId("globalTest")
|
||||
.cleanStart(false)
|
||||
// 全局订阅的 topic
|
||||
.globalSubscribe("/test", "/test/123", "/debug/#")
|
||||
// 全局监听,也会监听到服务端 http api 订阅的数据
|
||||
.globalMessageListener((context, topic, message, payload) -> {
|
||||
System.out.println("topic:\t" + topic);
|
||||
System.out.println("payload:\t" + ByteBufferUtil.toString(payload));
|
||||
})
|
||||
// .debug()
|
||||
.connectSync();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.client;
|
||||
|
||||
import org.dromara.mica.mqtt.core.client.MqttClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 客户端测试
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttClientSyncTest {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MqttClientSyncTest.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 初始化 mqtt 客户端
|
||||
MqttClient client = MqttClient.create()
|
||||
.ip("127.0.0.1")
|
||||
.port(1883)
|
||||
.username("mica")
|
||||
.password("mica")
|
||||
.connectListener(new MqttClientConnectListener())
|
||||
// 同步连接,注意:连接会阻塞
|
||||
.connectSync();
|
||||
|
||||
client.subQos0("/test/#", (context, topic, message, payload) -> {
|
||||
logger.info(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
|
||||
});
|
||||
|
||||
client.unSubscribe("/test/#", "/test/123");
|
||||
|
||||
// 连接上之后发送消息,注意:连接时出现异常等就不会发出
|
||||
client.publish("/test/client", "mica最牛皮".getBytes(StandardCharsets.UTF_8));
|
||||
// 2.3.0 开始支持,可停止
|
||||
// client.stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.client;
|
||||
|
||||
import org.dromara.mica.mqtt.codec.message.MqttPublishMessage;
|
||||
import org.dromara.mica.mqtt.codec.MqttQoS;
|
||||
import org.dromara.mica.mqtt.core.client.IMqttClientMessageListener;
|
||||
import org.dromara.mica.mqtt.core.client.MqttClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tio.core.ChannelContext;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 客户端测试
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttClientTest {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MqttClientTest.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 初始化 mqtt 客户端
|
||||
MqttClient client = MqttClient.create()
|
||||
.ip("127.0.0.1")
|
||||
.port(1883)
|
||||
.username("mica")
|
||||
.password("mica")
|
||||
// 绑定网卡 v2.5.1 添加,支持 bindIp(网卡ip)和 bindNetworkInterface(网卡名)取一即可
|
||||
// .bindIp("127.0.0.1")
|
||||
// .bindNetworkInterface("lo")
|
||||
// 如果包体过大,建议将此参数设置和 maxBytesInMessage 一样大
|
||||
// .readBufferSize(1024 * 10)
|
||||
// 最大包体长度,如果包体过大需要设置此参数
|
||||
// .maxBytesInMessage(1024 * 10)
|
||||
// .version(MqttVersion.MQTT_5)
|
||||
// 连接监听
|
||||
.connectListener(new MqttClientConnectListener())
|
||||
// 遗嘱消息
|
||||
.willMessage(builder -> {
|
||||
builder.topic("/test/offline")
|
||||
.messageText("down")
|
||||
.retain(false)
|
||||
.qos(MqttQoS.QOS0)
|
||||
// mqtt5 遗嘱消息属性
|
||||
.willProperties(props -> {
|
||||
props.setWillDelayInterval(1000);
|
||||
props.setContentType("text/plain");
|
||||
});
|
||||
})
|
||||
// 同步连接,也可以使用 connect() 异步(可以避免 broker 没启动照成启动卡住),但是下面的订阅和发布可能还没连接成功。
|
||||
.connectSync();
|
||||
|
||||
client.subQos0("/test/#", new IMqttClientMessageListener() {
|
||||
@Override
|
||||
public void onSubscribed(ChannelContext context, String topicFilter, MqttQoS mqttQoS) {
|
||||
// 订阅成功之后触发,可在此处做一些业务逻辑
|
||||
logger.info("topicFilter:{} MqttQoS:{} 订阅成功!!!", topicFilter, mqttQoS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(ChannelContext context, String topic, MqttPublishMessage message, byte[] payload) {
|
||||
logger.info(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
|
||||
}
|
||||
});
|
||||
|
||||
client.subQos0("/test/1", new IMqttClientMessageListener() {
|
||||
@Override
|
||||
public void onSubscribed(ChannelContext context, String topicFilter, MqttQoS mqttQoS) {
|
||||
// 订阅成功之后触发,可在此处做一些业务逻辑
|
||||
logger.info("topicFilter:{} MqttQoS:{} 订阅成功!!!", topicFilter, mqttQoS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(ChannelContext context, String topic, MqttPublishMessage message, byte[] payload) {
|
||||
logger.info(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
|
||||
}
|
||||
});
|
||||
|
||||
client.subQos0("/test/2", new IMqttClientMessageListener() {
|
||||
@Override
|
||||
public void onSubscribed(ChannelContext context, String topicFilter, MqttQoS mqttQoS) {
|
||||
// 订阅成功之后触发,可在此处做一些业务逻辑
|
||||
logger.info("topicFilter:{} MqttQoS:{} 订阅成功!!!", topicFilter, mqttQoS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(ChannelContext context, String topic, MqttPublishMessage message, byte[] payload) {
|
||||
logger.info(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
|
||||
}
|
||||
});
|
||||
|
||||
client.subQos0("/test/3", new IMqttClientMessageListener() {
|
||||
@Override
|
||||
public void onSubscribed(ChannelContext context, String topicFilter, MqttQoS mqttQoS) {
|
||||
// 订阅成功之后触发,可在此处做一些业务逻辑
|
||||
logger.info("topicFilter:{} MqttQoS:{} 订阅成功!!!", topicFilter, mqttQoS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(ChannelContext context, String topic, MqttPublishMessage message, byte[] payload) {
|
||||
logger.info(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
|
||||
}
|
||||
});
|
||||
|
||||
client.publish("/test/client", "mica最牛皮1".getBytes(StandardCharsets.UTF_8));
|
||||
client.publish("/test/client", "mica最牛皮2".getBytes(StandardCharsets.UTF_8));
|
||||
client.publish("/test/client", "mica最牛皮3".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
client.schedule(() -> {
|
||||
client.publish("/test/client", "mica最牛皮".getBytes(StandardCharsets.UTF_8));
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.huawei;
|
||||
|
||||
import org.dromara.mica.mqtt.core.client.MqttClient;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 客户端测试
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttClientTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 设备id和密钥,请从华为云iot获取
|
||||
String deviceId = "630eb6f8664c6f7938db6ef0_test";
|
||||
String deviceSecret = "";
|
||||
// 计算MQTT连接参数。
|
||||
MqttSign sign = new MqttSign(deviceId, deviceSecret);
|
||||
|
||||
String username = sign.getUsername();
|
||||
String password = sign.getPassword();
|
||||
String clientId = sign.getClientId();
|
||||
System.out.println("username: " + username);
|
||||
System.out.println("password: " + password);
|
||||
System.out.println("clientid: " + clientId);
|
||||
|
||||
// 初始化 mqtt 客户端
|
||||
MqttClient client = MqttClient.create()
|
||||
.ip("iot-mqtts.cn-north-4.myhuaweicloud.com")
|
||||
.port(8883)
|
||||
.username(username)
|
||||
.password(password)
|
||||
.clientId(clientId)
|
||||
.useSsl()
|
||||
.connectSync();
|
||||
|
||||
// 订阅命令下发topic
|
||||
String cmdRequestTopic = "$oc/devices/" + deviceId + "/sys/commands/#";
|
||||
|
||||
client.subQos0(cmdRequestTopic, (context, topic, message, payload) -> {
|
||||
System.out.println(topic + '\t' + new String(payload, StandardCharsets.UTF_8));
|
||||
});
|
||||
|
||||
// 属性上报消息
|
||||
String reportTopic = "$oc/devices/" + deviceId + "/sys/properties/report";
|
||||
String jsonMsg = "{\"services\":[{\"service_id\":\"Temperature\", \"properties\":{\"value\":57}},{\"service_id\":\"Battery\",\"properties\":{\"level\":88}}]}";
|
||||
|
||||
client.schedule(() -> {
|
||||
client.publish(reportTopic, jsonMsg.getBytes(StandardCharsets.UTF_8));
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.huawei;
|
||||
|
||||
import org.tio.utils.mica.DigestUtils;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 华为云 mqtt 签名方式
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttSign {
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private final String username;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private final String password;
|
||||
/**
|
||||
* 客户端id
|
||||
*/
|
||||
private final String clientId;
|
||||
|
||||
public MqttSign(String deviceId, String deviceSecret) {
|
||||
Objects.requireNonNull(deviceId, "deviceId is null");
|
||||
Objects.requireNonNull(deviceSecret, "deviceSecret is null");
|
||||
this.username = deviceId;
|
||||
String timestamp = getTimeStamp();
|
||||
this.password = getPassword(deviceSecret, timestamp);
|
||||
this.clientId = getClientId(deviceId, timestamp);
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
private static String getPassword(String deviceSecret, String timestamp) {
|
||||
return hmacSha256(deviceSecret, timestamp);
|
||||
}
|
||||
|
||||
private static String getClientId(String deviceId, String timestamp) {
|
||||
return deviceId + "_0_0_" + timestamp;
|
||||
}
|
||||
|
||||
/***
|
||||
* 要求:10位数字
|
||||
*/
|
||||
private static String getTimeStamp() {
|
||||
return ZonedDateTime.ofInstant(Instant.now(), ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern("yyyyMMddHH"));
|
||||
}
|
||||
|
||||
/***
|
||||
* 调用sha256算法进行哈希
|
||||
*/
|
||||
private static String hmacSha256(String message, String tStamp) {
|
||||
return DigestUtils.hmacSha256Hex(message, tStamp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.nginx;
|
||||
|
||||
|
||||
import org.dromara.mica.mqtt.core.server.MqttServer;
|
||||
|
||||
/**
|
||||
* mqtt 服务端测试
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttServerProxyProtocol {
|
||||
|
||||
public static void main(String[] args) {
|
||||
MqttServer.create()
|
||||
.enableMqtt()
|
||||
.enableMqttWs()
|
||||
// 开启代理协议
|
||||
.proxyProtocolEnable()
|
||||
.statEnable(false)
|
||||
.debug()
|
||||
.start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.proxy;
|
||||
|
||||
import org.dromara.mica.mqtt.codec.MqttQoS;
|
||||
import org.dromara.mica.mqtt.core.client.MqttClient;
|
||||
|
||||
/**
|
||||
* 2 个 mqtt 服务间,使用 2 个 client 做数据传输
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttClientProxy {
|
||||
|
||||
public static void main(String[] args) {
|
||||
MqttClient client1 = MqttClient.create()
|
||||
.ip("ip1")
|
||||
.port(1883)
|
||||
.clientId("clientI")
|
||||
.username("mica")
|
||||
.password("mica")
|
||||
.debug()
|
||||
.connectSync();
|
||||
|
||||
MqttClient client2 = MqttClient.create()
|
||||
.ip("ip2")
|
||||
.port(1883)
|
||||
.clientId("client2")
|
||||
.username("mica")
|
||||
.password("mica")
|
||||
.debug()
|
||||
.connectSync();
|
||||
|
||||
String[] topics = new String[]{
|
||||
"$share/test/link/product1/+/event/+/post",
|
||||
"$share/test/link/product2/+/event/+/post",
|
||||
"$share/test/link/product3/+/event/+/post"
|
||||
};
|
||||
client1.subscribe(topics, MqttQoS.QOS0, (context, topic, message, payload) -> {
|
||||
client2.publish(topic, payload);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.proxy;
|
||||
|
||||
import org.dromara.mica.mqtt.core.client.MqttClient;
|
||||
import org.dromara.mica.mqtt.core.server.MqttServer;
|
||||
import org.dromara.mica.mqtt.server.MqttConnectStatusListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tio.utils.buffer.ByteBufferUtil;
|
||||
|
||||
/**
|
||||
* mqtt 服务端代理到另外一个服务端
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttServerProxy {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MqttServerProxy.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 需要将数据发往的服务端
|
||||
MqttClient client = MqttClient.create()
|
||||
.ip("ip")
|
||||
.port(1883)
|
||||
.clientId("proxy")
|
||||
.username("mica")
|
||||
.password("mcia")
|
||||
.debug()
|
||||
.connectSync();
|
||||
// 接受数据的服务端
|
||||
MqttServer.create()
|
||||
.messageListener((context, clientId, topic, qoS, message) -> {
|
||||
byte[] payload = message.payload();
|
||||
logger.info("clientId:{} topic:{} payload:\n{}", clientId, topic, ByteBufferUtil.toString(payload));
|
||||
// 转发数据
|
||||
client.publish(topic, payload);
|
||||
})
|
||||
// 开启 mqtt tcp 协议
|
||||
.enableMqtt()
|
||||
// 开启 mqtt websocket
|
||||
.enableMqttWs()
|
||||
// 开启 http api 接口
|
||||
.enableMqttHttpApi()
|
||||
// 客户端连接状态监听
|
||||
.connectStatusListener(new MqttConnectStatusListener())
|
||||
// 开始 stat 监控
|
||||
.statEnable()
|
||||
// 开启 debug 信息日志
|
||||
.debug()
|
||||
.start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.server;
|
||||
|
||||
import org.dromara.mica.mqtt.core.server.event.IMqttConnectStatusListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tio.core.ChannelContext;
|
||||
|
||||
/**
|
||||
* mqtt 连接状态
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttConnectStatusListener implements IMqttConnectStatusListener {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MqttConnectStatusListener.class);
|
||||
|
||||
@Override
|
||||
public void online(ChannelContext context, String clientId, String username) {
|
||||
logger.info("Mqtt clientId:{} username:{} online.", clientId, username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offline(ChannelContext context, String clientId, String username, String reason) {
|
||||
logger.info("Mqtt clientId:{} username:{} offline reason:{}.", clientId, username, reason);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.server;
|
||||
|
||||
import org.dromara.mica.mqtt.codec.message.MqttMessage;
|
||||
import org.dromara.mica.mqtt.core.server.interceptor.IMqttMessageInterceptor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tio.core.ChannelContext;
|
||||
import org.tio.core.Node;
|
||||
|
||||
/**
|
||||
* mqtt 消息拦截器
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttMessageInterceptor implements IMqttMessageInterceptor {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MqttMessageInterceptor.class);
|
||||
|
||||
@Override
|
||||
public void onAfterReceivedBytes(ChannelContext context, int receivedBytes) throws Exception {
|
||||
// 注意:此时 clientId 可能为空
|
||||
String clientId = context.getBsId();
|
||||
Node clientNode = context.getClientNode();
|
||||
// ChannelStat channelStat = context.stat;
|
||||
// 自定义规则,超限是可用 Tio.remove(context, "xxx超限"); 断开连接。
|
||||
logger.info("===接收 client:{} clientId:{} data:{}b", clientNode, clientId, receivedBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAfterDecoded(ChannelContext context, MqttMessage message, int packetSize) {
|
||||
// 注意:此时 clientId 可能为空
|
||||
String clientId = context.getBsId();
|
||||
Node clientNode = context.getClientNode();
|
||||
logger.info("===解码 client:{} clientId:{} message:{}", clientNode, clientId, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAfterHandled(ChannelContext context, MqttMessage message, long cost) throws Exception {
|
||||
String clientId = context.getBsId();
|
||||
Node clientNode = context.getClientNode();
|
||||
logger.info("===处理完成 ip:{} clientId:{} message:{} 耗时:{}", clientNode, clientId, message, cost);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.server;
|
||||
|
||||
import org.dromara.mica.mqtt.core.server.MqttServer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* mqtt 服务端测试
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MqttServerTest {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MqttServerTest.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 注意:为了能接受更多链接(降低内存),请添加 jvm 参数 -Xss129k
|
||||
MqttServer mqttServer = MqttServer.create()
|
||||
// 服务端 ip 默认为空,0.0.0.0,建议不要设置,端口 默认:1883
|
||||
.enableMqtt(1883)
|
||||
// 默认为: 8192(mqtt 默认最大消息大小),为了降低内存可以减小小此参数,如果消息过大 t-io 会尝试解析多次(建议根据实际业务情况而定)
|
||||
.readBufferSize(8192)
|
||||
// 最大包体长度
|
||||
// .maxBytesInMessage(1024 * 100)
|
||||
// mqtt 3.1 协议会校验 clientId 长度。
|
||||
// .maxClientIdLength(64)
|
||||
.messageListener((context, clientId, topic, qos, message) -> {
|
||||
logger.info("clientId:{} payload:{}", clientId, new String(message.payload(), StandardCharsets.UTF_8));
|
||||
})
|
||||
// 客户端连接状态监听
|
||||
.connectStatusListener(new MqttConnectStatusListener())
|
||||
// 自定义消息拦截器
|
||||
.addInterceptor(new MqttMessageInterceptor())
|
||||
// 开启 websocket
|
||||
.enableMqttWs()
|
||||
// 开启 mqtt http 接口
|
||||
.enableMqttHttpApi(builder ->
|
||||
builder
|
||||
.basicAuth("mica", "mica") // http basic 认证
|
||||
.mcpServer() // 开启 mcp 服务
|
||||
.build()
|
||||
)
|
||||
// 开始 stat 监控
|
||||
.statEnable()
|
||||
// 开启 debug 信息日志
|
||||
.debug()
|
||||
.start();
|
||||
|
||||
mqttServer.schedule(() -> {
|
||||
String message = "mica最牛皮 " + System.currentTimeMillis();
|
||||
mqttServer.publishAll("/test/123", message.getBytes(StandardCharsets.UTF_8));
|
||||
}, 2000);
|
||||
|
||||
// 2.3.2 开始支持 stop 关闭
|
||||
// mqttServer.stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.ssl;
|
||||
|
||||
import org.dromara.mica.mqtt.core.client.MqttClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tio.utils.buffer.ByteBufferUtil;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 客户端测试
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class SslMqttClientTest {
|
||||
private static final Logger logger = LoggerFactory.getLogger(SslMqttClientTest.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 初始化 mqtt 客户端
|
||||
MqttClient client = MqttClient.create()
|
||||
.ip("127.0.0.1")
|
||||
.port(8883)
|
||||
.username("mica")
|
||||
.password("mica")
|
||||
.useSsl("classpath:ssl/dreamlu.net.jks", "123456")
|
||||
.connectSync();
|
||||
|
||||
client.subQos0("/test/#", (context, topic, message, payload) -> {
|
||||
logger.info(topic + '\t' + ByteBufferUtil.toString(payload));
|
||||
});
|
||||
|
||||
// 定时发送数据
|
||||
client.schedule(() -> {
|
||||
String message = "mica最牛皮 " + System.currentTimeMillis();
|
||||
client.publish("/test/123", message.getBytes(StandardCharsets.UTF_8));
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.ssl;
|
||||
|
||||
import org.dromara.mica.mqtt.core.server.MqttServer;
|
||||
import org.dromara.mica.mqtt.server.MqttConnectStatusListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tio.utils.buffer.ByteBufferUtil;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* mqtt 服务端测试
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class SslMqttServerTest {
|
||||
private static final Logger logger = LoggerFactory.getLogger(SslMqttServerTest.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
MqttServer mqttServer = MqttServer.create()
|
||||
.enableMqttSsl(builder ->
|
||||
builder.useSsl("classpath:ssl/dreamlu.net.jks", "123456")
|
||||
.build()
|
||||
)
|
||||
.messageListener((context, clientId, topic, qoS, message) -> {
|
||||
logger.info("clientId:{} message:{} payload:{}", clientId, message, ByteBufferUtil.toString(message.payload()));
|
||||
})
|
||||
.connectStatusListener(new MqttConnectStatusListener())
|
||||
.debug()
|
||||
.start();
|
||||
|
||||
// 定时发送数据
|
||||
mqttServer.schedule(() -> {
|
||||
String message = "mica最牛皮 " + System.currentTimeMillis();
|
||||
mqttServer.publishAll("/test/123", message.getBytes(StandardCharsets.UTF_8));
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
ssl 自签双向证书详见:https://gitee.com/596392912/mica-mqtt/issues/I45GO7
|
||||
16
example/mica-mqtt-example/src/main/resources/ssl/ca-cert.pem
Normal file
16
example/mica-mqtt-example/src/main/resources/ssl/ca-cert.pem
Normal file
@@ -0,0 +1,16 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICejCCAeMCFGkzqgl8ilICj+PRCwIiUy3dPNuWMA0GCSqGSIb3DQEBCwUAMHwx
|
||||
CzAJBgNVBAYTAkNOMQswCQYDVQQIDAJITjELMAkGA1UEBwwCQ1MxDTALBgNVBAoM
|
||||
BFJNSlMxDTALBgNVBAsMBFJNSlMxFDASBgNVBAMMC2RyZWFtbHUubmV0MR8wHQYJ
|
||||
KoZIhvcNAQkBFhA1OTYzOTI5MTJAcXEuY29tMB4XDTIyMDkwMzA4MjExNVoXDTMy
|
||||
MDgzMTA4MjExNVowfDELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkhOMQswCQYDVQQH
|
||||
DAJDUzENMAsGA1UECgwEUk1KUzENMAsGA1UECwwEUk1KUzEUMBIGA1UEAwwLZHJl
|
||||
YW1sdS5uZXQxHzAdBgkqhkiG9w0BCQEWEDU5NjM5MjkxMkBxcS5jb20wgZ8wDQYJ
|
||||
KoZIhvcNAQEBBQADgY0AMIGJAoGBAJxanDadRHd+D9jH5/mq7Pn3Fl915CiOaaJr
|
||||
QS0ZxCqRtTaS9+JkFQJ4TnAezlKx/xDZu7G9pk7CJ6w+JQwfI+AwsAkOlrynyFbe
|
||||
Hc9s6DZyZHXxkpgeQUqpnrkXkWG+jbh2aulWB4smQE/vPnpkjVGEe86+/JsYW3Sm
|
||||
rFhL2xddAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAP39PM0XpNzg3Bne63oXHRWDJ
|
||||
bafiCwloO/SaxH7JtCBj1I05W3owwHSqWYadK+lg//tKf6TL+94GtW8s2VtpLGu7
|
||||
Y5R2aakhywzbWVfrEK+kyvTG/4nP9tvcKwh8Iqr2XlllDLfsQeyLacb4+pcfDY3G
|
||||
8nrbe0lBKepae8D0SvQ=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,16 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICejCCAeMCFBebamYN4nfF0CrZWuN68SXxTH/GMA0GCSqGSIb3DQEBCwUAMHwx
|
||||
CzAJBgNVBAYTAkNOMQswCQYDVQQIDAJITjELMAkGA1UEBwwCQ1MxDTALBgNVBAoM
|
||||
BFJNSlMxDTALBgNVBAsMBFJNSlMxFDASBgNVBAMMC2RyZWFtbHUubmV0MR8wHQYJ
|
||||
KoZIhvcNAQkBFhA1OTYzOTI5MTJAcXEuY29tMB4XDTIyMDkwMzA4MjM1N1oXDTMy
|
||||
MDgzMTA4MjM1N1owfDELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkhOMQswCQYDVQQH
|
||||
DAJDUzENMAsGA1UECgwEUk1KUzENMAsGA1UECwwEUk1KUzEUMBIGA1UEAwwLZHJl
|
||||
YW1sdS5uZXQxHzAdBgkqhkiG9w0BCQEWEDU5NjM5MjkxMkBxcS5jb20wgZ8wDQYJ
|
||||
KoZIhvcNAQEBBQADgY0AMIGJAoGBAL3hH0vRYCsqs79ghUPskgr+bQyVkO/6U1H1
|
||||
4yHJWyWeXT2tl+VTvXhou0Zk3yYtd6O3l241PgmAjwBDPyfmM2UU/zluXQzELEuf
|
||||
AnC4Q2wl0vpoj7mGQeMuRpjZN3FblS0bRifvctx3ubWVvSzCFIaKLZgdrnyWm5Lm
|
||||
kJQjRaEDAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAkm/Mv9nEGE4xy3L4NmwgyZMo
|
||||
j5CuarbCEv/MgXST8B/dJvtC7PbCzrb9BbUkiIUjCOnRC+2U0DbSAkHy7CCwHTLx
|
||||
g9Syn1SMWn9SF74l74qVtuaRjMKCGwSlqBLH5/t1FdPoevY2a+zi5dAqwIhkjY84
|
||||
uYOgxODe+kFBUbT6W3A=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,15 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXQIBAAKBgQC94R9L0WArKrO/YIVD7JIK/m0MlZDv+lNR9eMhyVslnl09rZfl
|
||||
U714aLtGZN8mLXejt5duNT4JgI8AQz8n5jNlFP85bl0MxCxLnwJwuENsJdL6aI+5
|
||||
hkHjLkaY2TdxW5UtG0Yn73Lcd7m1lb0swhSGii2YHa58lpuS5pCUI0WhAwIDAQAB
|
||||
AoGAR/T8azsZesJgA/KMDkWkws3QfahgmNEAqlrIjJFGHWd6ZllW6u1lLDBkaDTp
|
||||
7AnnAQAePwGmVOuHRc42LOSsLMX/D2wYsTGjLTT1w+fEDkQCVnDKV8ZYWA5fN7Zh
|
||||
m5cLB5IB23L/Xvs8UMYQ8qWufv6BxVPr+cawtXOK3O91AoECQQD5PwGSrMpsNkVG
|
||||
Ox1t1A9wVgelbq+9qANPl8TvdaZKvNsbWwI0PRqK7D1SJXJiMX4m/hc8YkIXJWsO
|
||||
Wd1yLg6TAkEAwwZLncJYlYOp0v8PXk75rDR7KV9fdBPPOdnZ1XU/yFW3anwOt0OX
|
||||
LvGL3X0A2f/tqOX9v+3LENCICF4gAv850QJBALo7Yal+giEoy8oWEX8mnAKLxVrO
|
||||
wXEsQI0QEY36ki31vqFJ9vOhVFvI+GiQok7MPD5WTHZJ1KgGxV8LtnLCBxECQCPD
|
||||
WcZ6RyhT1qaco0LWFK7hiNxTYvu0TkH7kxizwZiJL3NVgJVWzbiMDuv06l0Ps5NP
|
||||
abLydlSFCQ0PxasHBqECQQC0lhmqkNbaN3lJyGDoEWm6Kb9z3eh3+9Fk4j328aYW
|
||||
gQcBeRhKU8kdDTg2flOWS3sxrEysJYv8i9DPbX9RsRFd
|
||||
-----END RSA PRIVATE KEY-----
|
||||
BIN
example/mica-mqtt-example/src/main/resources/ssl/dreamlu.net.jks
Normal file
BIN
example/mica-mqtt-example/src/main/resources/ssl/dreamlu.net.jks
Normal file
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
writer = console
|
||||
writer.format = {date: HH:mm:ss.SSS} [{thread}] {level} {class-name}.{method} : {message}
|
||||
writer.level = info
|
||||
# level
|
||||
level@org.tio = warn
|
||||
level@org.tio.client.TioClient = off
|
||||
level@org.tio.server = info
|
||||
level@org.dromara.mica.mqtt = info
|
||||
@@ -0,0 +1 @@
|
||||
package net.dreamlu.iot;
|
||||
49
example/mica-mqtt-server-solon-plugin-example/pom.xml
Normal file
49
example/mica-mqtt-server-solon-plugin-example/pom.xml
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>example</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>mica-mqtt-server-solon-plugin-example</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-server-solon-plugin</artifactId>
|
||||
</dependency>
|
||||
<!-- 简单的本地定时任务调度 -->
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon-scheduling-simple</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon-logging-simple</artifactId>
|
||||
</dependency>
|
||||
<!-- metrics 指标 开始 -->
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon-cloud-metrics</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||
</dependency>
|
||||
<!-- metrics 指标 结束 -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.dromara.mica.mqtt.server.solon;
|
||||
|
||||
|
||||
import org.noear.solon.Solon;
|
||||
import org.noear.solon.annotation.Configuration;
|
||||
import org.noear.solon.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* @author wsq
|
||||
*/
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
public class MqttServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Solon.start(MqttServerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.dromara.mica.mqtt.server.solon.controller;
|
||||
|
||||
|
||||
import org.dromara.mica.mqtt.server.solon.service.ServerService;
|
||||
import org.noear.solon.annotation.Controller;
|
||||
import org.noear.solon.annotation.Inject;
|
||||
import org.noear.solon.annotation.Mapping;
|
||||
import org.noear.solon.annotation.Post;
|
||||
|
||||
@Mapping("/mqtt/server")
|
||||
@Controller
|
||||
public class ServerController {
|
||||
@Inject
|
||||
private ServerService service;
|
||||
|
||||
@Mapping("publish")
|
||||
@Post
|
||||
public boolean publish(String body) {
|
||||
return service.publish(body);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.server.solon.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.server.solon.event.MqttClientOfflineEvent;
|
||||
import org.noear.solon.annotation.Component;
|
||||
import org.noear.solon.core.event.EventListener;
|
||||
|
||||
/**
|
||||
* mqtt 连接状态,使用 solon event 方式,性能有损耗
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MqttConnectOfflineListener implements EventListener<MqttClientOfflineEvent> {
|
||||
|
||||
@Override
|
||||
public void onEvent(MqttClientOfflineEvent mqttClientOfflineEvent) throws Throwable {
|
||||
log.info("MqttClientOnlineEvent:{}", mqttClientOfflineEvent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.server.solon.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.server.solon.event.MqttClientOnlineEvent;
|
||||
import org.noear.solon.annotation.Component;
|
||||
import org.noear.solon.core.event.EventListener;
|
||||
|
||||
/**
|
||||
* mqtt 连接状态,使用 solon event 方式,性能有损耗
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MqttConnectOnlineListener implements EventListener<MqttClientOnlineEvent> {
|
||||
|
||||
@Override
|
||||
public void onEvent(MqttClientOnlineEvent mqttClientOnlineEvent) throws Throwable {
|
||||
log.info("MqttClientOnlineEvent:{}", mqttClientOnlineEvent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.dromara.mica.mqtt.server.solon.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.codec.message.MqttPublishMessage;
|
||||
import org.dromara.mica.mqtt.codec.MqttQoS;
|
||||
import org.dromara.mica.mqtt.core.server.event.IMqttMessageListener;
|
||||
import org.tio.core.ChannelContext;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 消息监听器示例1,直接实现 IMqttMessageListener,注意:如果实现了 IMqttMessageListener,MqttServerFunction 注解就不生效了。
|
||||
*
|
||||
* @author wsq
|
||||
*/
|
||||
@Slf4j
|
||||
//@Component
|
||||
public class MqttServerMessageListener1 implements IMqttMessageListener {
|
||||
|
||||
@Override
|
||||
public void onMessage(ChannelContext context, String clientId, String topic, MqttQoS qos, MqttPublishMessage message) {
|
||||
log.info("context:{} clientId:{} message:{} payload:{}", context, clientId, message, new String(message.payload(), StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.dromara.mica.mqtt.server.solon.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.codec.message.MqttPublishMessage;
|
||||
import org.dromara.mica.mqtt.core.annotation.MqttServerFunction;
|
||||
import org.dromara.mica.mqtt.server.solon.pojo.User;
|
||||
import org.noear.solon.annotation.Component;
|
||||
import org.tio.core.ChannelContext;
|
||||
import org.tio.core.Node;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 消息监听器示例2,MqttServerFunction 注解订阅,注意:如果自行实现了 IMqttMessageListener,MqttServerFunction 注解就不生效了。
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MqttServerMessageListener2 {
|
||||
|
||||
@MqttServerFunction("/test/object")
|
||||
public void func1(String topic, User<?> user) {
|
||||
log.info("topic:{} user:{}", topic, user);
|
||||
}
|
||||
|
||||
@MqttServerFunction("/test/client")
|
||||
public void func1(String topic, byte[] message) {
|
||||
log.info("topic:{} message:{}", topic, new String(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* MQTT消息处理函数,匹配 mqtt Topic /test/+,如何需要匹配所以消息,请使用通配符 #
|
||||
*
|
||||
* @param context ChannelContext,可选参数
|
||||
* @param topic 实际接收到消息的主题名称,可选参数
|
||||
* @param topicVars topic 中的 ${xxxx} 变量解析(v2.5.4支持),可选参数,注意:类型必须为 Map<String, String>
|
||||
* @param publishMessage 完整的MQTT发布消息对象,包含消息头和负载,可选参数
|
||||
* @param message 消息负载内容,以字节数组形式提供,可选参数,也可支持对象形式,默认 json 序列化
|
||||
*/
|
||||
@MqttServerFunction("/test/${xxxx}")
|
||||
public void func3(ChannelContext context, String topic, Map<String, String> topicVars, MqttPublishMessage publishMessage, byte[] message) {
|
||||
// 获取客户端节点信息
|
||||
Node clientNode = context.getClientNode();
|
||||
// 记录接收到的MQTT消息信息
|
||||
log.info("clientNode:{} topic:{} topicVars:{} publishMessage:{} message:{}", clientNode, topic, topicVars, publishMessage, new String(message));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.dromara.mica.mqtt.server.solon.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class User<T> {
|
||||
private String name;
|
||||
private T girlfriend;
|
||||
|
||||
public static User newUser(){
|
||||
User<User> user1 = new User();
|
||||
user1.setName("name1");
|
||||
|
||||
User<User> user2 = new User();
|
||||
user2.setName("name2");
|
||||
user2.setGirlfriend(user1);
|
||||
return user2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.dromara.mica.mqtt.server.solon.service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.server.solon.MqttServerTemplate;
|
||||
import org.noear.solon.annotation.Component;
|
||||
import org.noear.solon.annotation.Inject;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author wsq
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ServerService {
|
||||
@Inject
|
||||
private MqttServerTemplate server;
|
||||
|
||||
public boolean publish(String body) {
|
||||
boolean result = server.publishAll("/test/123", body.getBytes(StandardCharsets.UTF_8));
|
||||
log.info("Mqtt publishAll result:{}", result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.dromara.mica.mqtt.server.solon.task;
|
||||
|
||||
import org.dromara.mica.mqtt.server.solon.MqttServerTemplate;
|
||||
import org.dromara.mica.mqtt.server.solon.pojo.User;
|
||||
import org.noear.solon.annotation.Component;
|
||||
import org.noear.solon.annotation.Inject;
|
||||
import org.noear.solon.scheduling.annotation.Scheduled;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author wsq
|
||||
*/
|
||||
@Component
|
||||
public class PublishTask {
|
||||
@Inject
|
||||
private MqttServerTemplate mqttServerTemplate;
|
||||
|
||||
@Scheduled(fixedDelay = 1000)
|
||||
public void publish() {
|
||||
mqttServerTemplate.publishAll("/test/123", "mica最牛皮".getBytes(StandardCharsets.UTF_8));
|
||||
mqttServerTemplate.publishAll("/test/object", User.newUser());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
# solon 配置
|
||||
solon:
|
||||
logging:
|
||||
appender:
|
||||
console:
|
||||
level: INFO
|
||||
# mqtt 服务端配置
|
||||
mqtt:
|
||||
server:
|
||||
enabled: true # 是否开启服务端,默认:true
|
||||
name: Mica-Mqtt-Server # 名称,默认:Mica-Mqtt-Server
|
||||
heartbeat-timeout: 120000 # 心跳超时,单位毫秒,默认: 1000 * 120
|
||||
read-buffer-size: 8KB # 接收数据的 buffer size,默认:8k
|
||||
max-bytes-in-message: 10MB # 消息解析最大 bytes 长度,默认:10M
|
||||
auth:
|
||||
enable: false # 是否开启 mqtt 认证
|
||||
username: mica # mqtt 认证用户名
|
||||
password: mica # mqtt 认证密码
|
||||
debug: true # 如果开启 prometheus 指标收集建议关闭
|
||||
stat-enable: true # 开启指标收集,debug 和 prometheus 开启时需要打开,默认开启,关闭节省内存
|
||||
mqtt-listener: # mqtt 监听器
|
||||
enable: true # 是否开启,默认:false
|
||||
# ip: "0.0.0.0" # 服务端 ip 默认为空,0.0.0.0,建议不要设置
|
||||
port: 1883 # 端口,默认:1883
|
||||
mqtt-ssl-listener: # mqtt ssl 监听器
|
||||
enable: false # 是否开启,默认:false
|
||||
port: 8883 # 端口,默认:8883
|
||||
ssl: # ssl 配置,必须
|
||||
keystore-path: # 必须参数:ssl keystore 目录,支持 classpath:/ 路径。
|
||||
keystore-pass: # 必选参数:ssl keystore 密码
|
||||
truststore-path: # 可选参数:ssl 双向认证 truststore 目录,支持 classpath:/ 路径。
|
||||
truststore-pass: # 可选参数:ssl 双向认证 truststore 密码
|
||||
client-auth: none # 是否需要客户端认证(双向认证),默认:NONE(不需要)
|
||||
ws-listener: # websocket mqtt 监听器
|
||||
enable: true # 是否开启,默认:false
|
||||
port: 8083 # websocket 端口,默认:8083
|
||||
wss-listener: # websocket ssl mqtt 监听器
|
||||
enable: false # 是否开启,默认:false
|
||||
port: 8084 # 端口,默认:8084
|
||||
ssl: # ssl 配置,必须
|
||||
keystore-path: # 必须参数:ssl keystore 目录,支持 classpath:/ 路径。
|
||||
keystore-pass: # 必选参数:ssl keystore 密码
|
||||
truststore-path: # 可选参数:ssl 双向认证 truststore 目录,支持 classpath:/ 路径。
|
||||
truststore-pass: # 可选参数:ssl 双向认证 truststore 密码
|
||||
client-auth: none # 是否需要客户端认证(双向认证),默认:NONE(不需要)
|
||||
http-listener:
|
||||
enable: true
|
||||
port: 18083
|
||||
basic-auth: # 基础认证
|
||||
enable: true
|
||||
username: mica
|
||||
password: mica
|
||||
mcp-server: # 大模型 mcp
|
||||
enable: true
|
||||
14
example/mica-mqtt-server-spring-boot-example/README.md
Normal file
14
example/mica-mqtt-server-spring-boot-example/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## SpringBoot + mica-mqtt-server 应用演示
|
||||
|
||||
## 启动步骤
|
||||
1. 先启动 mica-mqtt-server-spring-boot-example
|
||||
|
||||
2. 再启动 mica-mqtt-client-spring-boot-example
|
||||
|
||||
3. 查看控制器 swagger 地址:http://localhost:30012/doc.html
|
||||
|
||||
4. 可开启 prometheus 指标收集,详见: http://localhost:30012/actuator/prometheus
|
||||
|
||||
## 连接
|
||||
|
||||
mica Spring boot 开发组件集文档:https://www.dreamlu.net/components/mica-swagger.html
|
||||
92
example/mica-mqtt-server-spring-boot-example/pom.xml
Normal file
92
example/mica-mqtt-server-spring-boot-example/pom.xml
Normal file
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>mica-mqtt-server-spring-boot-example</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>example</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-server-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.dreamlu</groupId>
|
||||
<artifactId>mica-lite</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.dreamlu</groupId>
|
||||
<artifactId>mica-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.dreamlu</groupId>
|
||||
<artifactId>mica-openapi</artifactId>
|
||||
</dependency>
|
||||
<!-- 开启 prometheus 指标收集,详见: http://localhost:30012/actuator/prometheus -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot4-starter</artifactId>
|
||||
<version>3.5.14</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- <!– MySQL Connector –>-->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.33</version>
|
||||
</dependency>
|
||||
|
||||
<!-- <!– Druid Connection Pool –>-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-starter</artifactId>
|
||||
<version>1.2.20</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.dromara.mica.mqtt.server;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* @author wsq
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
public class MqttServerApplication {
|
||||
|
||||
/**
|
||||
* 先启动本项目,再启动 mica-mqtt-client-spring-boot-example 进行测试
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MqttServerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.dromara.mica.mqtt.server.auth;
|
||||
|
||||
import org.dromara.mica.mqtt.core.server.auth.IMqttServerAuthHandler;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.tio.core.ChannelContext;
|
||||
|
||||
/**
|
||||
* 示例 mqtt tcp、websocket 认证,请按照自己的需求和业务进行扩展
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class MqttAuthHandler implements IMqttServerAuthHandler {
|
||||
|
||||
@Override
|
||||
public boolean authenticate(ChannelContext context, String uniqueId, String clientId, String username, String password) {
|
||||
// 客户端认证逻辑实现
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.dromara.mica.mqtt.server.auth;
|
||||
|
||||
import org.dromara.mica.mqtt.core.server.http.api.code.ResultCode;
|
||||
import org.dromara.mica.mqtt.core.server.http.api.result.Result;
|
||||
import org.dromara.mica.mqtt.core.server.http.handler.HttpFilter;
|
||||
import org.dromara.mica.mqtt.core.server.http.handler.MqttHttpRoutes;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.tio.http.common.HttpRequest;
|
||||
import org.tio.http.common.HttpResponse;
|
||||
|
||||
/**
|
||||
* 示例自定义 mqtt http 接口认证,请按照自己的需求和业务进行扩展
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class MqttHttpAuthFilter implements HttpFilter, InitializingBean {
|
||||
|
||||
@Override
|
||||
public boolean filter(HttpRequest request) throws Exception {
|
||||
// 自行实现逻辑
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpResponse response(HttpRequest request) {
|
||||
// 认证不通过时的响应
|
||||
return Result.fail(request, ResultCode.E103);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
MqttHttpRoutes.addFilter(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.dromara.mica.mqtt.server.auth;
|
||||
|
||||
import org.dromara.mica.mqtt.codec.MqttQoS;
|
||||
import org.dromara.mica.mqtt.core.server.auth.IMqttServerSubscribeValidator;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.tio.core.ChannelContext;
|
||||
|
||||
/**
|
||||
* 示例自定义订阅校验,请按照自己的需求和业务进行扩展
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class MqttSubscribeValidator implements IMqttServerSubscribeValidator {
|
||||
|
||||
@Override
|
||||
public boolean isValid(ChannelContext context, String clientId, String topicFilter, MqttQoS qoS) {
|
||||
// 校验客户端订阅的 topic,校验成功返回 true,失败返回 false
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.dromara.mica.mqtt.server.auth;
|
||||
|
||||
import org.dromara.mica.mqtt.core.server.auth.IMqttServerUniqueIdService;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.tio.core.ChannelContext;
|
||||
|
||||
/**
|
||||
* 示例自定义 clientId,请按照自己的需求和业务进行扩展
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class MqttUniqueIdService implements IMqttServerUniqueIdService {
|
||||
|
||||
@Override
|
||||
public String getUniqueId(ChannelContext context, String clientId, String userName, String password) {
|
||||
// 返回的 uniqueId 会替代 mqtt client 传过来的 clientId,请保证返回的 uniqueId 唯一。
|
||||
return clientId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.dromara.mica.mqtt.server.controller;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.dromara.mica.mqtt.server.entity.CarInfo;
|
||||
import org.dromara.mica.mqtt.server.service.ICarInfoService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/car")
|
||||
@AllArgsConstructor
|
||||
public class CarInfoController {
|
||||
|
||||
private final ICarInfoService carInfoService;
|
||||
|
||||
@GetMapping("/list")
|
||||
public List<CarInfo> list() {
|
||||
return carInfoService.list();
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public boolean save(@RequestBody CarInfo carInfo) {
|
||||
return carInfoService.save(carInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.dromara.mica.mqtt.server.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.dromara.mica.mqtt.server.service.ServerService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Tag(name = "Mqtt::服务端")
|
||||
@RequestMapping("/mqtt/server")
|
||||
@RestController
|
||||
public class ServerController {
|
||||
@Autowired
|
||||
private ServerService service;
|
||||
|
||||
@Operation(summary = "publish")
|
||||
@PostMapping("publish")
|
||||
public boolean publish(@RequestBody String body) {
|
||||
return service.publish(body);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.dromara.mica.mqtt.server.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("car_info")
|
||||
public class CarInfo {
|
||||
private Long customerId;
|
||||
private String plate;
|
||||
private String enable;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & dreamlu.net).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.mica.mqtt.server.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.spring.server.event.MqttClientOfflineEvent;
|
||||
import org.dromara.mica.mqtt.spring.server.event.MqttClientOnlineEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
|
||||
/**
|
||||
* mqtt 连接状态,使用 spring boot event 方式,性能有损耗
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
//@Service
|
||||
public class MqttConnectStatusListener1 {
|
||||
|
||||
@EventListener
|
||||
public void online(MqttClientOnlineEvent event) {
|
||||
log.info("MqttClientOnlineEvent:{}", event);
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void offline(MqttClientOfflineEvent event) {
|
||||
log.info("MqttClientOfflineEvent:{}", event);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user