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  }