15
谈 Golang http.Server 安全退出:容易被误用的 Shutdown()方法
source link: https://www.v2ex.com/t/803612
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
14 条回复 • 2021-09-23 18:14:05 +08:00
SorcererXW 17 小时 47 分钟前 1
我的理解是不是将退出操作放在主协程,其实 server 放在另外一个协程,就能避免立即退出?
func main() {
go server.Server()
<- signal
server.Shutdown(ctx)
}
func main() {
go server.Server()
<- signal
server.Shutdown(ctx)
}
nanmu42 17 小时 43 分钟前
@SorcererXW 这里就见仁见智了,ListenAndServe()在 goroutine 中的话,错误处理大概率是 log.Fatal(err)这样的操作,如果服务并不是主动退出的(比如启动时立马遇到端口占用的错误),主函数 main()中的 defer 是不会执行的。我这里用了一些额外的复杂度让安全退出的逻辑更圆满了一些。
v2Geeker 16 小时 52 分钟前 1
见识了。
我一般都是 ListenAndServe 和 Shutdown 都放在 2 个不同的 gorountine 中,用 sync.WaitGroup 的 Wait 来等待结束,于是我好像从来没意识到 Shutdown 有这样的问题。
我一般都是 ListenAndServe 和 Shutdown 都放在 2 个不同的 gorountine 中,用 sync.WaitGroup 的 Wait 来等待结束,于是我好像从来没意识到 Shutdown 有这样的问题。
whitedroa 16 小时 30 分钟前 1
@nanmu42 没太看懂:“错误处理大概率是 log.Fatal(err)这样的操作” 这句话是什么意思呢
“如果服务并不是主动退出的(比如启动时立马遇到端口占用的错误),主函数 main()中的 defer 是不会执行的”
这里 main 中的 defer 为什么不会执行呢,是因为其他协程 panic 导致程序直接退出吗?
“如果服务并不是主动退出的(比如启动时立马遇到端口占用的错误),主函数 main()中的 defer 是不会执行的”
这里 main 中的 defer 为什么不会执行呢,是因为其他协程 panic 导致程序直接退出吗?
FrankAdler 15 小时 45 分钟前 1
@nanmu42 #2 想让 main 的 defe 能执行,就需要让 main 正常退出,那在 goroutine 里出错错误的时候,发送 sign 到 main 里等等的 chan 就行了,比如
defer func() {
log.Println("defer")
}()
server := http.Server{
Addr: fmt.Sprintf(":%d", *port),
Handler: downright.SlowHandler(*sleepSeconds),
}
quit := make(chan os.Signal, 1)
go func() {
err := server.ListenAndServe()
if err != http.ErrServerClosed {
log.Printf("ListenAndServe err: %v", err)
quit <- syscall.SIGTERM
}
}()
signal.Notify(quit, os.Interrupt)
<-quit
log.Println("waiting for shutdown finishing...")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatalf("shutdown err: %v", err)
}
log.Println("shutdown finished")
ListenAndServe 不管出现什么级别的错误都可以处理(只要不调用 os.Exit ),毕竟 main 一直在等待新号阻塞着
defer func() {
log.Println("defer")
}()
server := http.Server{
Addr: fmt.Sprintf(":%d", *port),
Handler: downright.SlowHandler(*sleepSeconds),
}
quit := make(chan os.Signal, 1)
go func() {
err := server.ListenAndServe()
if err != http.ErrServerClosed {
log.Printf("ListenAndServe err: %v", err)
quit <- syscall.SIGTERM
}
}()
signal.Notify(quit, os.Interrupt)
<-quit
log.Println("waiting for shutdown finishing...")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatalf("shutdown err: %v", err)
}
log.Println("shutdown finished")
ListenAndServe 不管出现什么级别的错误都可以处理(只要不调用 os.Exit ),毕竟 main 一直在等待新号阻塞着
nanmu42 15 小时 41 分钟前
@whitedroa 在另一个 goroutine 里做 ListenAndServe(),它的返回值一般是用 log.Fatal()来接的,要不然就不晓得 HTTP 服务启停状态了。
log.Fatal()调用的是 os.Exit(),这个方法会造成 go 程序直接退出,main()里的 defer 函数不运行(博文里链了 godoc 链接)。
当然也可以不用 log.Fatal(),自己搞定同步,但是那样复杂度上来了。
log.Fatal()调用的是 os.Exit(),这个方法会造成 go 程序直接退出,main()里的 defer 函数不运行(博文里链了 godoc 链接)。
当然也可以不用 log.Fatal(),自己搞定同步,但是那样复杂度上来了。
Yoock 14 小时 53 分钟前 1
index90 13 小时 45 分钟前 1
你这样子改,如果一个程序要启动多个 http 服务就不行了。
如果你是担心 goroutine 启动 server,server 意外退出的问题,用 errgroup 好了。
比较完善的多 routine 和退出处理,用 rungourp 一把梭
如果你是担心 goroutine 启动 server,server 意外退出的问题,用 errgroup 好了。
比较完善的多 routine 和退出处理,用 rungourp 一把梭
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK