From 327c22379e5b85c800765a7fcc9047f26f2fcf79 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 13 May 2018 22:00:10 +0800 Subject: [PATCH] =?UTF-8?q?ghttp.Server=E7=83=AD=E9=87=8D=E5=90=AF?= =?UTF-8?q?=E7=89=B9=E6=80=A7=E6=B5=8B=E8=AF=95=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/net/ghttp/ghttp_server.go | 26 ++++++++++++++++--- g/net/ghttp/ghttp_server_comm.go | 4 +-- g/net/ghttp/ghttp_server_comm_child.go | 7 +++-- g/net/ghttp/ghttp_server_comm_main.go | 2 +- g/net/ghttp/ghttp_server_graceful.go | 23 +++++++++++++++- g/os/gproc/gproc_comm.go | 14 +++++++--- .../hot_restart/multi_port_and_server.go | 22 +++++++--------- geg/net/ghttp/https/https_http.go | 5 ++-- geg/other/test.go | 3 +++ 9 files changed, 80 insertions(+), 26 deletions(-) diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go index 64cfc481a..8a88a630c 100644 --- a/g/net/ghttp/ghttp_server.go +++ b/g/net/ghttp/ghttp_server.go @@ -225,16 +225,21 @@ func Wait() { // 开启底层Web Server执行 func (s *Server) startServer(fdMap listenerFdMap) { // 开始执行底层Web Server创建,端口监听 - var server *gracefulServer + var server *gracefulServer + var httpsEnabled bool if len(s.config.HTTPSCertPath) > 0 && len(s.config.HTTPSKeyPath) > 0 { + // ================ // HTTPS + // ================ if len(s.config.HTTPSAddr) == 0 { if len(s.config.Addr) > 0 { s.config.HTTPSAddr = s.config.Addr + s.config.Addr = "" } else { s.config.HTTPSAddr = gDEFAULT_HTTPS_ADDR } } + httpsEnabled = len(s.config.HTTPSAddr) > 0 var array []string var isFd bool if v, ok := fdMap["https"]; ok && len(v) > 0 { @@ -243,8 +248,10 @@ func (s *Server) startServer(fdMap listenerFdMap) { } else { array = strings.Split(s.config.HTTPSAddr, ",") } - for _, v := range array { + if len(v) == 0 { + continue + } go func(addrItem string) { // windows系统不支持文件描述符传递socket通信平滑交接,因此只能完整重启 if isFd && runtime.GOOS != "windows" { @@ -264,8 +271,11 @@ func (s *Server) startServer(fdMap listenerFdMap) { }(v) } } + // ================ // HTTP - if len(s.config.Addr) == 0 { + // ================ + // 当HTTPS服务未启用时,默认HTTP地址才会生效 + if !httpsEnabled && len(s.config.Addr) == 0 { s.config.Addr = gDEFAULT_HTTP_ADDR } var array []string @@ -277,6 +287,9 @@ func (s *Server) startServer(fdMap listenerFdMap) { array = strings.Split(s.config.Addr, ",") } for _, v := range array { + if len(v) == 0 { + continue + } go func(addrItem string) { // windows系统不支持文件描述符传递socket通信平滑交接,因此只能完整重启 if isFd && runtime.GOOS != "windows" { @@ -316,6 +329,13 @@ func (s *Server) getListenerFdMap() map[string]string { "https" : "", } 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 { diff --git a/g/net/ghttp/ghttp_server_comm.go b/g/net/ghttp/ghttp_server_comm.go index a2fa3b527..0203a6892 100644 --- a/g/net/ghttp/ghttp_server_comm.go +++ b/g/net/ghttp/ghttp_server_comm.go @@ -26,9 +26,9 @@ const ( gMSG_HEARTBEAT = 60 gPROC_HEARTBEAT_INTERVAL = 1000 // (毫秒)进程间心跳间隔 - gPROC_HEARTBEAT_TIMEOUT = 5000 // (毫秒)进程间心跳超时时间,如果子进程在这段内没有接收到任何心跳,那么自动退出,防止可能出现的僵尸子进程 + gPROC_HEARTBEAT_TIMEOUT = 3000 // (毫秒)进程间心跳超时时间,如果子进程在这段内没有接收到任何心跳,那么自动退出,防止可能出现的僵尸子进程 gPROC_MULTI_CHILD_CLEAR_INTERVAL = 1000 // (毫秒)检测间隔,当存在多个子进程时(往往是重启间隔非常短且频繁造成),需要进行清理,最终留下一个最新的子进程 - gPROC_MULTI_CHILD_CLEAR_MIN_EXPIRE = 5000 // (毫秒)当多个子进程存在时,允许子进程进程至少运行的最小时间,超过该时间则清理 + gPROC_MULTI_CHILD_CLEAR_MIN_EXPIRE = 3000 // (毫秒)当多个子进程存在时,允许子进程进程至少运行的最小时间,超过该时间则清理 ) // 进程信号量监听消息队列 diff --git a/g/net/ghttp/ghttp_server_comm_child.go b/g/net/ghttp/ghttp_server_comm_child.go index fb0a5dfec..d0744ffd1 100644 --- a/g/net/ghttp/ghttp_server_comm_child.go +++ b/g/net/ghttp/ghttp_server_comm_child.go @@ -39,7 +39,7 @@ func onCommChildRestart(pid int, data []byte) { p := procManager.NewProcess(os.Args[0], os.Args, os.Environ()) // windows系统无法进行文件描述符操作,只能重启进程 if runtime.GOOS == "windows" { - // windows下使用shutdownWebServers会造成协程阻塞,这里直接使用close强制关闭 + // windows下使用shutdown会造成协程阻塞,这里直接使用close强制关闭 closeWebServers() } else { // 创建新的服务进程,子进程自动从父进程复制文件描述来监听同样的端口 @@ -65,7 +65,7 @@ func onCommChildRestart(pid int, data []byte) { if newPid, err := p.Start(); err == nil { sendProcessMsg(newPid, gMSG_START, buffer) } else { - glog.Errorfln("%d: fork process failed, error:%s", err.Error()) + glog.Errorfln("%d: fork process failed, error:%s, %s", gproc.Pid(), err.Error(), string(buffer)) } } @@ -75,6 +75,7 @@ func onCommChildShutdown(pid int, data []byte) { if runtime.GOOS != "windows" { shutdownWebServers() } + glog.Printfln("%d: shutdown done", gproc.Pid()) } // 更新上一次主进程主动与子进程通信的时间 @@ -93,11 +94,13 @@ 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()) 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()) os.Exit(0) } } diff --git a/g/net/ghttp/ghttp_server_comm_main.go b/g/net/ghttp/ghttp_server_comm_main.go index ebabf2e01..c92f9a880 100644 --- a/g/net/ghttp/ghttp_server_comm_main.go +++ b/g/net/ghttp/ghttp_server_comm_main.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://gitee.com/johng/gf. // Web Server进程间通信 - 主进程. -// 管理子进程按照规则听话玩,不听话有一百种方法让子进程在本地混不下去. +// 管理子进程按照规则听话玩,不然有一百种方法让子进程在本地混不下去. package ghttp diff --git a/g/net/ghttp/ghttp_server_graceful.go b/g/net/ghttp/ghttp_server_graceful.go index cf7a3cabc..6c1387f83 100644 --- a/g/net/ghttp/ghttp_server_graceful.go +++ b/g/net/ghttp/ghttp_server_graceful.go @@ -22,7 +22,8 @@ type gracefulServer struct { fd uintptr addr string httpServer *http.Server - listener net.Listener + rawln *net.TCPListener // 原始listener + listener net.Listener // 接口化封装的listener isHttps bool shutdownChan chan bool } @@ -59,10 +60,25 @@ func (s *gracefulServer) ListenAndServe() error { if err != nil { return err } + //file, err := ln.(*net.TCPListener).File() + //if err != nil { + // return err + //} + //s.fd = file.Fd() s.listener = ln return s.doServe() } +// 获得文件描述符 +func (s *gracefulServer) Fd() uintptr { + file, err := s.listener.(*net.TCPListener).File() + //file, err := s.rawln.File() + if err == nil { + return file.Fd() + } + return 0 +} + // 设置自定义fd func (s *gracefulServer) setFd(fd int) { s.fd = uintptr(fd) @@ -88,6 +104,11 @@ func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string) error { if err != nil { return err } + //file, err := ln.(*net.TCPListener).File() + //if err != nil { + // return err + //} + //s.fd = file.Fd() s.listener = tls.NewListener(ln, config) s.isHttps = true return s.doServe() diff --git a/g/os/gproc/gproc_comm.go b/g/os/gproc/gproc_comm.go index 518f407d9..2e9a101c5 100644 --- a/g/os/gproc/gproc_comm.go +++ b/g/os/gproc/gproc_comm.go @@ -39,8 +39,11 @@ type Msg struct { func init() { path := getCommFilePath(os.Getpid()) if !gfile.Exists(path) { - // 检测存在性 - if err := gfile.Create(path); err != nil { + // 判断是否需要创建通信文件 + commLocker.Lock() + err := gfile.Create(path) + commLocker.UnLock() + if err != nil { glog.Error(err) os.Exit(1) } @@ -50,9 +53,14 @@ func init() { glog.Errorfln("%s is not writable for gproc", path) os.Exit(1) } - // 初始化时读取已有数据(文件修改时间在10秒以内) if gtime.Second() - gfile.MTime(path) < 10 { + // 初始化时读取已有数据(文件修改时间在10秒以内) checkCommBuffer(path) + } else { + // 否则清空旧的数据内容 + commLocker.Lock() + os.Truncate(path, 0) + commLocker.UnLock() } // 文件事件监听,如果通信数据文件有任何变化,读取文件并添加到消息队列 err := gfsnotify.Add(path, func(event *gfsnotify.Event) { diff --git a/geg/net/ghttp/hot_restart/multi_port_and_server.go b/geg/net/ghttp/hot_restart/multi_port_and_server.go index 0dd6e39d9..b0c9cadad 100644 --- a/geg/net/ghttp/hot_restart/multi_port_and_server.go +++ b/geg/net/ghttp/hot_restart/multi_port_and_server.go @@ -3,28 +3,26 @@ package main import ( "gitee.com/johng/gf/g" "gitee.com/johng/gf/g/net/ghttp" + "time" + "gitee.com/johng/gf/g/os/gproc" ) func main() { s1 := g.Server("s1") - s1.BindHandler("/", func(r *ghttp.Request){ - r.Response.Writeln("hello s1") + s1.EnableAdmin() + s1.BindHandler("/", func(r *ghttp.Request) { + r.Response.Write("before time, pid:", gproc.Pid()) + time.Sleep(10*time.Second) + r.Response.Write("after time, pid:", gproc.Pid()) }) - s1.BindHandler("/restart", func(r *ghttp.Request){ - r.Response.Writeln("restart server") - r.Server.Restart() - }) - s1.BindHandler("/shutdown", func(r *ghttp.Request){ - r.Response.Writeln("shutdown server") - r.Server.Shutdown() + s1.BindHandler("/pid", func(r *ghttp.Request) { + r.Response.Write(gproc.Pid()) }) s1.SetPort(8199, 8200) s1.Start() s2 := g.Server("s2") - s2.BindHandler("/", func(r *ghttp.Request){ - r.Response.Writeln("hello s2") - }) + s2.EnableAdmin() s2.SetPort(8300, 8080) s2.Start() diff --git a/geg/net/ghttp/https/https_http.go b/geg/net/ghttp/https/https_http.go index 2ab74b08b..4440453d0 100644 --- a/geg/net/ghttp/https/https_http.go +++ b/geg/net/ghttp/https/https_http.go @@ -6,11 +6,12 @@ import ( func main() { s := ghttp.GetServer() + s.EnableAdmin() s.BindHandler("/", func(r *ghttp.Request){ r.Response.Writeln("您可以同时通过HTTP和HTTPS方式看到该内容!") }) s.EnableHTTPS("/home/john/temp/server.crt", "/home/john/temp/server.key") - s.SetHTTPSPort(443) - s.SetPort(80) + s.SetHTTPSPort(8198) + s.SetPort(8199) s.Run() } \ No newline at end of file diff --git a/geg/other/test.go b/geg/other/test.go index 2af9ff714..19b906338 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -6,6 +6,7 @@ import ( "gitee.com/johng/gf/g/os/gtime" "gitee.com/johng/gf/g/encoding/gbinary" "gitee.com/johng/gf/g/os/gproc" + "strings" ) // 数据解包,防止黏包 @@ -43,6 +44,8 @@ func checksum(buffer []byte) uint32 { } func main(){ + fmt.Println(len(strings.Split("", ","))) + return b := gfile.GetBinContents("/tmp/gproc/30588") for _, msg := range bufferToMsgs(b) { fmt.Println(msg.Pid)