Golang中如何在SIGINT信号前延迟函数调用
Golang中如何在SIGINT信号前延迟函数调用 我的主函数大致如下:
func main() {
dpPool := &DatabasePool{db: connectDb()}
defer dpPool.db.Close()
ctx := context.Background()
dpPool.setUpTables(ctx)
defer dpPool.dropTables(ctx)
mux := http.NewServeMux()
mux.HandleFunc("/", getRoot)
mux.HandleFunc("/hello", getHello)
mux.HandleFunc("/ping", ping)
server := &http.Server{
Addr: "<address>",
Handler: mux,
BaseContext: func(l net.Listener) context.Context {
ctx = context.WithValue(ctx, keyServerAddr, l.Addr().String())
return ctx
},
}
err := server.ListenAndServe()
if errors.Is(err, http.ErrServerClosed) {
fmt.Printf("server closed\n")
} else if err != nil {
fmt.Printf("error listening for server: %s\n", err)
}
}
我正在尝试编写一个Web服务器,并希望在关闭服务器时关闭数据库连接池并删除表。我通常通过按 Ctrl + C 来关闭,这会向程序发送 SIGINT 信号。但表并没有被删除,所以我认为这与 defer 有关。任何帮助都将不胜感激。提前感谢。如果需要更多信息,请告诉我。
编辑: 我在这个 Stackoverflow 帖子 中找到了一个解决方案。这是一个有点混乱的变通方法,但它有效: 在主函数中:
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for sig := range c {
fmt.Println(sig)
dpPool.dropTables(ctx)
fmt.Println("Dropped those tables :)")
dpPool.db.Close()
pprof.StopCPUProfile()
os.Exit(1)
}
}()
如果有更好的方法来处理 SIGINT 中断发生时的清理函数,请告诉我。
更多关于Golang中如何在SIGINT信号前延迟函数调用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
如何捕获退出信号,就像我做的那样,以及取消上下文是什么意思
更多关于Golang中如何在SIGINT信号前延迟函数调用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你好。我在主函数中创建了一个上下文,并将其传递到各处。一旦捕获到退出信号,我就取消这个上下文,并可以在原地进行清理,而不是在主函数中进行。
是的,几乎就像你做的那样。我认为不会有其他更优雅的方法了。类似这样:
func exitContext(parent context.Context) (context.Context, context.CancelFunc) {
ctx, cancel := context.WithCancel(parent)
go func() {
defer cancel()
osSignalCh := make(chan os.Signal, 1)
defer func() {
signal.Stop(osSignalCh)
close(osSignalCh)
}()
signal.Notify(osSignalCh,
syscall.SIGINT,
syscall.SIGTERM,
os.Interrupt,
)
for {
select {
case <-ctx.Done():
return
case <-osSignalCh:
return
}
}
}()
return ctx, cancel
}
我使用这个函数来包装上下文并将其传递给其他函数。一旦信号到来,上下文将被取消,我就能在各个地方优雅地处理关闭操作。
在Go中处理SIGINT信号时,推荐使用context和signal.NotifyContext来优雅地处理清理工作。以下是改进后的实现:
func main() {
dpPool := &DatabasePool{db: connectDb()}
defer dpPool.db.Close()
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
dpPool.setUpTables(ctx)
defer dpPool.dropTables(ctx)
mux := http.NewServeMux()
mux.HandleFunc("/", getRoot)
mux.HandleFunc("/hello", getHello)
mux.HandleFunc("/ping", ping)
server := &http.Server{
Addr: "<address>",
Handler: mux,
BaseContext: func(l net.Listener) context.Context {
return context.WithValue(ctx, keyServerAddr, l.Addr().String())
},
}
go func() {
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Shutdown(shutdownCtx); err != nil {
fmt.Printf("server shutdown error: %v\n", err)
}
}()
err := server.ListenAndServe()
if errors.Is(err, http.ErrServerClosed) {
fmt.Printf("server closed\n")
} else if err != nil {
fmt.Printf("error listening for server: %s\n", err)
}
}
或者使用更明确的信号处理方式:
func main() {
dpPool := &DatabasePool{db: connectDb()}
defer dpPool.db.Close()
ctx := context.Background()
dpPool.setUpTables(ctx)
defer dpPool.dropTables(ctx)
mux := http.NewServeMux()
mux.HandleFunc("/", getRoot)
mux.HandleFunc("/hello", getHello)
mux.HandleFunc("/ping", ping)
server := &http.Server{
Addr: "<address>",
Handler: mux,
BaseContext: func(l net.Listener) context.Context {
return context.WithValue(ctx, keyServerAddr, l.Addr().String())
},
}
done := make(chan struct{})
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
go func() {
<-sigChan
fmt.Println("Received interrupt signal, shutting down...")
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Shutdown(shutdownCtx); err != nil {
fmt.Printf("server shutdown error: %v\n", err)
}
close(done)
}()
go func() {
err := server.ListenAndServe()
if !errors.Is(err, http.ErrServerClosed) && err != nil {
fmt.Printf("server error: %v\n", err)
}
}()
<-done
fmt.Println("Server shutdown complete")
}
对于需要确保清理函数在SIGINT之前执行的情况,可以使用sync.Once:
func main() {
dpPool := &DatabasePool{db: connectDb()}
var cleanupOnce sync.Once
cleanup := func() {
cleanupOnce.Do(func() {
dpPool.dropTables(context.Background())
dpPool.db.Close()
})
}
defer cleanup()
ctx := context.Background()
dpPool.setUpTables(ctx)
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
go func() {
<-sigChan
cleanup()
os.Exit(0)
}()
// 服务器代码...
}

