- 数据库:添加 amt_status 和 agent_status 字段 - 后端:Device 实体类和 DeviceDTO 添加新状态字段 - 后端:DeviceService 添加状态检测和更新方法 - 后端:添加 AmtStatusCheckTask 定时任务(每30秒检测一次) - 前端:设备列表页面拆分状态列显示 - 前端:统计卡片显示 AMT 和 Agent 在线/离线数量 - 网络扫描:自动设置 AMT 状态为在线 - 文档:添加 DEVICE_STATUS_SPLIT.md 和 AMT_REALTIME_STATUS.md
418 lines
9.4 KiB
Markdown
418 lines
9.4 KiB
Markdown
# AMT 实时状态检测功能
|
||
|
||
## 功能概述
|
||
|
||
实现了 AMT 设备状态的实时检测和自动更新功能,无需手动刷新即可看到设备的在线/离线状态变化。
|
||
|
||
## 实现方案
|
||
|
||
### 后端实现
|
||
|
||
#### 1. 定时任务 (AmtStatusCheckTask.java)
|
||
|
||
**位置**: `backend/src/main/java/com/soybean/admin/task/AmtStatusCheckTask.java`
|
||
|
||
**功能**:
|
||
- 每 30 秒自动检测一次所有设备的 AMT 状态
|
||
- 使用线程池并发检测,提高效率
|
||
- 只在状态变化时更新数据库,减少写入操作
|
||
- 使用 Socket 连接检测 AMT 端口(16992/16993)可用性
|
||
|
||
**关键特性**:
|
||
```java
|
||
@Scheduled(fixedRate = 30000, initialDelay = 10000)
|
||
public void checkAmtStatus() {
|
||
// 每 30 秒执行一次
|
||
// 初始延迟 10 秒,等待应用完全启动
|
||
}
|
||
```
|
||
|
||
**检测逻辑**:
|
||
1. 查询所有有 IP 地址的设备
|
||
2. 并发检测每个设备的 AMT 端口连接性
|
||
3. 先尝试 HTTP (16992),失败则尝试 HTTPS (16993)
|
||
4. 根据连接结果更新设备的 `amt_status` 字段
|
||
5. 只有状态变化时才更新数据库和记录日志
|
||
|
||
**线程池配置**:
|
||
- 固定 20 个线程
|
||
- 使用 CompletableFuture 异步执行
|
||
- 等待所有检测完成后结束任务
|
||
|
||
#### 2. 启用定时任务
|
||
|
||
**修改**: `backend/src/main/java/com/soybean/admin/SoybeanAdminApplication.java`
|
||
|
||
添加 `@EnableScheduling` 注解启用 Spring 定时任务功能:
|
||
|
||
```java
|
||
@SpringBootApplication
|
||
@MapperScan("com.soybean.admin.mapper")
|
||
@EnableScheduling // 启用定时任务
|
||
public class SoybeanAdminApplication {
|
||
// ...
|
||
}
|
||
```
|
||
|
||
### 前端实现
|
||
|
||
#### 1. 自动轮询刷新
|
||
|
||
**位置**: `src/views/device/list/index.vue`
|
||
|
||
**功能**:
|
||
- 页面加载后每 10 秒自动刷新设备列表
|
||
- 组件卸载时自动清除定时器
|
||
- 显示"自动刷新中"状态标签
|
||
|
||
**实现代码**:
|
||
```typescript
|
||
onMounted(() => {
|
||
loadData();
|
||
|
||
// 启动定时刷新,每 10 秒刷新一次设备状态
|
||
const refreshInterval = setInterval(() => {
|
||
loadData();
|
||
}, 10000);
|
||
|
||
// 组件卸载时清除定时器
|
||
onUnmounted(() => {
|
||
clearInterval(refreshInterval);
|
||
});
|
||
});
|
||
```
|
||
|
||
#### 2. 手动刷新按钮
|
||
|
||
添加了"刷新状态"按钮,用户可以随时手动刷新:
|
||
|
||
```vue
|
||
<n-button @click="handleManualRefresh" :loading="loading">
|
||
<icon-mdi-refresh class="mr-4px text-16px" />
|
||
刷新状态
|
||
</n-button>
|
||
```
|
||
|
||
#### 3. 状态指示器
|
||
|
||
显示自动刷新状态:
|
||
|
||
```vue
|
||
<n-tag v-if="autoRefreshEnabled" type="success" size="small">
|
||
<icon-mdi-autorenew class="mr-4px" />
|
||
自动刷新中
|
||
</n-tag>
|
||
```
|
||
|
||
## 工作流程
|
||
|
||
### 完整流程图
|
||
|
||
```
|
||
后端定时任务 (每30秒)
|
||
↓
|
||
检测所有设备 AMT 端口
|
||
↓
|
||
更新数据库状态
|
||
↓
|
||
前端轮询 (每10秒)
|
||
↓
|
||
获取最新设备列表
|
||
↓
|
||
更新界面显示
|
||
```
|
||
|
||
### 时间线示例
|
||
|
||
```
|
||
T=0s: 后端启动,10秒后开始第一次检测
|
||
T=10s: 后端第一次检测所有设备
|
||
T=10s: 前端页面加载,开始第一次数据获取
|
||
T=20s: 前端第二次刷新
|
||
T=30s: 前端第三次刷新
|
||
T=40s: 后端第二次检测 + 前端第四次刷新
|
||
T=50s: 前端第五次刷新
|
||
...
|
||
```
|
||
|
||
## 性能优化
|
||
|
||
### 后端优化
|
||
|
||
1. **并发检测**: 使用 20 个线程并发检测,大幅提升检测速度
|
||
2. **快速超时**: Socket 连接超时设置为 3 秒,避免长时间等待
|
||
3. **增量更新**: 只在状态变化时更新数据库
|
||
4. **简单检测**: 只检测端口连接性,不进行完整的 AMT 认证
|
||
|
||
### 前端优化
|
||
|
||
1. **合理间隔**: 10 秒刷新间隔,平衡实时性和性能
|
||
2. **自动清理**: 组件卸载时清除定时器,避免内存泄漏
|
||
3. **加载状态**: 显示加载状态,提升用户体验
|
||
|
||
## 状态检测逻辑
|
||
|
||
### 检测方法
|
||
|
||
使用 TCP Socket 连接检测,而不是完整的 AMT 认证:
|
||
|
||
```java
|
||
private boolean checkAmtConnectivity(String ipAddress) {
|
||
try {
|
||
// 尝试连接 AMT HTTP 端口
|
||
Socket socket = new Socket();
|
||
socket.connect(new InetSocketAddress(ipAddress, 16992), 3000);
|
||
socket.close();
|
||
return true;
|
||
} catch (Exception e) {
|
||
// HTTP 失败,尝试 HTTPS
|
||
try {
|
||
Socket socket = new Socket();
|
||
socket.connect(new InetSocketAddress(ipAddress, 16993), 3000);
|
||
socket.close();
|
||
return true;
|
||
} catch (Exception ex) {
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 优点
|
||
|
||
1. **快速**: 不需要完整的 SOAP 请求和认证
|
||
2. **轻量**: 只检测端口可用性
|
||
3. **可靠**: 能准确判断 AMT 服务是否运行
|
||
|
||
### 缺点
|
||
|
||
1. **不验证认证**: 不检查凭证是否正确
|
||
2. **端口占用**: 如果端口被其他服务占用,可能误判
|
||
|
||
## 使用说明
|
||
|
||
### 1. 执行数据库更新
|
||
|
||
如果还没有执行过数据库更新,先运行:
|
||
|
||
```bash
|
||
update_device_status.bat
|
||
```
|
||
|
||
### 2. 重新编译后端
|
||
|
||
```bash
|
||
cd backend
|
||
mvn clean package -DskipTests
|
||
```
|
||
|
||
### 3. 启动后端服务
|
||
|
||
```bash
|
||
start_backend.bat
|
||
```
|
||
|
||
后端启动后,会在 10 秒后开始第一次 AMT 状态检测,之后每 30 秒检测一次。
|
||
|
||
### 4. 打开前端页面
|
||
|
||
访问设备列表页面,会看到:
|
||
- 右上角显示"自动刷新中"标签
|
||
- 设备的 AMT 状态列实时更新
|
||
- 可以点击"刷新状态"按钮手动刷新
|
||
|
||
## 日志查看
|
||
|
||
### 后端日志
|
||
|
||
查看后端控制台输出,会看到类似日志:
|
||
|
||
```
|
||
开始 AMT 状态检测任务
|
||
开始检测 10 个设备的 AMT 状态
|
||
设备 Server-01 (192.168.8.100) AMT 状态变化: offline -> online
|
||
设备 Server-02 (192.168.8.101) AMT 状态变化: online -> offline
|
||
AMT 状态检测任务完成
|
||
```
|
||
|
||
### 前端日志
|
||
|
||
打开浏览器开发者工具,可以看到每 10 秒发送一次设备列表请求。
|
||
|
||
## 测试场景
|
||
|
||
### 场景 1: 设备上线
|
||
|
||
1. 启动一台 AMT 设备
|
||
2. 等待最多 30 秒(后端检测周期)
|
||
3. 前端会在下次刷新时显示设备状态变为"在线"
|
||
|
||
### 场景 2: 设备下线
|
||
|
||
1. 关闭一台 AMT 设备
|
||
2. 等待最多 30 秒(后端检测周期)
|
||
3. 前端会在下次刷新时显示设备状态变为"离线"
|
||
|
||
### 场景 3: 手动刷新
|
||
|
||
1. 点击"刷新状态"按钮
|
||
2. 立即获取最新的设备状态
|
||
3. 不需要等待自动刷新周期
|
||
|
||
## 配置参数
|
||
|
||
### 后端配置
|
||
|
||
在 `AmtStatusCheckTask.java` 中可以调整:
|
||
|
||
```java
|
||
// 检测周期(毫秒)
|
||
@Scheduled(fixedRate = 30000, initialDelay = 10000)
|
||
// fixedRate: 30000 = 30秒
|
||
// initialDelay: 10000 = 10秒初始延迟
|
||
|
||
// 线程池大小
|
||
private final ExecutorService executorService = Executors.newFixedThreadPool(20);
|
||
// 20 个线程
|
||
|
||
// Socket 超时时间(毫秒)
|
||
socket.connect(new InetSocketAddress(ipAddress, 16992), 3000);
|
||
// 3000 = 3秒
|
||
```
|
||
|
||
### 前端配置
|
||
|
||
在 `index.vue` 中可以调整:
|
||
|
||
```typescript
|
||
// 刷新周期(毫秒)
|
||
const refreshInterval = setInterval(() => {
|
||
loadData();
|
||
}, 10000); // 10000 = 10秒
|
||
```
|
||
|
||
## 性能影响
|
||
|
||
### 后端资源消耗
|
||
|
||
- **CPU**: 低(主要是网络 I/O 等待)
|
||
- **内存**: 低(20 个线程 + 设备列表)
|
||
- **网络**: 每个设备每 30 秒一次连接检测
|
||
|
||
### 前端资源消耗
|
||
|
||
- **网络**: 每 10 秒一次 API 请求
|
||
- **内存**: 低(只更新设备列表数据)
|
||
- **CPU**: 低(简单的数据更新和渲染)
|
||
|
||
### 数据库影响
|
||
|
||
- **读取**: 每 30 秒读取所有设备列表
|
||
- **写入**: 只在状态变化时写入(增量更新)
|
||
- **索引**: 建议在 `ip_address` 和 `amt_status` 字段上建立索引
|
||
|
||
## 后续优化建议
|
||
|
||
### 1. WebSocket 推送
|
||
|
||
替代轮询方式,使用 WebSocket 实时推送状态变化:
|
||
|
||
**优点**:
|
||
- 更实时
|
||
- 减少网络请求
|
||
- 降低服务器负载
|
||
|
||
**实现**:
|
||
- 后端使用 Spring WebSocket
|
||
- 状态变化时主动推送到前端
|
||
- 前端监听 WebSocket 消息更新界面
|
||
|
||
### 2. 智能检测频率
|
||
|
||
根据设备状态调整检测频率:
|
||
|
||
- 在线设备: 每 60 秒检测一次
|
||
- 离线设备: 每 30 秒检测一次
|
||
- 新添加设备: 每 10 秒检测一次(前 5 分钟)
|
||
|
||
### 3. 批量状态查询接口
|
||
|
||
添加专门的状态查询接口,只返回状态信息:
|
||
|
||
```java
|
||
@GetMapping("/device/status")
|
||
public Result<Map<Long, String>> getDeviceStatus() {
|
||
// 只返回 id -> status 的映射
|
||
// 减少数据传输量
|
||
}
|
||
```
|
||
|
||
### 4. 状态变化通知
|
||
|
||
设备状态变化时发送通知:
|
||
|
||
- 邮件通知
|
||
- 短信通知
|
||
- 系统消息通知
|
||
- Webhook 回调
|
||
|
||
### 5. 历史状态记录
|
||
|
||
记录设备状态变化历史:
|
||
|
||
- 创建 `device_status_history` 表
|
||
- 记录每次状态变化的时间和原因
|
||
- 生成可用性报表
|
||
|
||
## 故障排查
|
||
|
||
### 问题 1: 状态不更新
|
||
|
||
**可能原因**:
|
||
- 后端定时任务未启动
|
||
- 数据库连接失败
|
||
- 网络不通
|
||
|
||
**排查方法**:
|
||
1. 查看后端日志是否有"开始 AMT 状态检测任务"
|
||
2. 检查数据库 `amt_status` 字段是否存在
|
||
3. 手动测试设备网络连接
|
||
|
||
### 问题 2: 前端不刷新
|
||
|
||
**可能原因**:
|
||
- 定时器未启动
|
||
- API 请求失败
|
||
- 组件已卸载
|
||
|
||
**排查方法**:
|
||
1. 打开浏览器开发者工具查看网络请求
|
||
2. 检查控制台是否有错误
|
||
3. 确认 `autoRefreshEnabled` 为 true
|
||
|
||
### 问题 3: 性能问题
|
||
|
||
**可能原因**:
|
||
- 设备数量过多
|
||
- 检测频率过高
|
||
- 网络延迟大
|
||
|
||
**解决方案**:
|
||
1. 增加检测周期(如改为 60 秒)
|
||
2. 增加线程池大小
|
||
3. 使用 WebSocket 替代轮询
|
||
|
||
## 总结
|
||
|
||
实现了完整的 AMT 实时状态检测功能:
|
||
|
||
✅ 后端定时任务自动检测(每 30 秒)
|
||
✅ 前端自动刷新显示(每 10 秒)
|
||
✅ 手动刷新按钮
|
||
✅ 状态指示器
|
||
✅ 并发检测优化
|
||
✅ 增量数据库更新
|
||
✅ 完整的日志记录
|
||
|
||
用户无需手动刷新,即可实时看到设备的 AMT 在线/离线状态变化。
|