mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-03-30 12:12:39 +00:00
* feat: 引入通用 HTTP BodyStorage/DiskCache 缓存配置与管理 - 新增 common/body_storage.go 提供 HTTP 请求体存储抽象和文件缓存能力 - 增加 common/disk_cache_config.go 支持全局磁盘缓存配置 - main.go 挂载缓存初始化流程 - 新增和补充 controller/performance.go (及 unix/windows) 用于缓存性能监控接口 - middleware/body_cleanup.go 自动清理缓存文件 - router 挂载相关接口 - 前端 settings 页面新增性能监控设置 PerformanceSetting - 优化缓存开关状态和模块热插拔能力 - 其他相关文件同步适配缓存扩展 * fix: 修复 BodyStorage 并发安全和错误处理问题 - 修复 diskStorage.Close() 竞态条件,先获取锁再执行 CAS - 为 memoryStorage 添加互斥锁和 closed 状态检查 - 修复 CreateBodyStorageFromReader 在磁盘存储失败时的回退逻辑 - 添加缓存命中统计调用 (IncrementDiskCacheHits/IncrementMemoryCacheHits) - 修复 gin.go 中 Seek 错误被忽略的问题 - 在 api-router 添加 BodyStorageCleanup 中间件 - 修复前端 formatBytes 对异常值的处理 Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
203 lines
4.5 KiB
Go
203 lines
4.5 KiB
Go
package controller
|
||
|
||
import (
|
||
"net/http"
|
||
"os"
|
||
"path/filepath"
|
||
"runtime"
|
||
|
||
"github.com/QuantumNous/new-api/common"
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
// PerformanceStats 性能统计信息
|
||
type PerformanceStats struct {
|
||
// 缓存统计
|
||
CacheStats common.DiskCacheStats `json:"cache_stats"`
|
||
// 系统内存统计
|
||
MemoryStats MemoryStats `json:"memory_stats"`
|
||
// 磁盘缓存目录信息
|
||
DiskCacheInfo DiskCacheInfo `json:"disk_cache_info"`
|
||
// 磁盘空间信息
|
||
DiskSpaceInfo DiskSpaceInfo `json:"disk_space_info"`
|
||
// 配置信息
|
||
Config PerformanceConfig `json:"config"`
|
||
}
|
||
|
||
// MemoryStats 内存统计
|
||
type MemoryStats struct {
|
||
// 已分配内存(字节)
|
||
Alloc uint64 `json:"alloc"`
|
||
// 总分配内存(字节)
|
||
TotalAlloc uint64 `json:"total_alloc"`
|
||
// 系统内存(字节)
|
||
Sys uint64 `json:"sys"`
|
||
// GC 次数
|
||
NumGC uint32 `json:"num_gc"`
|
||
// Goroutine 数量
|
||
NumGoroutine int `json:"num_goroutine"`
|
||
}
|
||
|
||
// DiskCacheInfo 磁盘缓存目录信息
|
||
type DiskCacheInfo struct {
|
||
// 缓存目录路径
|
||
Path string `json:"path"`
|
||
// 目录是否存在
|
||
Exists bool `json:"exists"`
|
||
// 文件数量
|
||
FileCount int `json:"file_count"`
|
||
// 总大小(字节)
|
||
TotalSize int64 `json:"total_size"`
|
||
}
|
||
|
||
// DiskSpaceInfo 磁盘空间信息
|
||
type DiskSpaceInfo struct {
|
||
// 总空间(字节)
|
||
Total uint64 `json:"total"`
|
||
// 可用空间(字节)
|
||
Free uint64 `json:"free"`
|
||
// 已用空间(字节)
|
||
Used uint64 `json:"used"`
|
||
// 使用百分比
|
||
UsedPercent float64 `json:"used_percent"`
|
||
}
|
||
|
||
// PerformanceConfig 性能配置
|
||
type PerformanceConfig struct {
|
||
// 是否启用磁盘缓存
|
||
DiskCacheEnabled bool `json:"disk_cache_enabled"`
|
||
// 磁盘缓存阈值(MB)
|
||
DiskCacheThresholdMB int `json:"disk_cache_threshold_mb"`
|
||
// 磁盘缓存最大大小(MB)
|
||
DiskCacheMaxSizeMB int `json:"disk_cache_max_size_mb"`
|
||
// 磁盘缓存路径
|
||
DiskCachePath string `json:"disk_cache_path"`
|
||
// 是否在容器中运行
|
||
IsRunningInContainer bool `json:"is_running_in_container"`
|
||
}
|
||
|
||
// GetPerformanceStats 获取性能统计信息
|
||
func GetPerformanceStats(c *gin.Context) {
|
||
// 获取缓存统计
|
||
cacheStats := common.GetDiskCacheStats()
|
||
|
||
// 获取内存统计
|
||
var memStats runtime.MemStats
|
||
runtime.ReadMemStats(&memStats)
|
||
|
||
// 获取磁盘缓存目录信息
|
||
diskCacheInfo := getDiskCacheInfo()
|
||
|
||
// 获取配置信息
|
||
diskConfig := common.GetDiskCacheConfig()
|
||
config := PerformanceConfig{
|
||
DiskCacheEnabled: diskConfig.Enabled,
|
||
DiskCacheThresholdMB: diskConfig.ThresholdMB,
|
||
DiskCacheMaxSizeMB: diskConfig.MaxSizeMB,
|
||
DiskCachePath: diskConfig.Path,
|
||
IsRunningInContainer: common.IsRunningInContainer(),
|
||
}
|
||
|
||
// 获取磁盘空间信息
|
||
diskSpaceInfo := getDiskSpaceInfo()
|
||
|
||
stats := PerformanceStats{
|
||
CacheStats: cacheStats,
|
||
MemoryStats: MemoryStats{
|
||
Alloc: memStats.Alloc,
|
||
TotalAlloc: memStats.TotalAlloc,
|
||
Sys: memStats.Sys,
|
||
NumGC: memStats.NumGC,
|
||
NumGoroutine: runtime.NumGoroutine(),
|
||
},
|
||
DiskCacheInfo: diskCacheInfo,
|
||
DiskSpaceInfo: diskSpaceInfo,
|
||
Config: config,
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"data": stats,
|
||
})
|
||
}
|
||
|
||
// ClearDiskCache 清理磁盘缓存
|
||
func ClearDiskCache(c *gin.Context) {
|
||
cachePath := common.GetDiskCachePath()
|
||
if cachePath == "" {
|
||
cachePath = os.TempDir()
|
||
}
|
||
dir := filepath.Join(cachePath, "new-api-body-cache")
|
||
|
||
// 删除缓存目录
|
||
err := os.RemoveAll(dir)
|
||
if err != nil && !os.IsNotExist(err) {
|
||
common.ApiError(c, err)
|
||
return
|
||
}
|
||
|
||
// 重置统计
|
||
common.ResetDiskCacheStats()
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"message": "磁盘缓存已清理",
|
||
})
|
||
}
|
||
|
||
// ResetPerformanceStats 重置性能统计
|
||
func ResetPerformanceStats(c *gin.Context) {
|
||
common.ResetDiskCacheStats()
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"message": "统计信息已重置",
|
||
})
|
||
}
|
||
|
||
// ForceGC 强制执行 GC
|
||
func ForceGC(c *gin.Context) {
|
||
runtime.GC()
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"message": "GC 已执行",
|
||
})
|
||
}
|
||
|
||
// getDiskCacheInfo 获取磁盘缓存目录信息
|
||
func getDiskCacheInfo() DiskCacheInfo {
|
||
cachePath := common.GetDiskCachePath()
|
||
if cachePath == "" {
|
||
cachePath = os.TempDir()
|
||
}
|
||
dir := filepath.Join(cachePath, "new-api-body-cache")
|
||
|
||
info := DiskCacheInfo{
|
||
Path: dir,
|
||
Exists: false,
|
||
}
|
||
|
||
entries, err := os.ReadDir(dir)
|
||
if err != nil {
|
||
return info
|
||
}
|
||
|
||
info.Exists = true
|
||
info.FileCount = 0
|
||
info.TotalSize = 0
|
||
|
||
for _, entry := range entries {
|
||
if entry.IsDir() {
|
||
continue
|
||
}
|
||
info.FileCount++
|
||
if fileInfo, err := entry.Info(); err == nil {
|
||
info.TotalSize += fileInfo.Size()
|
||
}
|
||
}
|
||
|
||
return info
|
||
}
|
||
|