github.com/zhongdalu/gf@v1.0.0/g/net/ghttp/ghttp_server_admin_process.go (about) 1 // Copyright 2018 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/zhongdalu/gf. 6 // pprof封装. 7 8 package ghttp 9 10 import ( 11 "bytes" 12 "errors" 13 "fmt" 14 "github.com/zhongdalu/gf/g/container/gtype" 15 "github.com/zhongdalu/gf/g/encoding/gjson" 16 "github.com/zhongdalu/gf/g/os/glog" 17 "github.com/zhongdalu/gf/g/os/gproc" 18 "github.com/zhongdalu/gf/g/os/gtime" 19 "github.com/zhongdalu/gf/g/os/gtimer" 20 "github.com/zhongdalu/gf/g/util/gconv" 21 "os" 22 "runtime" 23 "strings" 24 "sync" 25 "time" 26 ) 27 28 const ( 29 gADMIN_ACTION_INTERVAL_LIMIT = 2000 // (毫秒)服务开启后允许执行管理操作的间隔限制 30 gADMIN_ACTION_NONE = 0 31 gADMIN_ACTION_RESTARTING = 1 32 gADMIN_ACTION_SHUTINGDOWN = 2 33 gADMIN_ACTION_RELOAD_ENVKEY = "GF_SERVER_RELOAD" 34 gADMIN_ACTION_RESTART_ENVKEY = "GF_SERVER_RESTART" 35 gADMIN_GPROC_COMM_GROUP = "GF_GPROC_HTTP_SERVER" 36 ) 37 38 // 用于服务管理的对象 39 type utilAdmin struct{} 40 41 // (进程级别)用于Web Server管理操作的互斥锁,保证管理操作的原子性 42 var serverActionLocker sync.Mutex 43 44 // (进程级别)用于记录上一次操作的时间(毫秒) 45 var serverActionLastTime = gtype.NewInt64(gtime.Millisecond()) 46 47 // 当前服务进程所处的互斥管理操作状态 48 var serverProcessStatus = gtype.NewInt() 49 50 // 重启Web Server,参数支持自定义重启的可执行文件路径,不传递时默认和原有可执行文件路径一致。 51 // 针对*niux系统: 平滑重启 52 // 针对windows : 完整重启 53 func RestartAllServer(newExeFilePath ...string) error { 54 serverActionLocker.Lock() 55 defer serverActionLocker.Unlock() 56 if err := checkProcessStatus(); err != nil { 57 return err 58 } 59 if err := checkActionFrequence(); err != nil { 60 return err 61 } 62 return restartWebServers("", newExeFilePath...) 63 } 64 65 // 关闭所有的WebServer 66 func ShutdownAllServer() error { 67 serverActionLocker.Lock() 68 defer serverActionLocker.Unlock() 69 if err := checkProcessStatus(); err != nil { 70 return err 71 } 72 if err := checkActionFrequence(); err != nil { 73 return err 74 } 75 shutdownWebServers() 76 return nil 77 } 78 79 // 检查当前服务进程的状态 80 func checkProcessStatus() error { 81 status := serverProcessStatus.Val() 82 if status > 0 { 83 switch status { 84 case gADMIN_ACTION_RESTARTING: 85 return errors.New("server is restarting") 86 case gADMIN_ACTION_SHUTINGDOWN: 87 return errors.New("server is shutting down") 88 } 89 } 90 return nil 91 } 92 93 // 检测当前操作的频繁度 94 func checkActionFrequence() error { 95 interval := gtime.Millisecond() - serverActionLastTime.Val() 96 if interval < gADMIN_ACTION_INTERVAL_LIMIT { 97 return errors.New(fmt.Sprintf("too frequent action, please retry in %d ms", gADMIN_ACTION_INTERVAL_LIMIT-interval)) 98 } 99 serverActionLastTime.Set(gtime.Millisecond()) 100 return nil 101 } 102 103 // 平滑重启:创建一个子进程,通过环境变量传参 104 func forkReloadProcess(newExeFilePath ...string) error { 105 path := os.Args[0] 106 if len(newExeFilePath) > 0 { 107 path = newExeFilePath[0] 108 } 109 p := gproc.NewProcess(path, os.Args, os.Environ()) 110 // 创建新的服务进程,子进程自动从父进程复制文件描述来监听同样的端口 111 sfm := getServerFdMap() 112 // 将sfm中的fd按照子进程创建时的文件描述符顺序进行整理,以便子进程获取到正确的fd 113 for name, m := range sfm { 114 for fdk, fdv := range m { 115 if len(fdv) > 0 { 116 s := "" 117 for _, item := range strings.Split(fdv, ",") { 118 array := strings.Split(item, "#") 119 fd := uintptr(gconv.Uint(array[1])) 120 if fd > 0 { 121 s += fmt.Sprintf("%s#%d,", array[0], 3+len(p.ExtraFiles)) 122 p.ExtraFiles = append(p.ExtraFiles, os.NewFile(fd, "")) 123 } else { 124 s += fmt.Sprintf("%s#%d,", array[0], 0) 125 } 126 } 127 sfm[name][fdk] = strings.TrimRight(s, ",") 128 } 129 } 130 } 131 buffer, _ := gjson.Encode(sfm) 132 p.Env = append(p.Env, gADMIN_ACTION_RELOAD_ENVKEY+"="+string(buffer)) 133 if _, err := p.Start(); err != nil { 134 glog.Errorf("%d: fork process failed, error:%s, %s", gproc.Pid(), err.Error(), string(buffer)) 135 return err 136 } 137 return nil 138 } 139 140 // 完整重启:创建一个新的子进程 141 func forkRestartProcess(newExeFilePath ...string) error { 142 path := os.Args[0] 143 if len(newExeFilePath) > 0 { 144 path = newExeFilePath[0] 145 } 146 // 去掉平滑重启的环境变量参数 147 os.Unsetenv(gADMIN_ACTION_RELOAD_ENVKEY) 148 env := os.Environ() 149 env = append(env, gADMIN_ACTION_RESTART_ENVKEY+"=1") 150 p := gproc.NewProcess(path, os.Args, env) 151 if _, err := p.Start(); err != nil { 152 glog.Errorf("%d: fork process failed, error:%s", gproc.Pid(), err.Error()) 153 return err 154 } 155 return nil 156 } 157 158 // 获取所有Web Server的文件描述符map 159 func getServerFdMap() map[string]listenerFdMap { 160 sfm := make(map[string]listenerFdMap) 161 serverMapping.RLockFunc(func(m map[string]interface{}) { 162 for k, v := range m { 163 sfm[k] = v.(*Server).getListenerFdMap() 164 } 165 }) 166 return sfm 167 } 168 169 // 二进制转换为FdMap 170 func bufferToServerFdMap(buffer []byte) map[string]listenerFdMap { 171 sfm := make(map[string]listenerFdMap) 172 if len(buffer) > 0 { 173 j, _ := gjson.LoadContent(buffer) 174 for k, _ := range j.ToMap() { 175 m := make(map[string]string) 176 for k, v := range j.GetMap(k) { 177 m[k] = gconv.String(v) 178 } 179 sfm[k] = m 180 } 181 } 182 return sfm 183 } 184 185 // Web Server重启 186 func restartWebServers(signal string, newExeFilePath ...string) error { 187 serverProcessStatus.Set(gADMIN_ACTION_RESTARTING) 188 if runtime.GOOS == "windows" { 189 if len(signal) > 0 { 190 // 在终端信号下,立即执行重启操作 191 forceCloseWebServers() 192 forkRestartProcess(newExeFilePath...) 193 } else { 194 // 非终端信号下,异步1秒后再执行重启,目的是让接口能够正确返回结果,否则接口会报错(因为web server关闭了) 195 gtimer.SetTimeout(time.Second, func() { 196 forceCloseWebServers() 197 forkRestartProcess(newExeFilePath...) 198 }) 199 } 200 } else { 201 if err := forkReloadProcess(newExeFilePath...); err != nil { 202 glog.Printf("%d: server restarts failed", gproc.Pid()) 203 serverProcessStatus.Set(gADMIN_ACTION_NONE) 204 return err 205 } else { 206 if len(signal) > 0 { 207 glog.Printf("%d: server restarting by signal: %s", gproc.Pid(), signal) 208 } else { 209 glog.Printf("%d: server restarting by web admin", gproc.Pid()) 210 } 211 212 } 213 } 214 return nil 215 } 216 217 // 关闭所有Web Server 218 func shutdownWebServers(signal ...string) { 219 serverProcessStatus.Set(gADMIN_ACTION_SHUTINGDOWN) 220 if len(signal) > 0 { 221 glog.Printf("%d: server shutting down by signal: %s", gproc.Pid(), signal[0]) 222 // 在终端信号下,立即执行关闭操作 223 forceCloseWebServers() 224 allDoneChan <- struct{}{} 225 } else { 226 glog.Printf("%d: server shutting down by api", gproc.Pid()) 227 // 非终端信号下,异步1秒后再执行关闭, 228 // 目的是让接口能够正确返回结果,否则接口会报错(因为web server关闭了) 229 gtimer.SetTimeout(time.Second, func() { 230 forceCloseWebServers() 231 allDoneChan <- struct{}{} 232 }) 233 } 234 } 235 236 // 关优雅闭进程所有端口的Web Server服务 237 // 注意,只是关闭Web Server服务,并不是退出进程 238 func gracefulShutdownWebServers() { 239 serverMapping.RLockFunc(func(m map[string]interface{}) { 240 for _, v := range m { 241 for _, s := range v.(*Server).servers { 242 s.shutdown() 243 } 244 } 245 }) 246 } 247 248 // 强制关闭进程所有端口的Web Server服务 249 // 注意,只是关闭Web Server服务,并不是退出进程 250 func forceCloseWebServers() { 251 serverMapping.RLockFunc(func(m map[string]interface{}) { 252 for _, v := range m { 253 for _, s := range v.(*Server).servers { 254 s.close() 255 } 256 } 257 }) 258 } 259 260 // 异步监听进程间消息 261 func handleProcessMessage() { 262 for { 263 if msg := gproc.Receive(gADMIN_GPROC_COMM_GROUP); msg != nil { 264 if bytes.EqualFold(msg.Data, []byte("exit")) { 265 gracefulShutdownWebServers() 266 allDoneChan <- struct{}{} 267 return 268 } 269 } 270 } 271 }