改进ghttp.Server退出机制处理

This commit is contained in:
john
2018-08-01 13:04:15 +08:00
parent 711ef1e2cf
commit 2a7ea92c2e
7 changed files with 75 additions and 47 deletions

View File

@ -30,6 +30,10 @@ import (
"container/list"
)
const (
SERVER_STATUS_STOPPED = 0 // Server状态停止
SERVER_STATUS_RUNNING = 1 // Server状态运行
)
const (
gHTTP_METHODS = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
gDEFAULT_SERVER = "default"
@ -39,8 +43,6 @@ const (
gDEFAULT_COOKIE_MAX_AGE = 86400*365 // 默认cookie有效期(一年)
gDEFAULT_SESSION_MAX_AGE = 600 // 默认session有效期(600秒)
gDEFAULT_SESSION_ID_NAME = "gfsessionid" // 默认存放Cookie中的SessionId名称
gSERVER_STATUS_STOPPED = 0 // Server状态停止
gSERVER_STATUS_RUNNING = 1 // Server状态运行
)
// ghttp.Server结构体
@ -49,7 +51,6 @@ type Server struct {
name string // 服务名称,方便识别
paths *gspath.SPath // 静态文件检索对象
config ServerConfig // 配置对象
status int8 // 当前服务器状态(0未启动1运行中)
servers []*gracefulServer // 底层http.Server列表
methodsMap map[string]bool // 所有支持的HTTP Method(初始化时自动填充)
servedCount *gtype.Int // 已经服务的请求数(4-8字节不考虑溢出情况)同时作为请求ID
@ -120,6 +121,9 @@ type listenerFdMap map[string]string
// Server表用以存储和检索名称与Server对象之间的关联关系
var serverMapping = gmap.NewStringInterfaceMap()
// 正常运行的Server数量如果没有运行、失败或者全部退出那么该值为0
var serverRunning = gtype.NewInt()
// Web Socket默认配置
var wsUpgrader = websocket.Upgrader{}
@ -214,7 +218,7 @@ func (s *Server) Start() error {
}
}
if s.status == gSERVER_STATUS_RUNNING {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server is already running")
}
// 底层http server配置
@ -351,20 +355,32 @@ func (s *Server) startServer(fdMap listenerFdMap) {
// 开始执行异步监听
for _, v := range s.servers {
go func(server *gracefulServer) {
var err error
serverRunning.Add(1)
err := (error)(nil)
if server.isHttps {
err = server.ListenAndServeTLS(s.config.HTTPSCertPath, s.config.HTTPSKeyPath)
} else {
err = server.ListenAndServe()
}
serverRunning.Add(-1)
// 如果非关闭错误,那么提示报错,否则认为是正常的服务关闭操作
if err != nil && !strings.EqualFold(http.ErrServerClosed.Error(), err.Error()) {
glog.Error(err)
}
// 如果所有异步的Server都已经停止那么主Server就可以退出了
if serverRunning.Val() < 1 {
doneChan <- struct{}{}
}
}(v)
}
}
s.status = gSERVER_STATUS_RUNNING
// 获取当前服务器的状态
func (s *Server) Status() int {
if serverRunning.Val() > 0 {
return SERVER_STATUS_RUNNING
}
return SERVER_STATUS_STOPPED
}
// 获取当前监听的文件描述符信息构造成map返回

View File

@ -112,7 +112,7 @@ func (s *Server) Restart(newExeFilePath...string) error {
if err := s.checkActionFrequence(); err != nil {
return err
}
restartWebServers(newExeFilePath...)
restartWebServers(false, newExeFilePath...)
return nil
}
@ -126,7 +126,7 @@ func (s *Server) Shutdown() error {
if err := s.checkActionFrequence(); err != nil {
return err
}
shutdownWebServers()
shutdownWebServers(false)
return nil
}
@ -231,29 +231,41 @@ func bufferToServerFdMap(buffer []byte) map[string]listenerFdMap {
}
// Web Server重启
func restartWebServers(newExeFilePath...string) {
func restartWebServers(isSignal bool, newExeFilePath...string) {
serverProcessStatus.Set(gADMIN_ACTION_RESTARTING)
glog.Printfln("%d: server restarting", gproc.Pid())
if runtime.GOOS == "windows" {
// 异步1秒后再执行重启目的是让接口能够正确返回结果否则接口会报错(因为web server关闭了)
gtime.SetTimeout(time.Second, func() {
if isSignal {
// 在终端信号下,立即执行重启操作
forcedlyCloseWebServers()
forkRestartProcess(newExeFilePath...)
})
} else {
// 非终端信号下异步1秒后再执行重启目的是让接口能够正确返回结果否则接口会报错(因为web server关闭了)
gtime.SetTimeout(time.Second, func() {
forcedlyCloseWebServers()
forkRestartProcess(newExeFilePath...)
})
}
} else {
forkReloadProcess(newExeFilePath...)
}
}
// Web Server关闭服务
func shutdownWebServers() {
func shutdownWebServers(isSignal bool) {
serverProcessStatus.Set(gADMIN_ACTION_SHUTINGDOWN)
glog.Printfln("%d: server shutting down", gproc.Pid())
// 异步1秒后再执行重启目的是让接口能够正确返回结果否则接口会报错(因为web server关闭了)
gtime.SetTimeout(time.Second, func() {
if isSignal {
// 在终端信号下,立即执行关闭操作
forcedlyCloseWebServers()
doneChan <- struct{}{}
})
} else {
// 非终端信号下异步1秒后再执行关闭目的是让接口能够正确返回结果否则接口会报错(因为web server关闭了)
gtime.SetTimeout(time.Second, func() {
forcedlyCloseWebServers()
doneChan <- struct{}{}
})
}
}
// 关优雅闭进程所有端口的Web Server服务

View File

@ -35,12 +35,12 @@ func handleProcessSignal() {
switch sig {
// 进程终止,停止所有子进程运行
case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGHUP, syscall.SIGTERM:
shutdownWebServers()
shutdownWebServers(true)
return
// 用户信号,重启服务
case syscall.SIGUSR1:
restartWebServers()
restartWebServers(true)
return
default:

View File

@ -25,8 +25,8 @@ type gracefulServer struct {
httpServer *http.Server
rawListener net.Listener // 原始listener
listener net.Listener // 接口化封装的listener
isHttps bool
shutdownChan chan bool
isHttps bool // 是否HTTPS
status int // 当前Server状态(关闭/运行)
}
// 创建一个优雅的Http Server
@ -34,7 +34,6 @@ func (s *Server) newGracefulServer(addr string, fd...int) *gracefulServer {
gs := &gracefulServer {
addr : addr,
httpServer : s.newHttpServer(addr),
shutdownChan : make(chan bool),
}
// 是否有继承的文件描述符
if len(fd) > 0 && fd[0] > 0 {
@ -125,8 +124,9 @@ func (s *gracefulServer) doServe() error {
action = "reloaded"
}
glog.Printfln("%d: %s server %s listening on [%s]", gproc.Pid(), s.getProto(), action, s.addr)
s.status = SERVER_STATUS_RUNNING
err := s.httpServer.Serve(s.listener)
<-s.shutdownChan
s.status = SERVER_STATUS_STOPPED
return err
}
@ -162,21 +162,21 @@ func (s *gracefulServer) getNetListener(addr string) (net.Listener, error) {
// 执行请求优雅关闭
func (s *gracefulServer) shutdown() {
if s.status == SERVER_STATUS_STOPPED {
return
}
if err := s.httpServer.Shutdown(context.Background()); err != nil {
glog.Errorfln("%d: %s server [%s] shutdown error: %v", gproc.Pid(), s.getProto(), s.addr, err)
} else {
//glog.Printfln("%d: %s server [%s] shutdown smoothly", gproc.Pid(), s.getProto(), s.addr)
s.shutdownChan <- true
}
}
// 执行请求强制关闭
func (s *gracefulServer) close() {
if s.status == SERVER_STATUS_STOPPED {
return
}
if err := s.httpServer.Close(); err != nil {
glog.Errorfln("%d: %s server [%s] closed error: %v", gproc.Pid(), s.getProto(), s.addr, err)
} else {
//glog.Printfln("%d: %s server [%s] closed smoothly", gproc.Pid(), s.getProto(), s.addr)
s.shutdownChan <- true
}
}

View File

@ -19,7 +19,7 @@ import (
// http server setting设置
// 注意使用该方法进行http server配置时需要配置所有的配置项否则没有配置的属性将会默认变量为空
func (s *Server)SetConfig(c ServerConfig) error {
if s.status == 1 {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
}
if c.Handler == nil {
@ -42,7 +42,7 @@ func (s *Server)SetConfig(c ServerConfig) error {
// 设置http server参数 - Addr
func (s *Server)SetAddr(addr string) error {
if s.status == 1 {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
}
s.config.Addr = addr
@ -51,7 +51,7 @@ func (s *Server)SetAddr(addr string) error {
// 设置http server参数 - Port
func (s *Server)SetPort(port...int) error {
if s.status == 1 {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
}
if len(port) > 0 {
@ -68,7 +68,7 @@ func (s *Server)SetPort(port...int) error {
// 设置http server参数 - HTTPS Addr
func (s *Server)SetHTTPSAddr(addr string) error {
if s.status == 1 {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
}
s.config.HTTPSAddr = addr
@ -77,7 +77,7 @@ func (s *Server)SetHTTPSAddr(addr string) error {
// 设置http server参数 - HTTPS Port
func (s *Server)SetHTTPSPort(port...int) error {
if s.status == 1 {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
}
if len(port) > 0 {
@ -94,7 +94,7 @@ func (s *Server)SetHTTPSPort(port...int) error {
// 开启HTTPS支持但是必须提供Cert和Key文件
func (s *Server)EnableHTTPS(certFile, keyFile string) error {
if s.status == 1 {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
}
s.config.HTTPSCertPath = certFile
@ -104,7 +104,7 @@ func (s *Server)EnableHTTPS(certFile, keyFile string) error {
// 设置http server参数 - ReadTimeout
func (s *Server)SetReadTimeout(t time.Duration) error {
if s.status == 1 {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
}
s.config.ReadTimeout = t
@ -113,7 +113,7 @@ func (s *Server)SetReadTimeout(t time.Duration) error {
// 设置http server参数 - WriteTimeout
func (s *Server)SetWriteTimeout(t time.Duration) error {
if s.status == 1 {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
}
s.config.WriteTimeout = t
@ -122,7 +122,7 @@ func (s *Server)SetWriteTimeout(t time.Duration) error {
// 设置http server参数 - IdleTimeout
func (s *Server)SetIdleTimeout(t time.Duration) error {
if s.status == 1 {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
}
s.config.IdleTimeout = t
@ -131,7 +131,7 @@ func (s *Server)SetIdleTimeout(t time.Duration) error {
// 设置http server参数 - MaxHeaderBytes
func (s *Server)SetMaxHeaderBytes(b int) error {
if s.status == 1 {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
}
s.config.MaxHeaderBytes = b
@ -140,7 +140,7 @@ func (s *Server)SetMaxHeaderBytes(b int) error {
// 设置http server参数 - IndexFiles默认展示文件index.html, index.htm
func (s *Server)SetIndexFiles(index []string) error {
if s.status == 1 {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
}
s.config.IndexFiles = index
@ -149,7 +149,7 @@ func (s *Server)SetIndexFiles(index []string) error {
// 允许展示访问目录的文件列表
func (s *Server)SetIndexFolder(index bool) error {
if s.status == 1 {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
}
s.config.IndexFolder = index
@ -158,7 +158,7 @@ func (s *Server)SetIndexFolder(index bool) error {
// 设置http server参数 - ServerAgent
func (s *Server)SetServerAgent(agent string) error {
if s.status == 1 {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
}
s.config.ServerAgent = agent
@ -167,7 +167,7 @@ func (s *Server)SetServerAgent(agent string) error {
// 设置http server参数 - ServerRoot
func (s *Server)SetServerRoot(root string) error {
if s.status == 1 {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server config cannot be changed while running")
}
// RealPath的作用除了校验地址正确性以外还转换分隔符号为当前系统正确的文件分隔符号

View File

@ -69,7 +69,7 @@ func (s *Server)parsePattern(pattern string) (domain, method, uri string, err er
// 如果带有hook参数表示是回调注册方法否则为普通路由执行方法。
func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... string) error {
// Web Server正字运行时无法动态注册路由方法
if s.status == gSERVER_STATUS_RUNNING {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("cannnot bind handler while server running")
}
var hookName string

View File

@ -14,7 +14,7 @@ import (
)
// 注意该方法是直接绑定函数的内存地址,执行的时候直接执行该方法,不会存在初始化新的控制器逻辑
func (s *Server)BindHandler(pattern string, handler HandlerFunc) error {
func (s *Server) BindHandler(pattern string, handler HandlerFunc) error {
return s.bindHandlerItem(pattern, &handlerItem{
ctype : nil,
fname : "",
@ -25,15 +25,15 @@ func (s *Server)BindHandler(pattern string, handler HandlerFunc) error {
// 绑定URI到操作函数/方法
// pattern的格式形如/user/list, put:/user, delete:/user, post:/user@johng.cn
// 支持RESTful的请求格式具体业务逻辑由绑定的处理方法来执行
func (s *Server)bindHandlerItem(pattern string, item *handlerItem) error {
if s.status == 1 {
func (s *Server) bindHandlerItem(pattern string, item *handlerItem) error {
if s.Status() == SERVER_STATUS_RUNNING {
return errors.New("server handlers cannot be changed while running")
}
return s.setHandler(pattern, item)
}
// 通过映射数组绑定URI到操作函数/方法
func (s *Server)bindHandlerByMap(m handlerMap) error {
func (s *Server) bindHandlerByMap(m handlerMap) error {
for p, h := range m {
if err := s.bindHandlerItem(p, h); err != nil {
return err