DevilKing's blog

冷灯看剑,剑上几分功名?炉香无需计苍生,纵一穿烟逝,万丈云埋,孤阳还照古陵

0%

Go Graceful优雅重启

原文链接

优雅关闭部分:

就是服务器要关闭了, 会拒绝新的连接,但是老的连接不会被强制关闭,而是 会等待一定时间, 等待客户端主动关闭, 除非客户端一直没有关闭, 到了预设的超时时间才进行服务器端关闭.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func (srv *Server) shutdown(shutdown chan chan struct{}, kill chan struct{}) {
// Request done notification
done := make(chan struct{})
shutdown <- done

srv.stopLock.Lock()
defer srv.stopLock.Unlock()
if srv.Timeout > 0 {
select {
case <-done:
case <-time.After(srv.Timeout):
close(kill)
}
} else {
<-done
}
// Close the stopChan to wake up any blocked goroutines.
srv.chanLock.Lock()
if srv.stopChan != nil {
close(srv.stopChan)
}
srv.chanLock.Unlock()
}

当进行shutdown部分,会关闭stopChan这个channel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func main() { // goroutine 0
goAway := false
go func() { // goroutine 1
sig := <-sigs
fmt.Println("I have to go...", sig)
goAway = true
srv.Stop(10 * time.Second)
}()

for{
if (goAway){
break
}
fmt.Println("Started")
srv = &graceful.Server{
Timeout: 10 * time.Second,
NoSignalHandling: true,

ConnState: func(conn net.Conn, state http.ConnState) {
fmt.Println( "Connection ", state)
},

Server: &http.Server{
Addr: ":8001",
Handler: userRouter,
},
}

go srv.ListenAndServe() // goroutine 2
<- srv.StopChan() // goroutine 0
fmt.Println("Stopped")
}
}

得到stopChan channel关闭的消息,这样for循环又可以启动,重新接收相关的请求,重新初始化部分

可以在同一个socket对上, 共享同一个监听套接字地址, 然后在多个goroutine中执行accept函数,也就是http server中的accept操作

1
2
3
4
5
6
l := ln.(*net.TCPListener)
newFile, _ := l.File()
fmt.Println(newFile.Fd())

anotherListener, _ := net.FileListener(newFile)

套接字部分,转换给文件描述符

关于fork部分的操作

1
2
3
4
cmd := exec.Command(os.Args[0], "-graceful")
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
cmd.ExtraFiles = []*os.File{newFD}
cmd.Run()