Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 183 additions & 0 deletions docs/MONITOR_STATISTICS_API.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# Monitor Statistics API

This module provides endpoints to query historical system monitoring metrics.

## Database Setup

Before using the monitor statistics feature, you need to create the required database table. Run the following SQL script:

```bash
mysql -u your_username -p your_database < migration/monitor/create_sys_main_metrics_table.sql
```

Or execute the SQL directly:

```sql
CREATE TABLE IF NOT EXISTS `sys_main_metrics` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 'Primary key, auto-increment',
`record_time` DATETIME NOT NULL COMMENT 'Time when the metrics were recorded',
`cpu_usage` DOUBLE COMMENT 'CPU usage percentage (0-100)',
`memory_used` DOUBLE COMMENT 'Memory used in bytes',
`memory_total` DOUBLE COMMENT 'Total memory in bytes',
`disk_used` DOUBLE COMMENT 'Disk space used in bytes',
`disk_total` DOUBLE COMMENT 'Total disk space in bytes',
`network_recv_speed` BIGINT COMMENT 'Network receive speed in bytes per second',
`network_sent_speed` BIGINT COMMENT 'Network send speed in bytes per second',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'Record creation time',
INDEX `idx_record_time` (`record_time`) COMMENT 'Index for efficient time range queries'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='System monitoring metrics table';
```

## API Endpoints

### GET/POST `/api/monitor/getByMillRange`

Query system metrics within a specified time range.

#### Request Methods

**Method 1: GET with Query Parameters**

```bash
GET /api/monitor/getByMillRange?startMills=1707110400000&endMills=1707114000000
```

**Method 2: POST with JSON Body**

```bash
POST /api/monitor/getByMillRange
Content-Type: application/json

{
"startMills": 1707110400000,
"endMills": 1707114000000
}
```

**Method 3: Mixed (Query Params + JSON Body)**

The endpoint can accept parameters from both sources. JSON body values take precedence.

#### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| startMills | Long | Yes | Start time in milliseconds since epoch |
| endMills | Long | Yes | End time in milliseconds since epoch |

#### Response

**Success (200 OK)**

Returns an array of metrics objects:

```json
[
{
"id": 1,
"recordTime": "2024-02-05T10:00:00",
"cpuUsage": 45.5,
"memoryUsed": 8589934592,
"memoryTotal": 17179869184,
"diskUsed": 107374182400,
"diskTotal": 214748364800,
"networkRecvSpeed": 1048576,
"networkSentSpeed": 524288,
"createTime": "2024-02-05T10:00:01"
}
]
```

**Error Responses**

- **400 Bad Request**: Missing required parameters
```
startMills and endMills are required via query params or JSON body
```

- **500 Internal Server Error**: Server-side error during query execution
```
获取最近监控数据失败!
```

## Data Aggregation

The service automatically aggregates metrics based on the time range to optimize data volume:

| Time Range | Grouping Interval |
|------------|-------------------|
| ≤ 1 hour | 1 minute (60 seconds) |
| ≤ 6 hours | 5 minutes (300 seconds) |
| ≤ 24 hours | 10 minutes (600 seconds) |
| ≤ 7 days | 1 hour (3600 seconds) |
| > 7 days | 1 day (86400 seconds) |

Metrics are averaged within each interval to reduce data points while maintaining trends.

## Usage Examples

### Using cURL

**GET request:**
```bash
curl -X GET "http://localhost:8080/api/monitor/getByMillRange?startMills=1707110400000&endMills=1707114000000"
```

**POST request:**
```bash
curl -X POST "http://localhost:8080/api/monitor/getByMillRange" \
-H "Content-Type: application/json" \
-d '{"startMills":1707110400000,"endMills":1707114000000}'
```

### Using JavaScript/Fetch

```javascript
// GET request
fetch('/api/monitor/getByMillRange?startMills=1707110400000&endMills=1707114000000')
.then(response => response.json())
.then(data => console.log(data));

// POST request
fetch('/api/monitor/getByMillRange', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
startMills: 1707110400000,
endMills: 1707114000000
})
})
.then(response => response.json())
.then(data => console.log(data));
```

## Implementation Details

### Components

1. **Entity**: `SysMainMetrics` - JPA entity representing the metrics table
2. **Mapper**: `SysMainMetricsMapper` - MyBatis Plus mapper with custom SQL
3. **Service**: `SysMetricsStorageService` - Business logic for querying and aggregating metrics
4. **Controller**: `MonitorStatisticsController` - REST API endpoint

### Key Features

- Supports both GET and POST methods for flexibility
- Automatic time-based data aggregation
- Efficient database queries with indexed columns
- Comprehensive error handling
- OpenAPI/Swagger documentation included
- Full unit test coverage

## Testing

Unit tests are located in:
- `src/test/java/cn/programcx/foxnaserver/service/monitor/SysMetricsStorageServiceTest.java`
- `src/test/java/cn/programcx/foxnaserver/api/monitor/MonitorStatisticsControllerTest.java`

