first commit

This commit is contained in:
zc
2025-12-08 10:40:43 +08:00
commit 871ae8be0a
410 changed files with 38212 additions and 0 deletions

View 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

View 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>
<!-- &lt;!&ndash; MySQL Connector &ndash;&gt;-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- &lt;!&ndash; Druid Connection Pool &ndash;&gt;-->
<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>

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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.listener;
import lombok.extern.slf4j.Slf4j;
import org.dromara.mica.mqtt.core.server.event.IMqttConnectStatusListener;
import org.springframework.stereotype.Service;
import org.tio.core.ChannelContext;
/**
* mqtt 连接状态
*
* @author L.cm
*/
@Slf4j
@Service
public class MqttConnectStatusListener2 implements IMqttConnectStatusListener {
@Override
public void online(ChannelContext context, String clientId, String username) {
log.info("Mqtt clientId:{} username:{} online.", clientId, username);
}
@Override
public void offline(ChannelContext context, String clientId, String username, String reason) {
log.info("Mqtt clientId:{} username:{} offline reason:{}.", clientId, username, reason);
}
}

View File

@@ -0,0 +1,37 @@
package org.dromara.mica.mqtt.server.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.dromara.mica.mqtt.spring.server.MqttServerTemplate;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.tio.core.ChannelContext;
import java.nio.charset.StandardCharsets;
/**
* 消息监听器示例1直接实现 IMqttMessageListener注意如果实现了 IMqttMessageListenerMqttServerFunction 注解就不生效了。
*
* @author wsq
*/
@Slf4j
//@Service
public class MqttServerMessageListener1 implements IMqttMessageListener, SmartInitializingSingleton {
@Autowired
private ApplicationContext applicationContext;
private MqttServerTemplate mqttServerTemplate;
@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));
}
@Override
public void afterSingletonsInstantiated() {
// 单利 bean 初始化完成之后从 ApplicationContext 中获取 bean
mqttServerTemplate = applicationContext.getBean(MqttServerTemplate.class);
}
}

View File

@@ -0,0 +1,55 @@
package org.dromara.mica.mqtt.server.listener;
import lombok.extern.slf4j.Slf4j;
import org.dromara.mica.mqtt.codec.message.MqttPublishMessage;
import org.dromara.mica.mqtt.server.pojo.User;
import org.dromara.mica.mqtt.core.annotation.MqttServerFunction;
import org.springframework.stereotype.Service;
import org.tio.core.ChannelContext;
import org.tio.core.Node;
import java.util.Map;
/**
* 消息监听器示例2MqttServerFunction 注解订阅,注意:如果自行实现了 IMqttMessageListenerMqttServerFunction 注解就不生效了。
*
* @author wsq
*/
@Slf4j
@Service
public class MqttServerMessageListener2 {
/**
* MQTT消息处理函数
*
* @param topic mqtt Topic
* @param user 订阅消息的负载内容,默认 json 序列化
*/
@MqttServerFunction("/test/object")
public void func1(String topic, User<?> user) {
log.info("topic:{} user:{}", topic, user);
}
@MqttServerFunction("/test/client")
public void func2(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));
}
}

View File

@@ -0,0 +1,9 @@
package org.dromara.mica.mqtt.server.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.mica.mqtt.server.entity.CarInfo;
@Mapper
public interface CarInfoMapper extends BaseMapper<CarInfo> {
}

View File

@@ -0,0 +1,19 @@
package org.dromara.mica.mqtt.server.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;
}
}

View File

@@ -0,0 +1,7 @@
package org.dromara.mica.mqtt.server.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.mica.mqtt.server.entity.CarInfo;
public interface ICarInfoService extends IService<CarInfo> {
}

View File

@@ -0,0 +1,24 @@
package org.dromara.mica.mqtt.server.service;
import lombok.extern.slf4j.Slf4j;
import org.dromara.mica.mqtt.spring.server.MqttServerTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
/**
* @author wsq
*/
@Slf4j
@Service
public class ServerService {
@Autowired
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;
}
}

View File

@@ -0,0 +1,11 @@
package org.dromara.mica.mqtt.server.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.dromara.mica.mqtt.server.entity.CarInfo;
import org.dromara.mica.mqtt.server.mapper.CarInfoMapper;
import org.dromara.mica.mqtt.server.service.ICarInfoService;
import org.springframework.stereotype.Service;
@Service
public class CarInfoServiceImpl extends ServiceImpl<CarInfoMapper, CarInfo> implements ICarInfoService {
}

View File

@@ -0,0 +1,25 @@
package org.dromara.mica.mqtt.server.task;
import org.dromara.mica.mqtt.core.server.MqttServer;
import org.dromara.mica.mqtt.server.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
/**
* @author wsq
*/
@Service
public class PublishAllTask {
@Autowired
private MqttServer mqttServer;
@Scheduled(fixedDelay = 1000)
public void run() {
mqttServer.publishAll("/test/123", "mica最牛皮".getBytes(StandardCharsets.UTF_8));
mqttServer.publishAll("/test/object", User.newUser());
}
}

View File

@@ -0,0 +1,104 @@
server:
port: 30013
spring:
application:
name: mica-mqtt-server
datasource:
url: jdbc:mysql://127.0.0.1:3306/xa_cloud?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#mybatis
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
#实体扫描多个package用逗号或者分号分隔
typeAliasesPackage: org.dromara.mica.mqtt.server.entity
global-config:
#数据库相关配置
db-config:
#主键类型
id-type: ASSIGN_ID
banner: false
#原生配置
configuration:
map-underscore-to-camel-case: true
# 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
springdoc:
swagger-ui:
urls:
- name: swagger
url: /v3/api-docs
# actuator management
management:
info:
defaults:
enabled: true
metrics:
tags:
application: ${spring.application.name}
endpoint:
health:
show-details: ALWAYS
prometheus:
enabled: true
endpoints:
web:
exposure:
include: '*'
logging:
level:
root: info
server: info # t-io 服务端默认日志
org.tio: info # t-io 服务端默认日志
org.dromara.mica.mqtt: info # mica-mqtt 日志

View File

@@ -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}