ghttp.Server热重启特性在同一进程多Web Server下的稳定性测试

This commit is contained in:
John
2018-05-14 17:35:54 +08:00
parent 327c22379e
commit c739dd0ed6
6 changed files with 73 additions and 66 deletions

View File

@ -8,7 +8,6 @@ package ghttp
import (
"os"
"net"
"sync"
"errors"
"strings"
@ -241,9 +240,7 @@ func (s *Server) startServer(fdMap listenerFdMap) {
}
httpsEnabled = len(s.config.HTTPSAddr) > 0
var array []string
var isFd bool
if v, ok := fdMap["https"]; ok && len(v) > 0 {
isFd = true
array = strings.Split(v, ",")
} else {
array = strings.Split(s.config.HTTPSAddr, ",")
@ -253,18 +250,25 @@ func (s *Server) startServer(fdMap listenerFdMap) {
continue
}
go func(addrItem string) {
// windows系统不支持文件描述符传递socket通信平滑交接因此只能完整重启
if isFd && runtime.GOOS != "windows" {
tArray := strings.Split(addrItem, "#")
server = s.newGracefulServer(tArray[0], gconv.Int(tArray[1]))
fd := 0
addr := addrItem
array := strings.Split(addrItem, "#")
if len(array) > 1 {
addr = array[0]
// windows系统不支持文件描述符传递socket通信平滑交接因此只能完整重启
if runtime.GOOS != "windows" {
fd = gconv.Int(array[1])
}
}
if fd > 0 {
server = s.newGracefulServer(addr, fd)
} else {
server = s.newGracefulServer(addrItem)
server = s.newGracefulServer(addr)
}
s.servers = append(s.servers, server)
if err := server.ListenAndServeTLS(s.config.HTTPSCertPath, s.config.HTTPSKeyPath); err != nil {
// 如果非关闭错误,那么提示报错,否则认为是正常的服务关闭操作
if !strings.EqualFold(http.ErrServerClosed.Error(), err.Error()) {
s.servers = s.servers[0 : len(s.servers) - 1]
glog.Error(err)
}
}
@ -279,9 +283,7 @@ func (s *Server) startServer(fdMap listenerFdMap) {
s.config.Addr = gDEFAULT_HTTP_ADDR
}
var array []string
var isFd bool
if v, ok := fdMap["http"]; ok && len(v) > 0 {
isFd = true
array = strings.Split(v, ",")
} else {
array = strings.Split(s.config.Addr, ",")
@ -291,18 +293,25 @@ func (s *Server) startServer(fdMap listenerFdMap) {
continue
}
go func(addrItem string) {
// windows系统不支持文件描述符传递socket通信平滑交接因此只能完整重启
if isFd && runtime.GOOS != "windows" {
tArray := strings.Split(addrItem, "#")
server = s.newGracefulServer(tArray[0], gconv.Int(tArray[1]))
fd := 0
addr := addrItem
array := strings.Split(addrItem, "#")
if len(array) > 1 {
addr = array[0]
// windows系统不支持文件描述符传递socket通信平滑交接因此只能完整重启
if runtime.GOOS != "windows" {
fd = gconv.Int(array[1])
}
}
if fd > 0 {
server = s.newGracefulServer(addr, fd)
} else {
server = s.newGracefulServer(addrItem)
server = s.newGracefulServer(addr)
}
s.servers = append(s.servers, server)
if err := server.ListenAndServe(); err != nil {
// 如果非关闭错误,那么提示报错,否则认为是正常的服务关闭操作
if !strings.EqualFold(http.ErrServerClosed.Error(), err.Error()) {
s.servers = s.servers[0 : len(s.servers) - 1]
glog.Error(err)
}
}
@ -325,34 +334,26 @@ func (s *Server) Shutdown() {
// 获取当前监听的文件描述符信息构造成map返回
func (s *Server) getListenerFdMap() map[string]string {
m := map[string]string {
"http" : "",
"https" : "",
"http" : "",
}
// s.servers是从HTTPS到HTTP优先级遍历解析的时候也应当按照这个顺序读取fd
for _, v := range s.servers {
//switch l := v.listener.(type) {
//case *net.TCPListener:
//default:
//
// tls.NewListener(ln, config)
//
//}
if f, e := v.listener.(*net.TCPListener).File(); e == nil {
str := v.addr + "#" + gconv.String(f.Fd()) + ","
if v.isHttps {
m["https"] += str
} else {
m["http"] += str
}
str := v.addr + "#" + gconv.String(v.Fd()) + ","
if v.isHttps {
m["https"] += str
} else {
glog.Errorfln("failed to get listener file: %v", e)
m["http"] += str
}
}
// 去掉末尾的","号
if len(m["https"]) > 0 {
m["https"] = m["https"][0 : len(m["https"]) - 1]
}
if len(m["http"]) > 0 {
m["http"] = m["http"][0 : len(m["http"]) - 1]
}
if len(m["https"]) > 0 {
m["https"] = m["https"][0 : len(m["https"]) - 1]
}
return m
}

View File

@ -52,8 +52,12 @@ func onCommChildRestart(pid int, data []byte) {
for _, item := range strings.Split(fdv, ",") {
array := strings.Split(item, "#")
fd := uintptr(gconv.Uint(array[1]))
s += fmt.Sprintf("%s#%d,", array[0], 3 + len(p.ExtraFiles))
p.ExtraFiles = append(p.ExtraFiles, os.NewFile(fd, ""))
if fd > 0 {
s += fmt.Sprintf("%s#%d,", array[0], 3 + len(p.ExtraFiles))
p.ExtraFiles = append(p.ExtraFiles, os.NewFile(fd, ""))
} else {
s += fmt.Sprintf("%s#%d,", array[0], 0)
}
}
sfm[name][fdk] = strings.TrimRight(s, ",")
}
@ -75,7 +79,6 @@ func onCommChildShutdown(pid int, data []byte) {
if runtime.GOOS != "windows" {
shutdownWebServers()
}
glog.Printfln("%d: shutdown done", gproc.Pid())
}
// 更新上一次主进程主动与子进程通信的时间
@ -94,13 +97,12 @@ func handleChildProcessHeartbeat() {
// 子进程有时会无法退出(僵尸?)这里直接使用exit而不是return
//glog.Printfln("%d: %d - %d > %d", gproc.Pid(), int(gtime.Millisecond()), lastHeartbeatTime.Val(), gPROC_HEARTBEAT_TIMEOUT)
//glog.Printfln("%d: heartbeat timeout, exit", gproc.Pid())
glog.Printfln("%d: exit", gproc.Pid())
glog.Printfln("%d: waiting %dms for shutdown timeout, exit", gproc.Pid(), gPROC_HEARTBEAT_TIMEOUT)
os.Exit(0)
}
// 未开启心跳检测的闲置超过一定时间则主动关闭
if !checkHeartbeat.Val() && gproc.Uptime() > gPROC_CHILD_MAX_IDLE_TIME {
//glog.Printfln("%d: max idle time exceeded, exit", gproc.Pid())
glog.Printfln("%d: exit", gproc.Pid())
glog.Printfln("%d: max idle time %dms exceeded, exit", gproc.Pid(), gPROC_CHILD_MAX_IDLE_TIME)
os.Exit(0)
}
}

View File

@ -30,16 +30,18 @@ func handleProcessSignal() {
for {
sig = <- procSignalChan
switch sig {
// 进程终止,停止所有子进程运行
case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGHUP, syscall.SIGTERM:
sendProcessMsg(gproc.Pid(), gMSG_SHUTDOWN, nil)
return
// 进程终止,停止所有子进程运行
case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGHUP, syscall.SIGTERM:
sendProcessMsg(gproc.Pid(), gMSG_SHUTDOWN, nil)
// 强制性kill掉所有子进程
procManager.KillAll()
return
// 用户信号,重启服务
case syscall.SIGUSR1:
sendProcessMsg(gproc.Pid(), gMSG_RESTART, nil)
case syscall.SIGUSR1:
sendProcessMsg(gproc.Pid(), gMSG_RESTART, nil)
default:
default:
}
}
}

View File

@ -71,10 +71,11 @@ func (s *gracefulServer) ListenAndServe() error {
// 获得文件描述符
func (s *gracefulServer) Fd() uintptr {
file, err := s.listener.(*net.TCPListener).File()
//file, err := s.rawln.File()
if err == nil {
return file.Fd()
if s.listener != nil {
file, err := s.listener.(*net.TCPListener).File()
if err == nil {
return file.Fd()
}
}
return 0
}
@ -155,9 +156,9 @@ func (s *gracefulServer) getNetListener(addr string) (net.Listener, error) {
// 执行请求优雅关闭
func (s *gracefulServer) shutdown() {
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)
//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)
//glog.Printfln("%d: %s server [%s] shutdown smoothly", gproc.Pid(), s.getProto(), s.addr)
s.shutdownChan <- true
}
}

View File

@ -11,9 +11,10 @@ func main() {
s1 := g.Server("s1")
s1.EnableAdmin()
s1.BindHandler("/", func(r *ghttp.Request) {
r.Response.Write("before time, pid:", gproc.Pid())
pid := gproc.Pid()
r.Response.Writeln("before restart, pid:", pid)
time.Sleep(10*time.Second)
r.Response.Write("after time, pid:", gproc.Pid())
r.Response.Writeln("after restart, pid:", gproc.Pid())
})
s1.BindHandler("/pid", func(r *ghttp.Request) {
r.Response.Write(gproc.Pid())

View File

@ -3,21 +3,21 @@ package main
import (
"gitee.com/johng/gf/g"
"gitee.com/johng/gf/g/net/ghttp"
"gitee.com/johng/gf/g/os/gproc"
"time"
)
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request){
r.Response.Writeln("hello")
s.BindHandler("/sleep", func(r *ghttp.Request){
r.Response.Writeln(gproc.Pid())
time.Sleep(10*time.Second)
r.Response.Writeln(gproc.Pid())
})
s.BindHandler("/restart", func(r *ghttp.Request){
r.Response.Writeln("restart server")
r.Server.Restart()
})
s.BindHandler("/shutdown", func(r *ghttp.Request){
r.Response.Writeln("shutdown server")
r.Server.Shutdown()
s.BindHandler("/pid", func(r *ghttp.Request){
r.Response.Writeln(gproc.Pid())
})
s.EnableAdmin()
s.SetPort(8199)
s.Run()
}