🐛 fix(server): 增加优雅退出与请求超时保护

- 新增 SIGINT/SIGTERM 信号处理与 http.Server.Shutdown,退出前确保统计数据保存、数据库正常关闭
- 为 http.Server 设置 ReadHeaderTimeout/ReadTimeout/WriteTimeout/IdleTimeout,防止慢客户端长期占用连接
- 全局配置指针改为 atomic.Pointer 并提供 GetAppConfig 访问器,消除配置热更新时的数据竞争,同时删除冗余的 manager.config 字段
- 请求统计九组计数器重构为 map[string]*atomic.Int64,统一 incrementStat/getStatsSnapshot 访问,精简约 40 行样板代码
- go mod tidy 清理 go.mod 与 go.sum 中的冗余依赖条目
This commit is contained in:
chenxin committed 2026-09-29 19:47:04 +08:00
1 parent 6981148727
commit d710d382ee
12 files changed
+125 -305

No files matched your search

+35 -52
View File
@@ -13,42 +13,41 @@ import (
var (
startTime = time.Now()
deviceHeader string
// 请求统计
totalRequests int64
searchRequests int64
detailRequests int64
catalogRequests int64
contentRequests int64
mangaRequests int64
audioRequests int64
videoRequests int64
categoryRequests int64
requestStats = newRequestStats()
// 统计保存定时器
statsSaveTicker *time.Ticker
)
// 统计函数
func incrementTotal() { atomic.AddInt64(&totalRequests, 1) }
func incrementSearch() { atomic.AddInt64(&searchRequests, 1) }
func incrementDetail() { atomic.AddInt64(&detailRequests, 1) }
func incrementCatalog() { atomic.AddInt64(&catalogRequests, 1) }
func incrementContent() { atomic.AddInt64(&contentRequests, 1) }
func incrementManga() { atomic.AddInt64(&mangaRequests, 1) }
func incrementAudio() { atomic.AddInt64(&audioRequests, 1) }
func incrementVideo() { atomic.AddInt64(&videoRequests, 1) }
func incrementCategory() { atomic.AddInt64(&categoryRequests, 1) }
// statsKinds 统计类别
var statsKinds = []string{"total", "search", "detail", "catalog", "content", "manga", "audio", "video", "category"}
func getTotal() int64 { return atomic.LoadInt64(&totalRequests) }
func getSearch() int64 { return atomic.LoadInt64(&searchRequests) }
func getDetail() int64 { return atomic.LoadInt64(&detailRequests) }
func getCatalog() int64 { return atomic.LoadInt64(&catalogRequests) }
func getContent() int64 { return atomic.LoadInt64(&contentRequests) }
func getManga() int64 { return atomic.LoadInt64(&mangaRequests) }
func getAudio() int64 { return atomic.LoadInt64(&audioRequests) }
func getVideo() int64 { return atomic.LoadInt64(&videoRequests) }
func getCategory() int64 { return atomic.LoadInt64(&categoryRequests) }
// newRequestStats 初始化各类计数器
func newRequestStats() map[string]*atomic.Int64 {
m := make(map[string]*atomic.Int64, len(statsKinds))
for _, k := range statsKinds {
m[k] = &atomic.Int64{}
}
return m
}
// incrementStat 原子递增请求计数
func incrementStat(kinds ...string) {
for _, k := range kinds {
requestStats[k].Add(1)
}
}
// getStatsSnapshot 获取所有计数器的快照
func getStatsSnapshot() map[string]int64 {
snapshot := make(map[string]int64, len(requestStats))
for k, c := range requestStats {
snapshot[k] = c.Load()
}
return snapshot
}
// SetDeviceHeader 设置设备header
func SetDeviceHeader(header string) {
@@ -63,32 +62,16 @@ func LoadStatistics() {
return
}
atomic.StoreInt64(&totalRequests, stats["total"])
atomic.StoreInt64(&searchRequests, stats["search"])
atomic.StoreInt64(&detailRequests, stats["detail"])
atomic.StoreInt64(&catalogRequests, stats["catalog"])
atomic.StoreInt64(&contentRequests, stats["content"])
atomic.StoreInt64(&mangaRequests, stats["manga"])
atomic.StoreInt64(&audioRequests, stats["audio"])
atomic.StoreInt64(&videoRequests, stats["video"])
atomic.StoreInt64(&categoryRequests, stats["category"])
for k, v := range stats {
if c, ok := requestStats[k]; ok {
c.Store(v)
}
}
}
// SaveStatistics 保存统计数据到数据库
func SaveStatistics() {
stats := map[string]int64{
"total": atomic.LoadInt64(&totalRequests),
"search": atomic.LoadInt64(&searchRequests),
"detail": atomic.LoadInt64(&detailRequests),
"catalog": atomic.LoadInt64(&catalogRequests),
"content": atomic.LoadInt64(&contentRequests),
"manga": atomic.LoadInt64(&mangaRequests),
"audio": atomic.LoadInt64(&audioRequests),
"video": atomic.LoadInt64(&videoRequests),
"category": atomic.LoadInt64(&categoryRequests),
}
if err := fqapi.SaveStatistics(stats); err != nil {
if err := fqapi.SaveStatistics(getStatsSnapshot()); err != nil {
fmt.Printf("保存统计数据失败: %v\n", err)
}
}