🐛 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:
1 parent
6981148727
commit
d710d382ee
12 files changed
+125
-305
No files matched your search
@@ -9,8 +9,7 @@ import (
|
||||
|
||||
// HandleSearch 搜索处理
|
||||
func HandleSearch(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementSearch()
|
||||
incrementStat("total", "search")
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
query := r.URL.Query().Get("query")
|
||||
@@ -41,8 +40,7 @@ func HandleSearch(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// HandleDetail 详情处理
|
||||
func HandleDetail(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementDetail()
|
||||
incrementStat("total", "detail")
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
bookID := r.URL.Query().Get("book_id")
|
||||
@@ -58,8 +56,7 @@ func HandleDetail(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// HandleCatalog 目录处理
|
||||
func HandleCatalog(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementCatalog()
|
||||
incrementStat("total", "catalog")
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
bookID := r.URL.Query().Get("book_id")
|
||||
|
||||
@@ -8,8 +8,7 @@ import (
|
||||
|
||||
// HandleCategoryFront 分类列表处理
|
||||
func HandleCategoryFront(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementCategory()
|
||||
incrementStat("total", "category")
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
tab := r.URL.Query().Get("tab")
|
||||
@@ -25,8 +24,7 @@ func HandleCategoryFront(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// HandleCategoryLanding 分类详情处理
|
||||
func HandleCategoryLanding(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementCategory()
|
||||
incrementStat("total", "category")
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
categoryID := r.URL.Query().Get("category_id")
|
||||
|
||||
+35
-52
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,7 @@ import (
|
||||
|
||||
// HandleContent 内容处理
|
||||
func HandleContent(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementContent()
|
||||
incrementStat("total", "content")
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
// 从查询参数获取item_id
|
||||
|
||||
@@ -10,8 +10,7 @@ import (
|
||||
|
||||
// HandleAudio 音频处理
|
||||
func HandleAudio(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementAudio()
|
||||
incrementStat("total", "audio")
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
itemIDs := r.URL.Query().Get("item_ids")
|
||||
@@ -29,7 +28,7 @@ func HandleAudio(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// HandleAudioProxy 音频代理处理
|
||||
func HandleAudioProxy(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementStat("total")
|
||||
|
||||
// 从URL路径提取key: /audio/proxy/{key}
|
||||
path := r.URL.Path
|
||||
@@ -60,8 +59,7 @@ func HandleAudioProxy(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// HandleVideo 视频处理
|
||||
func HandleVideo(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementVideo()
|
||||
incrementStat("total", "video")
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
itemIDs := r.URL.Query().Get("item_ids")
|
||||
@@ -77,8 +75,7 @@ func HandleVideo(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// HandleManga 漫画处理
|
||||
func HandleManga(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementManga()
|
||||
incrementStat("total", "manga")
|
||||
itemID := r.URL.Query().Get("item_ids")
|
||||
if itemID == "" {
|
||||
itemID = r.URL.Query().Get("item_id")
|
||||
|
||||
@@ -70,7 +70,7 @@ func handleReviewSuccess(w http.ResponseWriter, data interface{}) {
|
||||
|
||||
// HandleChapterReview 处理章节评论请求(混合数据)
|
||||
func HandleChapterReview(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementStat("total")
|
||||
|
||||
// 验证必需参数
|
||||
params, err := validateParams(r, "item_id")
|
||||
@@ -94,7 +94,7 @@ func HandleChapterReview(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// HandleReview 处理段落评论请求
|
||||
func HandleReview(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementStat("total")
|
||||
|
||||
// 验证必需参数
|
||||
params, err := validateParams(r, "item_id")
|
||||
@@ -141,7 +141,7 @@ func HandleReview(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// HandleReviewHTML 处理段落评论 HTML 显示
|
||||
func HandleReviewHTML(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementStat("total")
|
||||
|
||||
// 验证必需参数
|
||||
params, err := validateParams(r, "item_id", "para")
|
||||
|
||||
+10
-18
@@ -72,15 +72,7 @@ func HandleStatus(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// 获取统计数据
|
||||
total := getTotal()
|
||||
search := getSearch()
|
||||
detail := getDetail()
|
||||
catalog := getCatalog()
|
||||
content := getContent()
|
||||
manga := getManga()
|
||||
audio := getAudio()
|
||||
video := getVideo()
|
||||
category := getCategory()
|
||||
stats := getStatsSnapshot()
|
||||
|
||||
// 获取设备密钥池状态
|
||||
poolStatus := getDeviceKeyPoolStatus()
|
||||
@@ -857,15 +849,15 @@ func HandleStatus(w http.ResponseWriter, r *http.Request) {
|
||||
poolStatus["valid"],
|
||||
poolStatus["invalid"],
|
||||
poolStatus["current_index"],
|
||||
total,
|
||||
search,
|
||||
detail,
|
||||
catalog,
|
||||
content,
|
||||
manga,
|
||||
audio,
|
||||
video,
|
||||
category,
|
||||
stats["total"],
|
||||
stats["search"],
|
||||
stats["detail"],
|
||||
stats["catalog"],
|
||||
stats["content"],
|
||||
stats["manga"],
|
||||
stats["audio"],
|
||||
stats["video"],
|
||||
stats["category"],
|
||||
getDBCacheCount(),
|
||||
getCacheStatus(),
|
||||
getCleanupStatus(),
|
||||
|
||||
Reference in new issue
Block a user