Procházet zdrojové kódy

Merge pull request #2835 from QuantumNous/feat/performance-monitoring

feat(performance): implement system performance monitoring
Calcium-Ion před 1 týdnem
rodič
revize
65b2ca4176

+ 33 - 0
common/performance_config.go

@@ -0,0 +1,33 @@
+package common
+
+import "sync/atomic"
+
+// PerformanceMonitorConfig 性能监控配置
+type PerformanceMonitorConfig struct {
+	Enabled         bool
+	CPUThreshold    int
+	MemoryThreshold int
+	DiskThreshold   int
+}
+
+var performanceMonitorConfig atomic.Value
+
+func init() {
+	// 初始化默认配置
+	performanceMonitorConfig.Store(PerformanceMonitorConfig{
+		Enabled:         true,
+		CPUThreshold:    90,
+		MemoryThreshold: 90,
+		DiskThreshold:   90,
+	})
+}
+
+// GetPerformanceMonitorConfig 获取性能监控配置
+func GetPerformanceMonitorConfig() PerformanceMonitorConfig {
+	return performanceMonitorConfig.Load().(PerformanceMonitorConfig)
+}
+
+// SetPerformanceMonitorConfig 设置性能监控配置
+func SetPerformanceMonitorConfig(config PerformanceMonitorConfig) {
+	performanceMonitorConfig.Store(config)
+}

+ 81 - 0
common/system_monitor.go

@@ -0,0 +1,81 @@
+package common
+
+import (
+	"sync/atomic"
+	"time"
+
+	"github.com/shirou/gopsutil/cpu"
+	"github.com/shirou/gopsutil/mem"
+)
+
+// DiskSpaceInfo 磁盘空间信息
+type DiskSpaceInfo struct {
+	// 总空间(字节)
+	Total uint64 `json:"total"`
+	// 可用空间(字节)
+	Free uint64 `json:"free"`
+	// 已用空间(字节)
+	Used uint64 `json:"used"`
+	// 使用百分比
+	UsedPercent float64 `json:"used_percent"`
+}
+
+// SystemStatus 系统状态信息
+type SystemStatus struct {
+	CPUUsage    float64
+	MemoryUsage float64
+	DiskUsage   float64
+}
+
+var latestSystemStatus atomic.Value
+
+func init() {
+	latestSystemStatus.Store(SystemStatus{})
+}
+
+// StartSystemMonitor 启动系统监控
+func StartSystemMonitor() {
+	go func() {
+		for {
+			config := GetPerformanceMonitorConfig()
+			if !config.Enabled {
+				time.Sleep(30 * time.Second)
+				continue
+			}
+
+			updateSystemStatus()
+			time.Sleep(5 * time.Second)
+		}
+	}()
+}
+
+func updateSystemStatus() {
+	var status SystemStatus
+
+	// CPU
+	// 注意:cpu.Percent(0, false) 返回自上次调用以来的 CPU 使用率
+	// 如果是第一次调用,可能会返回错误或不准确的值,但在循环中会逐渐正常
+	percents, err := cpu.Percent(0, false)
+	if err == nil && len(percents) > 0 {
+		status.CPUUsage = percents[0]
+	}
+
+	// Memory
+	memInfo, err := mem.VirtualMemory()
+	if err == nil {
+		status.MemoryUsage = memInfo.UsedPercent
+	}
+
+	// Disk
+	diskInfo := GetDiskSpaceInfo()
+	if diskInfo.Total > 0 {
+		status.DiskUsage = diskInfo.UsedPercent
+	}
+
+	latestSystemStatus.Store(status)
+}
+
+// GetSystemStatus 获取当前系统状态
+func GetSystemStatus() SystemStatus {
+	return latestSystemStatus.Load().(SystemStatus)
+}

+ 4 - 5
controller/performance_unix.go → common/system_monitor_unix.go

@@ -1,17 +1,16 @@
 //go:build !windows
 
-package controller
+package common
 
 import (
 	"os"
 
-	"github.com/QuantumNous/new-api/common"
 	"golang.org/x/sys/unix"
 )
 
