update 重写ruoyi-mqtt模块
parent
5c6a78a3bd
commit
aebd46af2e
@ -0,0 +1,34 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# Environment files
|
||||
.env*
|
||||
|
||||
# IDE files
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Build outputs
|
||||
dist/
|
||||
build/
|
||||
|
||||
# PM2 logs
|
||||
.pm2/
|
||||
|
||||
# Docker
|
||||
.dockerignore
|
||||
|
||||
# pnpm
|
||||
pnpm-lock.yaml
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*.temp
|
||||
@ -0,0 +1,35 @@
|
||||
# Use the official Node.js image as the base image
|
||||
FROM node:22.16.0 AS builder
|
||||
|
||||
# Set the working directory inside the container
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package.json and package-lock.json (if available) to the working directory
|
||||
COPY package*.json ./
|
||||
|
||||
# Install the application dependencies
|
||||
RUN npm install
|
||||
|
||||
# Copy the rest of the application code to the working directory
|
||||
COPY . .
|
||||
|
||||
# Production stage
|
||||
FROM node:22.16.0-alpine AS production
|
||||
|
||||
# Set the working directory inside the container
|
||||
WORKDIR /app
|
||||
|
||||
# Copy dependencies from builder stage
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
|
||||
# Copy application code from builder stage
|
||||
COPY --from=builder /app/. .
|
||||
|
||||
# Install pm2 globally
|
||||
RUN npm install -g pm2
|
||||
|
||||
# Expose the port that the application listens on
|
||||
EXPOSE 3000
|
||||
|
||||
# Define the command to run the application in production mode with pm2.json config
|
||||
CMD ["pm2", "start", "pm2.json", "--no-daemon"]
|
||||
@ -0,0 +1,3 @@
|
||||
# 内部授权服务器
|
||||
|
||||
为内部第三方提供授权restful-api,如:emqx,zlmediakit
|
||||
@ -0,0 +1,27 @@
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
const port = process.env.PORT || 3000;
|
||||
const username = process.env.AUTH_USERNAME || 'admin';
|
||||
const password = process.env.AUTH_PASSWORD || '3.1415926'
|
||||
const authUrl = process.env.AUTH_URL || 'http://127.0.0.1:8080'
|
||||
|
||||
// Middleware to parse JSON bodies
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({extended: true}));
|
||||
|
||||
// RESTful routes
|
||||
app.get('/', (req, res) => {
|
||||
res.json({message: 'Welcome to the auth-inner-server API'});
|
||||
});
|
||||
|
||||
app.post('/emqx-login', (req, res) => {
|
||||
if (req.body.username === username && req.body.password === password) {
|
||||
res.json({is_superuser: true, result: 'allow'});
|
||||
} else {
|
||||
res.json({is_superuser: false, result: 'deny'});
|
||||
}
|
||||
})
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server is running on port ${port}`);
|
||||
});
|
||||
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "auth-inner-server",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"dev": "nodemon index.js",
|
||||
"prod": "pm2 start index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"express": "^4.21.2",
|
||||
"pm2": "^6.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": "22.16.0"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
{
|
||||
"apps": [
|
||||
{
|
||||
"name": "auth-inner-server",
|
||||
"script": "index.js",
|
||||
"watch": [
|
||||
"app"
|
||||
],
|
||||
"ignore_watch": [
|
||||
"app/public"
|
||||
],
|
||||
"log_date_format": "YYYY-MM-DD HH:mm Z",
|
||||
"error_file": "./logs/pm2-err.log",
|
||||
"out_file": "./logs/pm2-out.log",
|
||||
"merge_logs": true,
|
||||
"exec_mode": "fork",
|
||||
"max_memory_restart": "200M",
|
||||
"autorestart": true,
|
||||
"env": {
|
||||
"NODE_ENV": "prd"
|
||||
},
|
||||
"instances": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
# 网络设置
|
||||
networks:
|
||||
base2024-network:
|
||||
name: base2024-network
|
||||
driver: bridge
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 192.168.222.0/24
|
||||
gateway: 192.168.222.1
|
||||
|
||||
services:
|
||||
|
||||
|
||||
# mqtt服务
|
||||
mqtt:
|
||||
container_name: mqtt
|
||||
restart: always
|
||||
image: registry.cn-hangzhou.aliyuncs.com/awl/emqx:5.6.1
|
||||
ports:
|
||||
- "1883:1883"
|
||||
- "8083:8083"
|
||||
# - "8084:8084"
|
||||
# - "8883:8883"
|
||||
- "18083:18083"
|
||||
volumes:
|
||||
- ./mqtt/configs/:/opt/emqx/data/configs/:rw
|
||||
- ./mqtt/log/:/opt/emqx/log/:rw
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
privileged: true
|
||||
networks:
|
||||
- base2024-network
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
authentication = [
|
||||
{
|
||||
backend = http
|
||||
body {
|
||||
password = "${password}"
|
||||
username = "${username}"
|
||||
}
|
||||
connect_timeout = 15s
|
||||
enable_pipelining = 100
|
||||
headers {content-type = "application/json"}
|
||||
mechanism = password_based
|
||||
method = post
|
||||
pool_size = 8
|
||||
request_timeout = 5s
|
||||
ssl {enable = false, verify = verify_peer}
|
||||
url = "http://server1:8080/emqx-login"
|
||||
}
|
||||
]
|
||||
mqtt {
|
||||
await_rel_timeout = 300s
|
||||
exclusive_subscription = false
|
||||
idle_timeout = 15s
|
||||
ignore_loop_deliver = false
|
||||
keepalive_multiplier = 1.5
|
||||
max_awaiting_rel = 100
|
||||
max_clientid_len = 65535
|
||||
max_inflight = 32
|
||||
max_mqueue_len = 1000
|
||||
max_packet_size = 256MB
|
||||
max_qos_allowed = 2
|
||||
max_subscriptions = infinity
|
||||
max_topic_alias = 65535
|
||||
max_topic_levels = 128
|
||||
message_expiry_interval = infinity
|
||||
mqueue_default_priority = lowest
|
||||
mqueue_priorities = disabled
|
||||
mqueue_store_qos0 = true
|
||||
peer_cert_as_clientid = disabled
|
||||
peer_cert_as_username = disabled
|
||||
response_information = ""
|
||||
retain_available = true
|
||||
retry_interval = 30s
|
||||
server_keepalive = disabled
|
||||
session_expiry_interval = 2h
|
||||
shared_subscription = true
|
||||
shared_subscription_strategy = round_robin
|
||||
strict_mode = false
|
||||
upgrade_qos = false
|
||||
use_username_as_clientid = false
|
||||
wildcard_subscription = true
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
# emqx日志目录
|
||||
@ -0,0 +1,41 @@
|
||||
<?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">
|
||||
<parent>
|
||||
<artifactId>ruoyi-vue-plus</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>4.6.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-mqtt</artifactId>
|
||||
|
||||
<description>
|
||||
mqtt模块
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.paho</groupId>
|
||||
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
|
||||
<version>1.2.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-json</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-cache</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -0,0 +1,17 @@
|
||||
package com.ruoyi.mqtt;
|
||||
|
||||
import com.ruoyi.mqtt.config.MqttConfig;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 非ruoyi项目需要使用本注解
|
||||
*/
|
||||
@Import(MqttConfig.class)
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface MqttEnabled {
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package com.ruoyi.mqtt;
|
||||
|
||||
import com.ruoyi.mqtt.event.*;
|
||||
|
||||
/**
|
||||
* 事件 处理句柄
|
||||
*/
|
||||
public interface MqttEventHandler {
|
||||
|
||||
/**
|
||||
* 处理
|
||||
*
|
||||
* @param event 事件
|
||||
* @param item 产生的MQTT操作项
|
||||
* @return 是否处理下一个句柄
|
||||
*/
|
||||
default boolean next(MqttEvent event, MqttItem item) {
|
||||
if (event instanceof MqttConnectionExceptionEvent) {
|
||||
return next((MqttConnectionExceptionEvent) event, item);
|
||||
} else if (event instanceof MqttConnectionLostEvent) {
|
||||
return next((MqttConnectionLostEvent) event, item);
|
||||
} else if (event instanceof MqttConnectionSuccessEvent) {
|
||||
return next((MqttConnectionSuccessEvent) event, item);
|
||||
} else if (event instanceof MqttMessageDeliveryEvent) {
|
||||
return next((MqttMessageDeliveryEvent) event, item);
|
||||
} else if (event instanceof MqttMessageEvent) {
|
||||
return next((MqttMessageEvent) event, item);
|
||||
} else if (event instanceof MqttReconnectionEvent) {
|
||||
return next((MqttReconnectionEvent) event, item);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean next(MqttConnectionExceptionEvent event, MqttItem item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
default boolean next(MqttConnectionLostEvent event, MqttItem item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
default boolean next(MqttConnectionSuccessEvent event, MqttItem item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
default boolean next(MqttMessageDeliveryEvent event, MqttItem item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
default boolean next(MqttMessageEvent event, MqttItem item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
default boolean next(MqttReconnectionEvent event, MqttItem item) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,181 @@
|
||||
package com.ruoyi.mqtt.config;
|
||||
|
||||
import com.ruoyi.mqtt.MqttEventHandler;
|
||||
import com.ruoyi.mqtt.MqttFactory;
|
||||
import com.ruoyi.mqtt.MqttItem;
|
||||
import com.ruoyi.mqtt.MqttUtil;
|
||||
import com.ruoyi.mqtt.event.MqttEvent;
|
||||
import com.ruoyi.mqtt.event.MqttSendEvent;
|
||||
import com.ruoyi.mqtt.event.MqttSendTopicEvent;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
|
||||
import org.eclipse.paho.client.mqttv3.MqttException;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(MqttProperties.class)
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class MqttConfig implements MqttFactory, MqttEventHandler, PriorityOrdered {
|
||||
|
||||
private final MqttProperties properties;
|
||||
private final ApplicationContext act;
|
||||
|
||||
@Getter
|
||||
private static MqttFactory mqttFactory;
|
||||
|
||||
private ScheduledExecutorService executorService;
|
||||
|
||||
private final Map<String, MqttItem> clients = new ConcurrentHashMap<>();
|
||||
|
||||
@PostConstruct
|
||||
public void init() throws Exception {
|
||||
if (!properties.getEnabled()) {
|
||||
log.info("mqtt模块未激活");
|
||||
return;
|
||||
}
|
||||
log.info("mqtt模块启动中...");
|
||||
try {
|
||||
executorService = act.getBeansOfType(ScheduledExecutorService.class).values().stream().findFirst().get();
|
||||
} catch (Exception e) {
|
||||
executorService = new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors());
|
||||
}
|
||||
properties.getConfigs().forEach((a, b) -> {
|
||||
if (!b.getEnabled()) {
|
||||
return;
|
||||
}
|
||||
put(a, b, this);
|
||||
});
|
||||
mqttFactory = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean next(MqttEvent event, MqttItem item) {
|
||||
log.debug("mqtt event: {} = {}", event.getConfigName(), event.getClass().getName());
|
||||
act.publishEvent(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void destroy() {
|
||||
clients.forEach((a, b) -> {
|
||||
try {
|
||||
b.destroy();
|
||||
} catch (Exception e) {
|
||||
log.error("mqtt客户端关闭失败:" + a, e);
|
||||
}
|
||||
});
|
||||
try {
|
||||
executorService.shutdown();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public MqttItem get(String configName) {
|
||||
return clients.get(configName);
|
||||
}
|
||||
|
||||
|
||||
public void put(String configName, MqttProperties.Config config, MqttEventHandler... handlers) {
|
||||
if (clients.containsKey(configName)) {
|
||||
throw new RuntimeException("配置项已经存在");
|
||||
}
|
||||
MqttItem mqttItem = new MqttItem(configName, config, executorService);
|
||||
if (handlers != null && handlers.length > 0) {
|
||||
Collections.addAll(mqttItem.getMessageHandlers(), handlers);
|
||||
}
|
||||
clients.put(configName, mqttItem);
|
||||
log.debug("mqtt配置项添加:" + configName);
|
||||
}
|
||||
|
||||
public boolean contains(String configName) {
|
||||
return clients.containsKey(configName);
|
||||
}
|
||||
|
||||
public void remove(String configName) {
|
||||
if (contains(configName)) {
|
||||
get(configName).destroy();
|
||||
clients.remove(configName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public IMqttDeliveryToken send(String configName, String topic, byte[] payload, int qos, boolean retained) throws MqttException {
|
||||
MqttItem item = get(configName);
|
||||
if (item == null) {
|
||||
throw new RuntimeException("mqtt客户端未找到:" + configName);
|
||||
}
|
||||
log.debug("mqtt发送成功:configName={},topic={}", configName, topic);
|
||||
return item.getClient().publish(topic, payload, qos, retained);
|
||||
}
|
||||
|
||||
public IMqttDeliveryToken send(String configName, String sendName, byte[] payload, String... params) throws MqttException {
|
||||
|
||||
MqttItem item = get(configName);
|
||||
if (item == null) {
|
||||
throw new RuntimeException("mqtt客户端未找到:" + configName);
|
||||
}
|
||||
|
||||
MqttProperties.Topic topic = item.getConfig().getSends().get(sendName);
|
||||
if (topic == null) {
|
||||
throw new RuntimeException("mqtt配置的主题未找到:" + configName + " = " + sendName);
|
||||
}
|
||||
String topicTempalte = topic.getTopic();
|
||||
if (params != null) {
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
topicTempalte = topicTempalte.replace("{" + i + "}", params[i]);
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("mqtt发送成功:configName={},sendName={},topic={}", configName, sendName, topicTempalte);
|
||||
return item.getClient().publish(topicTempalte, payload, topic.getQos(), topic.getRetained());
|
||||
}
|
||||
|
||||
|
||||
@EventListener
|
||||
public void listener(MqttSendEvent event) throws MqttException {
|
||||
send(event.getConfigName(), event.getSendName(), event.getPayload(), event.getParams());
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void listener(MqttSendTopicEvent event) throws MqttException {
|
||||
send(event.getConfigName(), event.getTopic(), event.getPayload(), event.getQos(), event.isRetained());
|
||||
}
|
||||
|
||||
|
||||
// @Scheduled(cron = "*/5 * * * * ?")
|
||||
// public void test() {
|
||||
// String s = Long.toString(System.currentTimeMillis(), 36);
|
||||
// try {
|
||||
//// act.publishEvent(new MqttSendEvent("test",s.getBytes(StandardCharsets.UTF_8),s));
|
||||
// MqttUtil.send("test", s, new String[]{s});
|
||||
// log.info("test success:{}", s);
|
||||
// } catch (Exception e) {
|
||||
// log.info("test error:" + s, e);
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.HIGHEST_PRECEDENCE; // 最高优先级
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.ruoyi.mqtt.event;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* MQTT连接异常
|
||||
*/
|
||||
public final class MqttConnectionExceptionEvent extends MqttEvent{
|
||||
|
||||
@Getter
|
||||
private Throwable cause;
|
||||
|
||||
public MqttConnectionExceptionEvent(Throwable cause) {
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
public MqttConnectionExceptionEvent(String configName, Throwable cause) {
|
||||
super(configName);
|
||||
this.cause = cause;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.ruoyi.mqtt.event;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* MQTT连接丢失
|
||||
*/
|
||||
public final class MqttConnectionLostEvent extends MqttEvent{
|
||||
|
||||
@Getter
|
||||
private Throwable cause;
|
||||
|
||||
public MqttConnectionLostEvent(Throwable cause) {
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
public MqttConnectionLostEvent(String configName,Throwable cause) {
|
||||
super(configName);
|
||||
this.cause = cause;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.ruoyi.mqtt.event;
|
||||
|
||||
/**
|
||||
* MQTT连接成功
|
||||
*/
|
||||
public final class MqttConnectionSuccessEvent extends MqttEvent{
|
||||
|
||||
public MqttConnectionSuccessEvent() {
|
||||
}
|
||||
|
||||
public MqttConnectionSuccessEvent(String configName) {
|
||||
super(configName);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package com.ruoyi.mqtt.event;
|
||||
|
||||
import com.ruoyi.mqtt.config.MqttProperties;
|
||||
import lombok.Getter;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* Mqtt事件
|
||||
*/
|
||||
public abstract class MqttEvent extends ApplicationEvent {
|
||||
@Getter
|
||||
private String configName = MqttProperties.DEFAULT;
|
||||
|
||||
public MqttEvent() {
|
||||
this(MqttProperties.DEFAULT);
|
||||
}
|
||||
|
||||
public MqttEvent(String configName) {
|
||||
super(configName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSource() {
|
||||
return (String)super.getSource();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package com.ruoyi.mqtt.event;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
|
||||
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
||||
|
||||
/**
|
||||
* 接受的消息
|
||||
*/
|
||||
public final class MqttMessageDeliveryEvent extends MqttEvent {
|
||||
|
||||
@Getter
|
||||
private IMqttDeliveryToken token;
|
||||
|
||||
|
||||
public MqttMessageDeliveryEvent(IMqttDeliveryToken token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public MqttMessageDeliveryEvent(String configName, IMqttDeliveryToken token) {
|
||||
super(configName);
|
||||
this.token = token;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package com.ruoyi.mqtt.event;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
||||
|
||||
/**
|
||||
* 接受的消息
|
||||
*/
|
||||
public final class MqttMessageEvent extends MqttEvent {
|
||||
|
||||
@Getter
|
||||
private String topic;
|
||||
|
||||
@Getter
|
||||
private MqttMessage message;
|
||||
|
||||
public MqttMessageEvent(String topic, MqttMessage message) {
|
||||
this.topic = topic;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public MqttMessageEvent(String configName, String topic, MqttMessage message) {
|
||||
super(configName);
|
||||
this.topic = topic;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
|
||||
package com.ruoyi.mqtt.event;
|
||||
|
||||
/**
|
||||
* MQTT重连开始
|
||||
*/
|
||||
public final class MqttReconnectionEvent extends MqttEvent{
|
||||
|
||||
public MqttReconnectionEvent() {
|
||||
}
|
||||
|
||||
public MqttReconnectionEvent(String configName) {
|
||||
super(configName);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package com.ruoyi.mqtt.event;
|
||||
|
||||
import com.ruoyi.mqtt.config.MqttProperties;
|
||||
import lombok.Getter;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 发送的
|
||||
*/
|
||||
public class MqttSendEvent extends ApplicationEvent {
|
||||
|
||||
@Getter
|
||||
private String configName;
|
||||
|
||||
@Getter
|
||||
private String sendName;
|
||||
|
||||
@Getter
|
||||
private byte[] payload;
|
||||
|
||||
@Getter
|
||||
private String[] params;
|
||||
|
||||
|
||||
public MqttSendEvent(String configName, String sendName, byte[] payload, String... params) {
|
||||
super(configName);
|
||||
this.configName = configName;
|
||||
this.sendName = sendName;
|
||||
this.payload = payload;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public MqttSendEvent(String configName, String sendName, byte[] payload, List<String> params) {
|
||||
this(configName, sendName, payload, params.toArray(new String[0]));
|
||||
}
|
||||
|
||||
public MqttSendEvent(String sendName, byte[] payload, List<String> params) {
|
||||
this(MqttProperties.DEFAULT, sendName, payload, params.toArray(new String[0]));
|
||||
}
|
||||
|
||||
public MqttSendEvent(String sendName, byte[] payload, String... params) {
|
||||
this(MqttProperties.DEFAULT, sendName, payload, params);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getSource() {
|
||||
return (String) super.getSource();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
package com.ruoyi.mqtt.event;
|
||||
|
||||
import com.ruoyi.mqtt.config.MqttProperties;
|
||||
import lombok.Getter;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 发送的
|
||||
*/
|
||||
public class MqttSendTopicEvent extends ApplicationEvent {
|
||||
|
||||
@Getter
|
||||
private String configName;
|
||||
|
||||
@Getter
|
||||
private String topic;
|
||||
|
||||
@Getter
|
||||
private byte[] payload;
|
||||
|
||||
@Getter
|
||||
private int qos;
|
||||
|
||||
@Getter
|
||||
private boolean retained;
|
||||
|
||||
|
||||
public MqttSendTopicEvent(String configName, String topic, byte[] payload, int qos, boolean retained) {
|
||||
super(configName);
|
||||
this.configName = configName;
|
||||
this.topic = topic;
|
||||
this.payload = payload;
|
||||
this.qos = qos;
|
||||
this.retained = retained;
|
||||
}
|
||||
|
||||
public MqttSendTopicEvent(String topic, byte[] payload, int qos, boolean retained) {
|
||||
this(MqttProperties.DEFAULT, topic, payload, qos, retained);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getSource() {
|
||||
return (String) super.getSource();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package com.ruoyi.mqtt.rmi;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
/**
|
||||
* MQTT RMI 请求类
|
||||
*
|
||||
* 该类用于封装 MQTT RMI 调用的请求数据。
|
||||
*/
|
||||
public class MqttRmiRequest {
|
||||
|
||||
private String requestId;
|
||||
private String name;
|
||||
private String method;
|
||||
private List<JsonNode> args;
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package com.ruoyi.mqtt.rmi;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
public interface MqttRmiRequestSender {
|
||||
|
||||
ObjectMapper getMapper();
|
||||
|
||||
MqttRmiResponse request(MqttRmiRequest request, long timeout);
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.ruoyi.mqtt.rmi;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
/**
|
||||
* MQTT RMI 响应类
|
||||
*
|
||||
* 该类用于封装 MQTT RMI 调用的响应数据。
|
||||
*/
|
||||
public class MqttRmiResponse {
|
||||
|
||||
private String requestId;
|
||||
private String name;
|
||||
private String method;
|
||||
private Boolean ok;
|
||||
private JsonNode body;
|
||||
private String error;
|
||||
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.ruoyi.mqtt.rmi;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.ruoyi.mqtt.rmi.impl.MqttRmiConsumerImpl;
|
||||
|
||||
public class MqttRmiUtil {
|
||||
|
||||
private MqttRmiUtil() {
|
||||
}
|
||||
|
||||
public static MqttRmiResponse request(MqttRmiRequest request, long timeout) {
|
||||
return MqttRmiConsumerImpl.getSender().request(request, timeout);
|
||||
}
|
||||
|
||||
public static MqttRmiResponse request(MqttRmiRequest request) {
|
||||
return MqttRmiConsumerImpl.getSender().request(request, 0);
|
||||
}
|
||||
|
||||
public static ObjectMapper getMapper() {
|
||||
return MqttRmiConsumerImpl.getSender().getMapper();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package com.ruoyi.mqtt.rmi.annotation;
|
||||
|
||||
import com.ruoyi.mqtt.rmi.config.MqttRmiConfig;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 非ruoyi项目需要使用本注解
|
||||
*/
|
||||
@Import(MqttRmiConfig.class)
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface MqttRmiEnabled {
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.ruoyi.mqtt.rmi.annotation;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
/**
|
||||
* MQTT RMI 提供者注解
|
||||
*
|
||||
* 该注解用于标记一个类为 MQTT RMI 服务提供者。
|
||||
*/
|
||||
public @interface MqttRmiProvider {
|
||||
|
||||
/**
|
||||
* 默认为类名
|
||||
* @return
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.ruoyi.mqtt.rmi.annotation;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
/**
|
||||
* MQTT RMI 服务注解
|
||||
*
|
||||
* 该注解用于标记一个接口为 MQTT RMI 服务接口。
|
||||
*/
|
||||
public @interface MqttRmiService {
|
||||
|
||||
/**
|
||||
* 默认为类名
|
||||
* @return
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package com.ruoyi.mqtt.rmi.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.ruoyi.mqtt.MqttFactory;
|
||||
import com.ruoyi.mqtt.rmi.impl.MqttRmiConsumerBeanDefinitionRegistryPostProcessor;
|
||||
import com.ruoyi.mqtt.rmi.impl.MqttRmiConsumerImpl;
|
||||
import com.ruoyi.mqtt.rmi.impl.MqttRmiProviderImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(MqttRmiProperties.class)
|
||||
/**
|
||||
* MQTT RMI 配置类
|
||||
*
|
||||
* 该类负责配置和初始化 MQTT RMI 相关的 Bean。
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class MqttRmiConfig implements PriorityOrdered {
|
||||
|
||||
MqttRmiProperties properties;
|
||||
|
||||
@Bean
|
||||
// @ConditionalOnProperty(prefix = MqttRmiProperties.PREFIX,name = "provider",havingValue = "true",matchIfMissing = false)
|
||||
public MqttRmiProviderImpl mqttRmiProviderImpl(MqttRmiProperties properties, MqttFactory factory, ObjectMapper mapper) {
|
||||
return new MqttRmiProviderImpl(properties, factory, mapper);
|
||||
}
|
||||
|
||||
@Bean
|
||||
// @ConditionalOnProperty(prefix = MqttRmiProperties.PREFIX,name = "consumer",havingValue = "true",matchIfMissing = false)
|
||||
public MqttRmiConsumerImpl mqttRmiConsumer(MqttRmiProperties properties, MqttFactory factory, ObjectMapper mapper, ApplicationContext act) {
|
||||
return new MqttRmiConsumerImpl(properties, factory, mapper, act);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = MqttRmiProperties.PREFIX,name = "consumer",havingValue = "true",matchIfMissing = false)
|
||||
public MqttRmiConsumerBeanDefinitionRegistryPostProcessor mqttRmiConsumerBeanDefinitionRegistryPostProcessor() {
|
||||
return new MqttRmiConsumerBeanDefinitionRegistryPostProcessor();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.HIGHEST_PRECEDENCE; // 最高优先级
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.ruoyi.mqtt.rmi.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* MQTT RMI 错误异常类
|
||||
*
|
||||
* 该类表示 MQTT RMI 调用过程中发生错误的异常。
|
||||
*/
|
||||
public class MqttRmiErrorException extends MqttRmiException {
|
||||
|
||||
public MqttRmiErrorException(String requestId) {
|
||||
super(requestId);
|
||||
}
|
||||
|
||||
public MqttRmiErrorException(String requestId, String message) {
|
||||
super(requestId, message);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package com.ruoyi.mqtt.rmi.exception;
|
||||
|
||||
/**
|
||||
* MQTT RMI 超时异常类
|
||||
*
|
||||
* 该类表示 MQTT RMI 调用超时的异常。
|
||||
*/
|
||||
public class MqttRmiTimeoutException extends MqttRmiException {
|
||||
|
||||
public MqttRmiTimeoutException(String requestId) {
|
||||
super(requestId);
|
||||
}
|
||||
|
||||
public MqttRmiTimeoutException(String requestId, String message) {
|
||||
super(requestId, message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package com.ruoyi.mqtt.rmi.test;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.ruoyi.mqtt.rmi.MqttRmiRequestSender;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
//@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class MqttRmiTestServiceTest implements ApplicationRunner {
|
||||
|
||||
private final MqttRmiTestService service;
|
||||
|
||||
private final MqttRmiRequestSender sender;
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
// public MqttRmiTestServiceTest() {
|
||||
// log.info("MqttRmiTestServiceTest create");
|
||||
// }
|
||||
|
||||
|
||||
public void test() {
|
||||
String s = Long.toString(System.currentTimeMillis(), 36);
|
||||
try {
|
||||
service.test0();
|
||||
log.info("service.test0 success");
|
||||
} catch (Exception e) {
|
||||
log.info("service.test0 error",e);
|
||||
}
|
||||
try {
|
||||
service.test1(s);
|
||||
log.info("service.test1(s) success");
|
||||
} catch (Exception e) {
|
||||
log.info("service.test1(s) error",e);
|
||||
}
|
||||
|
||||
try {
|
||||
String ret =service.test2(s);
|
||||
log.info("service.test2(s) success: "+ret);
|
||||
} catch (Exception e) {
|
||||
log.info("service.test2(s) error",e);
|
||||
}
|
||||
|
||||
try {
|
||||
String ret =service.test3(s);
|
||||
log.info("service.test3(s) success: "+ret);
|
||||
} catch (Exception e) {
|
||||
log.info("service.test3(s) error",e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
test();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
com.ruoyi.mqtt.config.MqttConfig
|
||||
com.ruoyi.mqtt.rmi.config.MqttRmiConfig
|
||||
@ -0,0 +1,45 @@
|
||||
--- # mqtt配置
|
||||
spring:
|
||||
mqtt:
|
||||
enabled: true # 是否启用MQTT功能, 默认值: false
|
||||
default-config: default # 默认配置名称, 默认值: "default"
|
||||
configs:
|
||||
default:
|
||||
enabled: true # 是否启用该MQTT客户端配置, 默认值: true
|
||||
client-id: ${ruoyi.name}-client # 客户端编号(唯一), 默认值: 基于系统时间生成的36进制字符串
|
||||
url: tcp://192.168.3.222:1883 # 服务器URL, 默认值: tcp://127.0.0.1:1883
|
||||
urls: # 服务器集群URL列表(可选)
|
||||
- tcp://192.168.3.222:1883
|
||||
username: ${ruoyi.name} # 用户名, 默认值: admin
|
||||
password: ${ruoyi.name}1415926 # 密码, 默认值: 123456
|
||||
clean-session: true # 是否清空session, 默认值: true
|
||||
connection-timeout: 5 # 连接超时时间(秒), 默认值: 5
|
||||
keep-alive-interval: 60 # 心跳时间(秒), 默认值: 60
|
||||
will-topic: will/topic # 遗嘱主题, 默认值: will/topic
|
||||
will-message: offline # 遗嘱消息, 默认值: offline
|
||||
will-qos: 0 # 遗嘱消息的服务质量(QoS), 默认值: 0
|
||||
custom-web-socket-headers: {} # 自定义WebSocket头部信息
|
||||
ssl-properties: {} # SSL属性配置
|
||||
mqtt-version: MQTT_VERSION_DEFAULT # MQTT协议版本, 默认值: MQTT_VERSION_DEFAULT
|
||||
max-inflight: 10 # 最大未确认消息数量, 默认值: 10
|
||||
sends: # 发送主题映射 发送主题可以使用如:{0},{1},{2},发送时使用params数组替换
|
||||
test:
|
||||
topic: ${ruoyi.name}/test/{0}
|
||||
qos: 0 # 服务质量(QoS), 默认值: 0
|
||||
retained: false # 是否保留消息, 默认值: false
|
||||
subscribes: # 订阅主题列表,订阅主题可以使用 单级通配符 "+" 和多级通配符 "#"
|
||||
- topic: ${ruoyi.name}/#
|
||||
qos: 0 # 服务质量(QoS), 默认值: 0
|
||||
|
||||
rmi:
|
||||
enabled: false # 是否启用MQTT的远程方法调用功能, 默认值: false
|
||||
provider: true # 是否是提供者,负责接口的实现, 默认值: false
|
||||
consumer: true # 是否是消费者,负责接口的定义, 默认值: false
|
||||
config-name: default # 默认配置名称, 默认值: "default"
|
||||
topic: # 用于rmi的发送和订阅主题,不能含变量和通配符
|
||||
topic: ${ruoyi.name}/rmi # 主题名称
|
||||
qos: 0 # 服务质量(QoS), 默认值: 0
|
||||
retained: false # 是否保留消息, 默认值: false
|
||||
topic-request-prefix: "" # 发送请求时,主题添加的前缀
|
||||
not-find-method-send-error: true # 没有发现方法是否发送错误, 默认值: true
|
||||
timeout: 500 # 远程方法调用默认超时(毫秒), 默认值: 500
|
||||
Loading…
Reference in New Issue