🐛 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
+42
-15
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fanqie/internal/algos"
|
||||
"fanqie/internal/common"
|
||||
"fanqie/internal/config"
|
||||
@@ -10,6 +11,9 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -85,15 +89,15 @@ func initConfig() {
|
||||
|
||||
// initAlgorithm 初始化算法
|
||||
func initAlgorithm() {
|
||||
if config.AppConfig != nil {
|
||||
algos.GlobalAlgorithmSwitcher.SwitchAlgorithm(config.AppConfig.Fanqie.Algorithm)
|
||||
logger.Info(fmt.Sprintf("当前算法: %s", config.AppConfig.Fanqie.Algorithm))
|
||||
if config.GetAppConfig() != nil {
|
||||
algos.GlobalAlgorithmSwitcher.SwitchAlgorithm(config.GetAppConfig().Fanqie.Algorithm)
|
||||
logger.Info(fmt.Sprintf("当前算法: %s", config.GetAppConfig().Fanqie.Algorithm))
|
||||
|
||||
algos.GlobalSignatureSwitcher.SwitchSignature(config.AppConfig.Fanqie.Signature)
|
||||
algos.GlobalSignatureSwitcher.SwitchSignature(config.GetAppConfig().Fanqie.Signature)
|
||||
|
||||
// 根据签名方法显示详细信息
|
||||
var signatureDetails string
|
||||
if config.AppConfig.Fanqie.Signature == "sign4" {
|
||||
if config.GetAppConfig().Fanqie.Signature == "sign4" {
|
||||
signatureDetails = "X-Gorgon + X-Ladon + X-Argus + X-Khronos"
|
||||
} else {
|
||||
signatureDetails = "X-Gorgon + X-Khronos"
|
||||
@@ -159,22 +163,45 @@ func startServer() {
|
||||
host := "0.0.0.0"
|
||||
port := "40180"
|
||||
|
||||
if config.AppConfig != nil {
|
||||
if config.AppConfig.Server.Host != "" {
|
||||
host = config.AppConfig.Server.Host
|
||||
if config.GetAppConfig() != nil {
|
||||
if config.GetAppConfig().Server.Host != "" {
|
||||
host = config.GetAppConfig().Server.Host
|
||||
}
|
||||
if config.AppConfig.Server.Port != "" {
|
||||
port = config.AppConfig.Server.Port
|
||||
if config.GetAppConfig().Server.Port != "" {
|
||||
port = config.GetAppConfig().Server.Port
|
||||
}
|
||||
}
|
||||
|
||||
addr := host + ":" + port
|
||||
logger.Info(fmt.Sprintf("服务器启动在 %s", addr))
|
||||
|
||||
if err := http.ListenAndServe(addr, nil); err != nil {
|
||||
logger.Error(fmt.Sprintf("服务器启动失败: %v", err))
|
||||
os.Exit(1)
|
||||
server := &http.Server{
|
||||
Addr: addr,
|
||||
// 超时保护:防止慢客户端长期占用连接
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
ReadTimeout: 60 * time.Second,
|
||||
WriteTimeout: 10 * time.Minute, // 代理响应整体缓冲后写出,慢客户端下载大音频/图片需要余量
|
||||
IdleTimeout: 120 * time.Second,
|
||||
}
|
||||
|
||||
go func() {
|
||||
logger.Info(fmt.Sprintf("服务器启动在 %s", addr))
|
||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
logger.Error(fmt.Sprintf("服务器启动失败: %v", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
// 等待退出信号,优雅关闭,让 main 中的 defer(保存统计、关闭数据库)得以执行
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
|
||||
<-sigCh
|
||||
logger.Info("收到退出信号,正在关闭服务器...")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
if err := server.Shutdown(ctx); err != nil {
|
||||
logger.Warning(fmt.Sprintf("服务器关闭超时(30秒内未完成的连接被强制断开): %v", err))
|
||||
}
|
||||
logger.Info("服务器已关闭")
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
Reference in new issue
Block a user