support hot-update
Showing
2 changed files
with
171 additions
and
7 deletions
| ... | @@ -29,10 +29,10 @@ var ( | ... | @@ -29,10 +29,10 @@ var ( |
| 29 | ViewsPath string | 29 | ViewsPath string |
| 30 | RunMode string //"dev" or "prod" | 30 | RunMode string //"dev" or "prod" |
| 31 | AppConfig *Config | 31 | AppConfig *Config |
| 32 | //related to session | 32 | //related to session |
| 33 | GlobalSessions *session.Manager //GlobalSessions | 33 | GlobalSessions *session.Manager //GlobalSessions |
| 34 | SessionOn bool // wheather auto start session,default is false | 34 | SessionOn bool // wheather auto start session,default is false |
| 35 | SessionProvider string // default session provider memory mysql redis | 35 | SessionProvider string // default session provider memory mysql redis |
| 36 | SessionName string // sessionName cookie's name | 36 | SessionName string // sessionName cookie's name |
| 37 | SessionGCMaxLifetime int64 // session's gc maxlifetime | 37 | SessionGCMaxLifetime int64 // session's gc maxlifetime |
| 38 | SessionSavePath string // session savepath if use mysql/redis/file this set to the connectinfo | 38 | SessionSavePath string // session savepath if use mysql/redis/file this set to the connectinfo |
| ... | @@ -81,15 +81,27 @@ func NewApp() *App { | ... | @@ -81,15 +81,27 @@ func NewApp() *App { |
| 81 | 81 | ||
| 82 | func (app *App) Run() { | 82 | func (app *App) Run() { |
| 83 | addr := fmt.Sprintf("%s:%d", HttpAddr, HttpPort) | 83 | addr := fmt.Sprintf("%s:%d", HttpAddr, HttpPort) |
| 84 | var err error | 84 | var ( |
| 85 | err error | ||
| 86 | l net.Listener | ||
| 87 | ) | ||
| 85 | if UseFcgi { | 88 | if UseFcgi { |
| 86 | l, e := net.Listen("tcp", addr) | 89 | l, err = net.Listen("tcp", addr) |
| 87 | if e != nil { | 90 | if err != nil { |
| 88 | BeeLogger.Fatal("Listen: ", e) | 91 | BeeLogger.Fatal("Listen: ", err) |
| 89 | } | 92 | } |
| 90 | err = fcgi.Serve(l, app.Handlers) | 93 | err = fcgi.Serve(l, app.Handlers) |
| 91 | } else { | 94 | } else { |
| 92 | err = http.ListenAndServe(addr, app.Handlers) | 95 | server := &http.Server{Handler: app.Handlers} |
| 96 | laddr, err := net.ResolveTCPAddr("tcp", addr) | ||
| 97 | if nil != err { | ||
| 98 | BeeLogger.Fatal("ResolveTCPAddr:", err) | ||
| 99 | } | ||
| 100 | l, err = GetInitListner(laddr) | ||
| 101 | theStoppable = newStoppable(l) | ||
| 102 | err = server.Serve(theStoppable) | ||
| 103 | theStoppable.wg.Wait() | ||
| 104 | CloseSelf() | ||
| 93 | } | 105 | } |
| 94 | if err != nil { | 106 | if err != nil { |
| 95 | BeeLogger.Fatal("ListenAndServe: ", err) | 107 | BeeLogger.Fatal("ListenAndServe: ", err) | ... | ... |
reload.go
0 → 100644
| 1 | // Zero-downtime restarts in Go. | ||
| 2 | package beego | ||
| 3 | |||
| 4 | import ( | ||
| 5 | "errors" | ||
| 6 | "fmt" | ||
| 7 | "log" | ||
| 8 | "net" | ||
| 9 | "os" | ||
| 10 | "os/exec" | ||
| 11 | "os/signal" | ||
| 12 | "reflect" | ||
| 13 | "strconv" | ||
| 14 | "sync" | ||
| 15 | "syscall" | ||
| 16 | ) | ||
| 17 | |||
| 18 | const ( | ||
| 19 | FDKey = "BEEGO_HOT_FD" | ||
| 20 | ) | ||
| 21 | |||
| 22 | // Export an error equivalent to net.errClosing for use with Accept during | ||
| 23 | // a graceful exit. | ||
| 24 | var ErrClosing = errors.New("use of closed network connection") | ||
| 25 | var ErrInitStart = errors.New("init from") | ||
| 26 | |||
| 27 | // Allows for us to notice when the connection is closed. | ||
| 28 | type conn struct { | ||
| 29 | net.Conn | ||
| 30 | wg *sync.WaitGroup | ||
| 31 | } | ||
| 32 | |||
| 33 | func (c conn) Close() error { | ||
| 34 | err := c.Conn.Close() | ||
| 35 | c.wg.Done() | ||
| 36 | return err | ||
| 37 | } | ||
| 38 | |||
| 39 | type stoppableListener struct { | ||
| 40 | net.Listener | ||
| 41 | count int64 | ||
| 42 | stopped bool | ||
| 43 | wg sync.WaitGroup | ||
| 44 | } | ||
| 45 | |||
| 46 | var theStoppable *stoppableListener | ||
| 47 | |||
| 48 | func newStoppable(l net.Listener) (sl *stoppableListener) { | ||
| 49 | sl = &stoppableListener{Listener: l} | ||
| 50 | |||
| 51 | // this goroutine monitors the channel. Can't do this in | ||
| 52 | // Accept (below) because once it enters sl.Listener.Accept() | ||
| 53 | // it blocks. We unblock it by closing the fd it is trying to | ||
| 54 | // accept(2) on. | ||
| 55 | go func() { | ||
| 56 | WaitSignal(l) | ||
| 57 | sl.stopped = true | ||
| 58 | sl.Listener.Close() | ||
| 59 | }() | ||
| 60 | return | ||
| 61 | } | ||
| 62 | |||
| 63 | func (sl *stoppableListener) Accept() (c net.Conn, err error) { | ||
| 64 | c, err = sl.Listener.Accept() | ||
| 65 | if err != nil { | ||
| 66 | return | ||
| 67 | } | ||
| 68 | sl.wg.Add(1) | ||
| 69 | // Wrap the returned connection, so that we can observe when | ||
| 70 | // it is closed. | ||
| 71 | c = conn{Conn: c, wg: &sl.wg} | ||
| 72 | |||
| 73 | return | ||
| 74 | } | ||
| 75 | |||
| 76 | func WaitSignal(l net.Listener) error { | ||
| 77 | ch := make(chan os.Signal, 1) | ||
| 78 | signal.Notify(ch, syscall.SIGTERM, syscall.SIGHUP) | ||
| 79 | for { | ||
| 80 | sig := <-ch | ||
| 81 | log.Println(sig.String()) | ||
| 82 | switch sig { | ||
| 83 | |||
| 84 | case syscall.SIGTERM: | ||
| 85 | return nil | ||
| 86 | case syscall.SIGHUP: | ||
| 87 | err := Restart(l) | ||
| 88 | if nil != err { | ||
| 89 | return err | ||
| 90 | } | ||
| 91 | return nil | ||
| 92 | } | ||
| 93 | } | ||
| 94 | return nil // It'll never get here. | ||
| 95 | } | ||
| 96 | |||
| 97 | func CloseSelf() error { | ||
| 98 | ppid := os.Getpid() | ||
| 99 | if ppid == 1 { // init provided sockets, for example systemd | ||
| 100 | return nil | ||
| 101 | } | ||
| 102 | p, err := os.FindProcess(ppid) | ||
| 103 | if err != nil { | ||
| 104 | return err | ||
| 105 | } | ||
| 106 | return p.Kill() | ||
| 107 | } | ||
| 108 | |||
| 109 | // Re-exec this image without dropping the listener passed to this function. | ||
| 110 | func Restart(l net.Listener) error { | ||
| 111 | argv0, err := exec.LookPath(os.Args[0]) | ||
| 112 | if nil != err { | ||
| 113 | return err | ||
| 114 | } | ||
| 115 | wd, err := os.Getwd() | ||
| 116 | if nil != err { | ||
| 117 | return err | ||
| 118 | } | ||
| 119 | v := reflect.ValueOf(l).Elem().FieldByName("fd").Elem() | ||
| 120 | fd := uintptr(v.FieldByName("sysfd").Int()) | ||
| 121 | allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, | ||
| 122 | os.NewFile(fd, string(v.FieldByName("sysfile").String()))) | ||
| 123 | |||
| 124 | p, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{ | ||
| 125 | Dir: wd, | ||
| 126 | Env: append(os.Environ(), fmt.Sprintf("%s=%d", FDKey, fd)), | ||
| 127 | Files: allFiles, | ||
| 128 | }) | ||
| 129 | if nil != err { | ||
| 130 | return err | ||
| 131 | } | ||
| 132 | log.Printf("spawned child %d\n", p.Pid) | ||
| 133 | return nil | ||
| 134 | } | ||
| 135 | |||
| 136 | func GetInitListner(tcpaddr *net.TCPAddr) (l net.Listener, err error) { | ||
| 137 | countStr := os.Getenv(FDKey) | ||
| 138 | if countStr == "" { | ||
| 139 | return net.ListenTCP("tcp", tcpaddr) | ||
| 140 | } else { | ||
| 141 | count, err := strconv.Atoi(countStr) | ||
| 142 | if err != nil { | ||
| 143 | return nil, err | ||
| 144 | } | ||
| 145 | f := os.NewFile(uintptr(count), "listen socket") | ||
| 146 | l, err = net.FileListener(f) | ||
| 147 | if err != nil { | ||
| 148 | return nil, err | ||
| 149 | } | ||
| 150 | return l, nil | ||
| 151 | } | ||
| 152 | } |
-
Please register or sign in to post a comment