mirror of
https://gitee.com/johng/gf
synced 2026-07-07 06:15:15 +08:00
ghttp.Server热重启特性在同一进程多Web Server下的稳定性测试
This commit is contained in:
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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:
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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()
|
||||
}
|
||||
Reference in New Issue
Block a user