-// getDiskSpaceInfo 获取缓存目录所在磁盘的空间信息 (Unix/Linux/macOS)
-func getDiskSpaceInfo() DiskSpaceInfo {
-	cachePath := common.GetDiskCachePath()
+// GetDiskSpaceInfo 获取缓存目录所在磁盘的空间信息 (Unix/Linux/macOS)
+func GetDiskSpaceInfo() DiskSpaceInfo {
+	cachePath := GetDiskCachePath()
 	if cachePath == "" {
 		cachePath = os.TempDir()
 	}

+ 4 - 6
controller/performance_windows.go → common/system_monitor_windows.go

@@ -1,18 +1,16 @@
 //go:build windows
 
-package controller
+package common
 
 import (
 	"os"
 	"syscall"
 	"unsafe"
-
-	"github.com/QuantumNous/new-api/common"
 )
 
-// getDiskSpaceInfo 获取缓存目录所在磁盘的空间信息 (Windows)
-func getDiskSpaceInfo() DiskSpaceInfo {
-	cachePath := common.GetDiskCachePath()
+// GetDiskSpaceInfo 获取缓存目录所在磁盘的空间信息 (Windows)
+func GetDiskSpaceInfo() DiskSpaceInfo {
+	cachePath := GetDiskCachePath()
 	if cachePath == "" {
 		cachePath = os.TempDir()
 	}

+ 30 - 19
controller/performance.go

@@ -19,7 +19,7 @@ type PerformanceStats struct {
 	// 磁盘缓存目录信息
 	DiskCacheInfo DiskCacheInfo `json:"disk_cache_info"`
 	// 磁盘空间信息
-	DiskSpaceInfo DiskSpaceInfo `json:"disk_space_info"`
+	DiskSpaceInfo common.DiskSpaceInfo `json:"disk_space_info"`
 	// 配置信息
 	Config PerformanceConfig `json:"config"`
 }
@@ -50,18 +50,6 @@ type DiskCacheInfo struct {
 	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 {
 	// 是否启用磁盘缓存
@@ -74,6 +62,15 @@ type PerformanceConfig struct {
 	DiskCachePath string `json:"disk_cache_path"`
 	// 是否在容器中运行
 	IsRunningInContainer bool `json:"is_running_in_container"`
+
+	// MonitorEnabled 是否启用性能监控
+	MonitorEnabled bool `json:"monitor_enabled"`
+	// MonitorCPUThreshold CPU 使用率阈值(%)
+	MonitorCPUThreshold int `json:"monitor_cpu_threshold"`
+	// MonitorMemoryThreshold 内存使用率阈值(%)
+	MonitorMemoryThreshold int `json:"monitor_memory_threshold"`
+	// MonitorDiskThreshold 磁盘使用率阈值(%)
+	MonitorDiskThreshold int `json:"monitor_disk_threshold"`
 }
 
 // GetPerformanceStats 获取性能统计信息
@@ -91,16 +88,30 @@ func GetPerformanceStats(c *gin.Context) {
 
 	// 获取配置信息
 	diskConfig := common.GetDiskCacheConfig()
+	monitorConfig := common.GetPerformanceMonitorConfig()
 	config := PerformanceConfig{
-		DiskCacheEnabled:     diskConfig.Enabled,
-		DiskCacheThresholdMB: diskConfig.ThresholdMB,
-		DiskCacheMaxSizeMB:   diskConfig.MaxSizeMB,
-		DiskCachePath:        diskConfig.Path,
-		IsRunningInContainer: common.IsRunningInContainer(),
+		DiskCacheEnabled:       diskConfig.Enabled,
+		DiskCacheThresholdMB:   diskConfig.ThresholdMB,
+		DiskCacheMaxSizeMB:     diskConfig.MaxSizeMB,
+		DiskCachePath:          diskConfig.Path,
+		IsRunningInContainer:   common.IsRunningInContainer(),
+		MonitorEnabled:         monitorConfig.Enabled,
+		MonitorCPUThreshold:    monitorConfig.CPUThreshold,
+		MonitorMemoryThreshold: monitorConfig.MemoryThreshold,
+		MonitorDiskThreshold:   monitorConfig.DiskThreshold,
 	}
 
 	// 获取磁盘空间信息
-	diskSpaceInfo := getDiskSpaceInfo()
+	// 使用缓存的系统状态,避免频繁调用系统 API
+	systemStatus := common.GetSystemStatus()
+	diskSpaceInfo := common.DiskSpaceInfo{
+		UsedPercent: systemStatus.DiskUsage,
+	}
+	// 如果需要详细信息,可以按需获取,或者扩展 SystemStatus
+	// 这里为了保持接口兼容性,我们仍然调用 GetDiskSpaceInfo,但注意这可能会有性能开销
+	// 考虑到 GetPerformanceStats 是管理接口,频率较低,直接调用是可以接受的
+	// 但为了一致性,我们也可以考虑从 SystemStatus 中获取部分信息
+	diskSpaceInfo = common.GetDiskSpaceInfo()
 
 	stats := PerformanceStats{
 		CacheStats: cacheStats,

+ 4 - 0
main.go

@@ -274,5 +274,9 @@ func InitResources() error {
 	if err != nil {
 		return err
 	}
+
+	// 启动系统监控
+	common.StartSystemMonitor()
+
 	return nil
 }

+ 65 - 0
middleware/performance.go

@@ -0,0 +1,65 @@
+package middleware
+
+import (
+	"errors"
+	"net/http"
+	"strings"
+
+	"github.com/QuantumNous/new-api/common"
+	"github.com/QuantumNous/new-api/types"
+	"github.com/gin-gonic/gin"
+)
+
+// SystemPerformanceCheck 检查系统性能中间件
+func SystemPerformanceCheck() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		// 仅检查 Relay 接口 (/v1, /v1beta 等)
+		// 这里简单判断路径前缀,可以根据实际路由调整
+		path := c.Request.URL.Path
+		if strings.HasPrefix(path, "/v1/messages") {
+			if err := checkSystemPerformance(); err != nil {
+				c.JSON(err.StatusCode, gin.H{
+					"error": err.ToClaudeError(),
+				})
+				c.Abort()
+				return
+			}
+		} else {
+			if err := checkSystemPerformance(); err != nil {
+				c.JSON(err.StatusCode, gin.H{
+					"error": err.ToOpenAIError(),
+				})
+				c.Abort()
+				return
+			}
+		}
+		c.Next()
+	}
+}
+
+// checkSystemPerformance 检查系统性能是否超过阈值
+func checkSystemPerformance() *types.NewAPIError {
+	config := common.GetPerformanceMonitorConfig()
+	if !config.Enabled {
+		return nil
+	}
+
+	status := common.GetSystemStatus()
+
+	// 检查 CPU
+	if config.CPUThreshold > 0 && int(status.CPUUsage) > config.CPUThreshold {
+		return types.NewErrorWithStatusCode(errors.New("system cpu overloaded"), "system_cpu_overloaded", http.StatusServiceUnavailable)
+	}
+
+	// 检查内存
+	if config.MemoryThreshold > 0 && int(status.MemoryUsage) > config.MemoryThreshold {
+		return types.NewErrorWithStatusCode(errors.New("system memory overloaded"), "system_memory_overloaded", http.StatusServiceUnavailable)
+	}
+
+	// 检查磁盘
+	if config.DiskThreshold > 0 && int(status.DiskUsage) > config.DiskThreshold {
+		return types.NewErrorWithStatusCode(errors.New("system disk overloaded"), "system_disk_overloaded", http.StatusServiceUnavailable)
+	}
+
+	return nil
+}

+ 6 - 0
router/relay-router.go

@@ -57,11 +57,13 @@ func SetRelayRouter(router *gin.Engine) {
 	}
 
 	playgroundRouter := router.Group("/pg")
+	playgroundRouter.Use(middleware.SystemPerformanceCheck())
 	playgroundRouter.Use(middleware.UserAuth(), middleware.Distribute())
 	{
 		playgroundRouter.POST("/chat/completions", controller.Playground)
 	}
 	relayV1Router := router.Group("/v1")
+	relayV1Router.Use(middleware.SystemPerformanceCheck())
 	relayV1Router.Use(middleware.TokenAuth())
 	relayV1Router.Use(middleware.ModelRequestRateLimit())
 	{
@@ -159,13 +161,16 @@ func SetRelayRouter(router *gin.Engine) {
 	}
 
 	relayMjRouter := router.Group("/mj")
+	relayMjRouter.Use(middleware.SystemPerformanceCheck())
 	registerMjRouterGroup(relayMjRouter)
 
 	relayMjModeRouter := router.Group("/:mode/mj")
+	relayMjModeRouter.Use(middleware.SystemPerformanceCheck())
 	registerMjRouterGroup(relayMjModeRouter)
 	//relayMjRouter.Use()
 
 	relaySunoRouter := router.Group("/suno")
+	relaySunoRouter.Use(middleware.SystemPerformanceCheck())
 	relaySunoRouter.Use(middleware.TokenAuth(), middleware.Distribute())
 	{
 		relaySunoRouter.POST("/submit/:action", controller.RelayTask)
@@ -174,6 +179,7 @@ func SetRelayRouter(router *gin.Engine) {
 	}
 
 	relayGeminiRouter := router.Group("/v1beta")
+	relayGeminiRouter.Use(middleware.SystemPerformanceCheck())
 	relayGeminiRouter.Use(middleware.TokenAuth())
 	relayGeminiRouter.Use(middleware.ModelRequestRateLimit())
 	relayGeminiRouter.Use(middleware.Distribute())

+ 21 - 0
setting/performance_setting/config.go

@@ -15,6 +15,15 @@ type PerformanceSetting struct {
 	DiskCacheMaxSizeMB int `json:"disk_cache_max_size_mb"`
 	// DiskCachePath 磁盘缓存目录
 	DiskCachePath string `json:"disk_cache_path"`
+
+	// MonitorEnabled 是否启用性能监控
+	MonitorEnabled bool `json:"monitor_enabled"`
+	// MonitorCPUThreshold CPU 使用率阈值(%)
+	MonitorCPUThreshold int `json:"monitor_cpu_threshold"`
+	// MonitorMemoryThreshold 内存使用率阈值(%)
+	MonitorMemoryThreshold int `json:"monitor_memory_threshold"`
+	// MonitorDiskThreshold 磁盘使用率阈值(%)
+	MonitorDiskThreshold int `json:"monitor_disk_threshold"`
 }
 
 // 默认配置
@@ -23,6 +32,11 @@ var performanceSetting = PerformanceSetting{
 	DiskCacheThresholdMB: 10,   // 超过 10MB 使用磁盘缓存
 	DiskCacheMaxSizeMB:   1024, // 最大 1GB 磁盘缓存
 	DiskCachePath:        "",   // 空表示使用系统临时目录
+
+	MonitorEnabled:         true,
+	MonitorCPUThreshold:    90,
+	MonitorMemoryThreshold: 90,
+	MonitorDiskThreshold:   90,
 }
 
 func init() {
@@ -40,6 +54,13 @@ func syncToCommon() {
 		MaxSizeMB:   performanceSetting.DiskCacheMaxSizeMB,
 		Path:        performanceSetting.DiskCachePath,
 	})
+
+	common.SetPerformanceMonitorConfig(common.PerformanceMonitorConfig{
+		Enabled:         performanceSetting.MonitorEnabled,
+		CPUThreshold:    performanceSetting.MonitorCPUThreshold,
+		MemoryThreshold: performanceSetting.MonitorMemoryThreshold,
+		DiskThreshold:   performanceSetting.MonitorDiskThreshold,
+	})
 }
 
 // GetPerformanceSetting 获取性能设置

+ 80 - 1
web/src/i18n/locales/en.json

@@ -2316,6 +2316,45 @@
     "输入验证码完成设置": "Enter verification code to complete setup",
     "输出": "Output",
     "输出 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}": "Output {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}}",
+    "磁盘缓存设置(磁盘换内存)": "Disk Cache Settings (Disk Swap Memory)",
+    "启用磁盘缓存后,大请求体将临时存储到磁盘而非内存,可显著降低内存占用,适用于处理包含大量图片/文件的请求。建议在 SSD 环境下使用。": "When enabled, large request bodies are temporarily stored on disk instead of memory, significantly reducing memory usage. Suitable for requests with large images/files. SSD recommended.",
+    "启用磁盘缓存": "Enable Disk Cache",
+    "将大请求体临时存储到磁盘": "Store large request bodies temporarily on disk",
+    "磁盘缓存阈值 (MB)": "Disk Cache Threshold (MB)",
+    "请求体超过此大小时使用磁盘缓存": "Use disk cache when request body exceeds this size",
+    "磁盘缓存最大总量 (MB)": "Max Disk Cache Size (MB)",
+    "可用空间: {{free}} / 总空间: {{total}}": "Free: {{free}} / Total: {{total}}",
+    "磁盘缓存占用的最大空间": "Maximum space occupied by disk cache",
+    "留空使用系统临时目录": "Leave empty to use system temp directory",
+    "例如 /var/cache/new-api": "e.g. /var/cache/new-api",
+    "性能监控": "Performance Monitor",
+    "刷新统计": "Refresh Stats",
+    "重置统计": "Reset Stats",
+    "执行 GC": "Run GC",
+    "请求体磁盘缓存": "Request Body Disk Cache",
+    "活跃文件": "Active Files",
+    "磁盘命中": "Disk Hits",
+    "请求体内存缓存": "Request Body Memory Cache",
+    "当前缓存大小": "Current Cache Size",
+    "活跃缓存数": "Active Cache Count",
+    "内存命中": "Memory Hits",
+    "缓存目录磁盘空间": "Cache Directory Disk Space",
+    "磁盘可用空间小于缓存最大总量设置": "Disk free space is less than max cache size setting",
+    "已分配内存": "Allocated Memory",
+    "总分配内存": "Total Allocated Memory",
+    "系统内存": "System Memory",
+    "GC 次数": "GC Count",
+    "Goroutine 数": "Goroutine Count",
+    "目录文件数": "Directory File Count",
+    "目录总大小": "Directory Total Size",
+    "磁盘缓存已清理": "Disk cache cleared",
+    "清理失败": "Cleanup failed",
+    "统计已重置": "Statistics reset",
+    "重置失败": "Reset failed",
+    "GC 已执行": "GC executed",
+    "GC 执行失败": "GC execution failed",
+    "缓存目录": "Cache Directory",
+    "可用": "Available",
     "输出价格": "Output Price",
     "输出价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "Output price: {{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (Completion ratio: {{completionRatio}})",
     "输出倍率 {{completionRatio}}": "Output ratio {{completionRatio}}",
@@ -2687,6 +2726,46 @@
     "套餐名称": "Plan Name",
     "应付金额": "Amount Due",
     "支付": "Pay",
-    "管理员未开启在线支付功能,请联系管理员配置。": "Online payment is not enabled by the admin. Please contact the administrator."
+    "管理员未开启在线支付功能,请联系管理员配置。": "Online payment is not enabled by the admin. Please contact the administrator.",
+    "磁盘缓存设置(磁盘换内存)": "Disk Cache Settings (Disk Swap Memory)",
+    "启用磁盘缓存后,大请求体将临时存储到磁盘而非内存,可显著降低内存占用,适用于处理包含大量图片/文件的请求。建议在 SSD 环境下使用。": "When enabled, large request bodies are temporarily stored on disk instead of memory, significantly reducing memory usage. Suitable for requests with large images/files. SSD recommended.",
+    "启用磁盘缓存": "Enable Disk Cache",
+    "将大请求体临时存储到磁盘": "Store large request bodies temporarily on disk",
+    "磁盘缓存阈值 (MB)": "Disk Cache Threshold (MB)",
+    "请求体超过此大小时使用磁盘缓存": "Use disk cache when request body exceeds this size",
+    "磁盘缓存最大总量 (MB)": "Max Disk Cache Size (MB)",
+    "可用空间: {{free}} / 总空间: {{total}}": "Free: {{free}} / Total: {{total}}",
+    "磁盘缓存占用的最大空间": "Maximum space occupied by disk cache",
+    "留空使用系统临时目录": "Leave empty to use system temp directory",
+    "例如 /var/cache/new-api": "e.g. /var/cache/new-api",
+    "性能监控": "Performance Monitor",
+    "刷新统计": "Refresh Stats",
+    "重置统计": "Reset Stats",
+    "执行 GC": "Run GC",
+    "请求体磁盘缓存": "Request Body Disk Cache",
+    "活跃文件": "Active Files",
+    "磁盘命中": "Disk Hits",
+    "请求体内存缓存": "Request Body Memory Cache",
+    "当前缓存大小": "Current Cache Size",
+    "活跃缓存数": "Active Cache Count",
+    "内存命中": "Memory Hits",
+    "缓存目录磁盘空间": "Cache Directory Disk Space",
+    "磁盘可用空间小于缓存最大总量设置": "Disk free space is less than max cache size setting",
+    "已分配内存": "Allocated Memory",
+    "总分配内存": "Total Allocated Memory",
+    "系统内存": "System Memory",
+    "GC 次数": "GC Count",
+    "Goroutine 数": "Goroutine Count",
+    "目录文件数": "Directory File Count",
+    "目录总大小": "Directory Total Size",
+    "磁盘缓存已清理": "Disk cache cleared",
+    "清理失败": "Cleanup failed",
+    "统计已重置": "Statistics reset",
+    "重置失败": "Reset failed",
+    "GC 已执行": "GC executed",
+    "GC execution failed": "GC execution failed",
+    "Cache Directory": "Cache Directory",
+    "Available": "Available",
+    "输出价格": "Output Price"
   }
 }

+ 50 - 0
web/src/i18n/locales/zh.json

@@ -1823,6 +1823,17 @@
     "系统文档和帮助信息": "系统文档和帮助信息",
     "系统消息": "系统消息",
     "系统管理功能": "系统管理功能",
+    "系统性能监控": "系统性能监控",
+    "启用性能监控后,当系统资源使用率超过设定阈值时,将拒绝新的 Relay 请求 (/v1, /v1beta 等),以保护系统稳定性。": "启用性能监控后,当系统资源使用率超过设定阈值时,将拒绝新的 Relay 请求 (/v1, /v1beta 等),以保护系统稳定性。",
+    "启用性能监控": "启用性能监控",
+    "超过阈值时拒绝新请求": "超过阈值时拒绝新请求",
+    "CPU 阈值 (%)": "CPU 阈值 (%)",
+    "CPU 使用率超过此值时拒绝请求": "CPU 使用率超过此值时拒绝请求",
+    "内存 阈值 (%)": "内存 阈值 (%)",
+    "内存使用率超过此值时拒绝请求": "内存使用率超过此值时拒绝请求",
+    "磁盘 阈值 (%)": "磁盘 阈值 (%)",
+    "磁盘使用率超过此值时拒绝请求": "磁盘使用率超过此值时拒绝请求",
+    "保存性能设置": "保存性能设置",
     "系统设置": "系统设置",
     "系统访问令牌": "系统访问令牌",
     "约": "约",
@@ -2305,6 +2316,45 @@
     "输入验证码完成设置": "输入验证码完成设置",
     "输出": "输出",
     "输出 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}": "输出 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}",
+    "磁盘缓存设置(磁盘换内存)": "磁盘缓存设置(磁盘换内存)",
+    "启用磁盘缓存后,大请求体将临时存储到磁盘而非内存,可显著降低内存占用,适用于处理包含大量图片/文件的请求。建议在 SSD 环境下使用。": "启用磁盘缓存后,大请求体将临时存储到磁盘而非内存,可显著降低内存占用,适用于处理包含大量图片/文件的请求。建议在 SSD 环境下使用。",
+    "启用磁盘缓存": "启用磁盘缓存",
+    "将大请求体临时存储到磁盘": "将大请求体临时存储到磁盘",
+    "磁盘缓存阈值 (MB)": "磁盘缓存阈值 (MB)",
+    "请求体超过此大小时使用磁盘缓存": "请求体超过此大小时使用磁盘缓存",
+    "磁盘缓存最大总量 (MB)": "磁盘缓存最大总量 (MB)",
+    "可用空间: {{free}} / 总空间: {{total}}": "可用空间: {{free}} / 总空间: {{total}}",
+    "磁盘缓存占用的最大空间": "磁盘缓存占用的最大空间",
+    "留空使用系统临时目录": "留空使用系统临时目录",
+    "例如 /var/cache/new-api": "例如 /var/cache/new-api",
+    "性能监控": "性能监控",
+    "刷新统计": "刷新统计",
+    "重置统计": "重置统计",
+    "执行 GC": "执行 GC",
+    "请求体磁盘缓存": "请求体磁盘缓存",
+    "活跃文件": "活跃文件",
+    "磁盘命中": "磁盘命中",
+    "请求体内存缓存": "请求体内存缓存",
+    "当前缓存大小": "当前缓存大小",
+    "活跃缓存数": "活跃缓存数",
+    "内存命中": "内存命中",
+    "缓存目录磁盘空间": "缓存目录磁盘空间",
+    "磁盘可用空间小于缓存最大总量设置": "磁盘可用空间小于缓存最大总量设置",
+    "已分配内存": "已分配内存",
+    "总分配内存": "总分配内存",
+    "系统内存": "系统内存",
+    "GC 次数": "GC 次数",
+    "Goroutine 数": "Goroutine 数",
+    "目录文件数": "目录文件数",
+    "目录总大小": "目录总大小",
+    "磁盘缓存已清理": "磁盘缓存已清理",
+    "清理失败": "清理失败",
+    "统计已重置": "统计已重置",
+    "重置失败": "重置失败",
+    "GC 已执行": "GC 已执行",
+    "GC 执行失败": "GC 执行失败",
+    "缓存目录": "缓存目录",
+    "可用": "可用",
     "输出价格": "输出价格",
     "输出价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "输出价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})",
     "输出倍率 {{completionRatio}}": "输出倍率 {{completionRatio}}",

+ 72 - 1
web/src/pages/Setting/Performance/SettingsPerformance.jsx

@@ -65,6 +65,10 @@ export default function SettingsPerformance(props) {
     'performance_setting.disk_cache_threshold_mb': 10,
     'performance_setting.disk_cache_max_size_mb': 1024,
     'performance_setting.disk_cache_path': '',
+    'performance_setting.monitor_enabled': false,
+    'performance_setting.monitor_cpu_threshold': 90,
+    'performance_setting.monitor_memory_threshold': 90,
+    'performance_setting.monitor_disk_threshold': 90,
   });
   const refForm = useRef();
   const [inputsRow, setInputsRow] = useState(inputs);
@@ -274,6 +278,70 @@ export default function SettingsPerformance(props) {
                 </Col>
               )}
             </Row>
+          </Form.Section>
+
+          <Form.Section text={t('系统性能监控')}>
+            <Banner
+              type='info'
+              description={t(
+                '启用性能监控后,当系统资源使用率超过设定阈值时,将拒绝新的 Relay 请求 (/v1, /v1beta 等),以保护系统稳定性。',
+              )}
+              style={{ marginBottom: 16 }}
+            />
+            <Row gutter={16}>
+              <Col xs={24} sm={12} md={6} lg={6} xl={6}>
+                <Form.Switch
+                  field={'performance_setting.monitor_enabled'}
+                  label={t('启用性能监控')}
+                  extraText={t('超过阈值时拒绝新请求')}
+                  size='default'
+                  checkedText='|'
+                  uncheckedText='〇'
+                  onChange={handleFieldChange(
+                    'performance_setting.monitor_enabled',
+                  )}
+                />
+              </Col>
+              <Col xs={24} sm={12} md={6} lg={6} xl={6}>
+                <Form.InputNumber
+                  field={'performance_setting.monitor_cpu_threshold'}
+                  label={t('CPU 阈值 (%)')}
+                  extraText={t('CPU 使用率超过此值时拒绝请求')}
+                  min={0}
+                  max={100}
+                  onChange={handleFieldChange(
+                    'performance_setting.monitor_cpu_threshold',
+                  )}
+                  disabled={!inputs['performance_setting.monitor_enabled']}
+                />
+              </Col>
+              <Col xs={24} sm={12} md={6} lg={6} xl={6}>
+                <Form.InputNumber
+                  field={'performance_setting.monitor_memory_threshold'}
+                  label={t('内存 阈值 (%)')}
+                  extraText={t('内存使用率超过此值时拒绝请求')}
+                  min={0}
+                  max={100}
+                  onChange={handleFieldChange(
+                    'performance_setting.monitor_memory_threshold',
+                  )}
+                  disabled={!inputs['performance_setting.monitor_enabled']}
+                />
+              </Col>
+              <Col xs={24} sm={12} md={6} lg={6} xl={6}>
+                <Form.InputNumber
+                  field={'performance_setting.monitor_disk_threshold'}
+                  label={t('磁盘 阈值 (%)')}
+                  extraText={t('磁盘使用率超过此值时拒绝请求')}
+                  min={0}
+                  max={100}
+                  onChange={handleFieldChange(
+                    'performance_setting.monitor_disk_threshold',
+                  )}
+                  disabled={!inputs['performance_setting.monitor_enabled']}
+                />
+              </Col>
+            </Row>
             <Row>
               <Button size='default' onClick={onSubmit}>
                 {t('保存性能设置')}
@@ -490,7 +558,10 @@ export default function SettingsPerformance(props) {
                         key: t('Goroutine 数'),
                         value: stats.memory_stats.num_goroutine,
                       },
-                      { key: t('缓存目录'), value: stats.disk_cache_info.path },
+                      {
+                        key: t('缓存目录'),
+                        value: stats.disk_cache_info.path,
+                      },
                       {
                         key: t('目录文件数'),
                         value: stats.disk_cache_info.file_count,