Run tests with:
```bash
mvn test -Dtest="SysMetricsStorageServiceTest,MonitorStatisticsControllerTest"
```
16 changes: 16 additions & 0 deletions migration/monitor/create_sys_main_metrics_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- Migration script for sys_main_metrics table
-- This table stores system monitoring metrics for tracking historical performance data

CREATE TABLE IF NOT EXISTS `sys_main_metrics` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 'Primary key, auto-increment',
`record_time` DATETIME NOT NULL COMMENT 'Time when the metrics were recorded',
`cpu_usage` DOUBLE COMMENT 'CPU usage percentage (0-100)',
`memory_used` DOUBLE COMMENT 'Memory used in bytes',
`memory_total` DOUBLE COMMENT 'Total memory in bytes',
`disk_used` DOUBLE COMMENT 'Disk space used in bytes',
`disk_total` DOUBLE COMMENT 'Total disk space in bytes',
`network_recv_speed` BIGINT COMMENT 'Network receive speed in bytes per second',
`network_sent_speed` BIGINT COMMENT 'Network send speed in bytes per second',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'Record creation time',
INDEX `idx_record_time` (`record_time`) COMMENT 'Index for efficient time range queries'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='System monitoring metrics table';
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package cn.programcx.foxnaserver.api.monitor;

import cn.programcx.foxnaserver.entity.SysMainMetrics;
import cn.programcx.foxnaserver.service.monitor.SysMetricsStorageService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;

@Slf4j
@RestController
@RequestMapping("/api/monitor")
@Tag(name = "Monitor Statistics", description = "监控统计相关接口")
public class MonitorStatisticsController {

@Autowired
private SysMetricsStorageService sysMetricsStorageService;

@Operation(
summary = "获取时间范围内的监控统计数据",
description = "通过毫秒时间戳范围查询监控统计数据,支持 GET 和 POST 请求"
)
@ApiResponse(
responseCode = "200",
description = "成功返回监控数据列表"
)
@ApiResponse(
responseCode = "400",
description = "缺少必需的参数 startMills 或 endMills"
)
@ApiResponse(
responseCode = "500",
description = "服务器内部错误"
)
@RequestMapping(value = "/getByMillRange", method = {RequestMethod.GET, RequestMethod.POST})
public ResponseEntity<?> getRecentStatisticsByMillRange(
@RequestParam(value = "startMills", required = false) Long startMills,
@RequestParam(value = "endMills", required = false) Long endMills,
@RequestBody(required = false) MetricsRangeRequest payload) {

// Merge parameters from query string and JSON body
if (payload != null) {
if (startMills == null) startMills = payload.getStartMills();
if (endMills == null) endMills = payload.getEndMills();
}

// Validate required parameters
if (startMills == null || endMills == null) {
return ResponseEntity.badRequest()
.body("startMills and endMills are required via query params or JSON body");
}

// Convert milliseconds to LocalDateTime
LocalDateTime startTime = LocalDateTime.ofInstant(
Instant.ofEpochMilli(startMills),
ZoneId.systemDefault()
);
LocalDateTime endTime = LocalDateTime.ofInstant(
Instant.ofEpochMilli(endMills),
ZoneId.systemDefault()
);

try {
List<SysMainMetrics> sysMainMetrics = sysMetricsStorageService.selectRecentMetrics(startTime, endTime);
return ResponseEntity.ok(sysMainMetrics);
} catch (Exception e) {
log.error("获取最近监控数据失败:{}", e.getMessage(), e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("获取最近监控数据失败! ");
}
}

@Getter
public static class MetricsRangeRequest {
private Long startMills;
private Long endMills;

public void setStartMills(Long startMills) {
this.startMills = startMills;
}

public void setEndMills(Long endMills) {
this.endMills = endMills;
}
}
}
36 changes: 36 additions & 0 deletions src/main/java/cn/programcx/foxnaserver/entity/SysMainMetrics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package cn.programcx.foxnaserver.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.time.LocalDateTime;

@Data
@EqualsAndHashCode(callSuper = false)
@TableName("sys_main_metrics")
public class SysMainMetrics {

@TableId(value = "id", type = IdType.AUTO)
private Long id;

private LocalDateTime recordTime;

private Double cpuUsage;

private Double memoryUsed;

private Double memoryTotal;

private Double diskUsed;

private Double diskTotal;

private Long networkRecvSpeed;

private Long networkSentSpeed;

private LocalDateTime createTime;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package cn.programcx.foxnaserver.mapper;

import cn.programcx.foxnaserver.entity.SysMainMetrics;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;

import java.time.LocalDateTime;
import java.util.List;

public interface SysMainMetricsMapper extends BaseMapper<SysMainMetrics> {

/**
* Query metrics within a time range with grouping
* @param startTime Start time
* @param endTime End time
* @param groupSeconds Grouping interval in seconds
* @return List of aggregated metrics
*/
List<SysMainMetrics> selectRecentMetrics(
@Param("startTime") LocalDateTime startTime,
@Param("endTime") LocalDateTime endTime,
@Param("groupSeconds") int groupSeconds
);
}
Loading