45536776 by astaxie

support hot-update

1 parent be929036
...@@ -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)
......
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 }
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!