背景:
更新配置文件、更新server程序等,需要重启服务器,需要做到重启服务器时,服务器不停止运行,请求不丢失
原理:
热重启原理涉及到一些系统调用及父子进程之间文件句柄的传递等较多细节,处理过程大致如下:
serve.go
package goo
import (
"fmt"
"log"
"os"
"os/signal"
"syscall"
)
type Serve struct {
ShutDown bool
ChanSignal chan os.Signal
}
func NewServe() *Serve {
c := make(chan os.Signal)
signal.Notify(c)
serve := Serve{
ChanSignal: c,
}
serve.pid()
go serve.run()
return &serve
}
func (this *Serve) run() {
for s := range this.ChanSignal {
switch s {
case syscall.SIGHUP:
execSpec := &syscall.ProcAttr{
Env: os.Environ(),
Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
}
if _, err := syscall.ForkExec(os.Args[0], os.Args, execSpec); err != nil {
log.Fatalln(err.Error())
}
this.ShutDown = true
signal.Stop(this.ChanSignal)
case syscall.SIGKILL, syscall.SIGINT:
this.ShutDown = true
signal.Stop(this.ChanSignal)
}
}
}
func (this *Serve) pid() error {
file, err := os.Create(".pid")
if err != nil {
return err
}
defer file.Close()
file.WriteString(fmt.Sprintf("%d", os.Getpid()))
return nil
}
func (this *Serve) Stop() {
os.Exit(0)
}
main.go
package main
import (
"fmt"
"time"
"googo.co/goo"
)
var (
serve *goo.Serve
)
func init() {
serve = goo.NewServe()
}
func main() {
num := 0
c := make(chan string)
for {
num = num + 1
go SyncArticle(c)
for {
switch msg := <-c; msg {
case "end":
if serve.ShutDown {
serve.Stop()
}
time.Sleep(time.Duration(config.IDataApi.Time) * time.Minute)
break
default:
fmt.Println(time.Now().Format("2006-01-02 15:04:05"), num, msg)
}
}
}
}