fix: register shutdown signals before startup

This commit is contained in:
MiMoCode
2026-07-10 19:45:53 +08:00
parent f8abacf2e5
commit fcb4a87be8
2 changed files with 64 additions and 3 deletions
+4 -3
View File
@@ -83,6 +83,10 @@ func run() error {
} }
log.Printf("[main] filter mode=%s patterns=%d", filter.Mode, len(filter.Patterns)) log.Printf("[main] filter mode=%s patterns=%d", filter.Mode, len(filter.Patterns))
stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(stop)
rootCtx, cancel := context.WithCancel(context.Background()) rootCtx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@@ -114,10 +118,7 @@ func run() error {
serverErr <- srv.ListenAndServe() serverErr <- srv.ListenAndServe()
}() }()
stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
runErr := waitForShutdown(stop, serverErr) runErr := waitForShutdown(stop, serverErr)
signal.Stop(stop)
// HTTP、升级连接、日志排空和数据库共享同一个关闭总预算。 // HTTP、升级连接、日志排空和数据库共享同一个关闭总预算。
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second) shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
+60
View File
@@ -3,6 +3,9 @@ package main
import ( import (
"context" "context"
"errors" "errors"
"go/ast"
"go/parser"
"go/token"
"net/http" "net/http"
"os" "os"
"reflect" "reflect"
@@ -11,6 +14,63 @@ import (
"time" "time"
) )
func TestRunRegistersSignalsBeforeStartingResources(t *testing.T) {
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "main.go", nil, 0)
if err != nil {
t.Fatalf("parse main.go: %v", err)
}
var runBody *ast.BlockStmt
for _, declaration := range file.Decls {
function, ok := declaration.(*ast.FuncDecl)
if ok && function.Name.Name == "run" {
runBody = function.Body
break
}
}
if runBody == nil {
t.Fatal("main.go does not define run")
}
positions := make(map[string]token.Pos)
ast.Inspect(runBody, func(node ast.Node) bool {
call, ok := node.(*ast.CallExpr)
if !ok {
return true
}
selector, ok := call.Fun.(*ast.SelectorExpr)
if !ok {
return true
}
owner, ok := selector.X.(*ast.Ident)
if !ok {
return true
}
name := owner.Name + "." + selector.Sel.Name
switch name {
case "signal.Notify", "db.NewPool", "queue.Start", "srv.ListenAndServe":
positions[name] = call.Pos()
}
return true
})
notifyPos, ok := positions["signal.Notify"]
if !ok {
t.Fatal("run does not register for shutdown signals")
}
for _, start := range []string{"db.NewPool", "queue.Start", "srv.ListenAndServe"} {
startPos, ok := positions[start]
if !ok {
t.Fatalf("run does not call %s", start)
}
if notifyPos >= startPos {
t.Errorf("signal.Notify at line %d must precede %s at line %d",
fset.Position(notifyPos).Line, start, fset.Position(startPos).Line)
}
}
}
func TestWaitForShutdownReturnsListenerError(t *testing.T) { func TestWaitForShutdownReturnsListenerError(t *testing.T) {
listenErr := errors.New("listen failed") listenErr := errors.New("listen failed")
serverErr := make(chan error, 1) serverErr := make(chan error, 1)