前言
grace是facebook公司为golang服务开发的优雅重启和零停机部署的开源库。可以实现服务重启时,旧有连接不断,新服务启动后,新连接连入新服务,如此客户端无感知。
使用方法
(1)获取
go get github.com/facebookgo/grace/gracehttp
mod可以使用如下方式引入:
require github.com/facebookgo/grace latest
(2)使用
gracehttp.Serve(
&http.Server{
Addr: *address0, Handler: newHandler("Zero ")},
&http.Server{
Addr: *address1, Handler: newHandler("First ")},
&http.Server{
Addr: *address2, Handler: newHandler("Second")},
)
(3)重启命令
sudo kill -USR2
pidof yourservername
pidof yourservername
是指需要重启的服务进程id
源码解析
启动服务
// Serve will serve the given http.Servers and will monitor for signals
// allowing for graceful termination (SIGTERM) or restart (SIGUSR2).
func Serve(servers ...*http.Server) error {
a := newApp(servers)
return a.run()
}
func newApp(servers []*http.Server) *app {
return &app{
servers: servers,
http: &httpdown.HTTP{
},
net: &gracenet.Net{
},
listeners: make([]net.Listener, 0, len(servers)),
sds: make([]httpdown.Server, 0, len(servers)),
preStartProcess: func() error {
return nil },
// 2x num servers for possible Close or Stop errors + 1 for possible
// StartProcess error.
errors: make(chan error, 1+(len(servers)*2)),
}
}
构造app,具体的处理逻辑均在app中进行。
注意:
此处注释说明了Serve会启动指定的http.Servers,并且会监听系统信号以允许优雅结束(SIGTERM)或重启服务(SIGUSR2),实际代码中还支持SIGINT结束服务进程,我们可以根据需求指定信号来决定是结束还是重启服务。
func (a *app) run() error {
// Acquire Listeners
//获取所有http.Server服务地址的Listeners
if err := a.listen(); err != nil {
return err
}
// Some useful logging.
// logger的处理
if logger != nil {
if didInherit {
if ppid == 1 {
logger.Printf("Listening on init activated %s", pprintAddr(a.listeners))
} else {
const msg = "Graceful handoff of %s with new pid %d and old pid %d"
logger.Printf(msg, pprintAddr(a.listeners), os.Getpid(), ppid)
}
} else {
const msg = "Serving %s with pid %d"
logger.Printf(msg, pprintAddr(a.listeners), os.Getpid())
}
}
// Start serving.
// 启动各http服务
a.serve()
// Close the parent if we inherited and it wasn't init that started us.
// 如果已经继承,并且不是从init启动,就说明是重启进程,需要将原进程关闭。
if didInherit && ppid != 1 {
if err := syscall.Kill(ppid, syscall.SIGTERM); err != nil {
return fmt.Errorf("failed to close parent: %s", err)
}
}
// 此处为最核心处理部分
waitdone := make(chan struct{
})
go func() {
defer close(waitdone)
a.wait()
}()
select {
case err := <-a.errors:/