🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 新进来的请求怎么办? * fork一个子进程,继承父进程的监听socket * 子进程启动成功之后,接收新的连接 * 父进程停止接收新的连接,等已有的请求处理完毕,退出 * 优雅重启成功 # 平滑升级 子进程如何继承父进程的文件句柄? * 通过os.Cmd对象中的ExtraFiles参数进行传递 # 子进程如何继承父进程的文件句柄 * 通过os.Cmd对象中的ExtraFiles参数进行传递 * 文件句柄继承实例分析 # web服务器平滑升级 * 使用go1.8版本的shutdown方法进行优雅关闭 * 使用socket继承实现,子进程接管父进程的监听socket # 文件句柄继承实例分析 ~~~ package main import ( "flag" "fmt" "os" "os/exec" "time" ) var ( child *bool ) func init() { child = flag.Bool("child", false, "继承于父进程(internal use only)") flag.Parse() } func readFromParent() { //fd = 0,标准输出 //fd = 1,标准输入 //fd = 2,标准错误输出 //fd = 3, ==> ExtraFiles[0] //fd = 4, ==> ExtraFiles[1] //第一个参数文件句柄的下标,就是ExtraFiles[0], 第二个参数名字可以随便取 f := os.NewFile(3, "") count := 0 for { //格式化字符串 str := fmt.Sprintf("hello, i'child process, write: %d line \n", count) count++ //写入到这个文件 _, err := f.WriteString(str) if err != nil { fmt.Printf("write string failed, err: %v\n", err) time.Sleep(time.Second) continue } //每一秒写下文件 time.Sleep(time.Second) } } //启动子进程 func startChild(file *os.File) { args := []string{"-child"} //os.Args[0]是文件路径,带上-child选项 cmd := exec.Command(os.Args[0], args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr //放socket fd在第一个entry,只要把父进程传递过来的放在这里 cmd.ExtraFiles = []*os.File{file} //到main函数 err := cmd.Start() if err != nil { fmt.Printf("start child failed, err: %v\n", err.Error()) return } } func main() { //表示已经是一个子进程了 if child != nil && *child == true { fmt.Printf("继承于父进程的文件句柄\n") //子进程 readFromParent() return } //父进程的逻辑,打开文件句柄 file, err := os.OpenFile("./test.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0755) if err != nil { fmt.Printf("open file failed, err:%v\n", err) return } //启动一个子进程,把文件句柄给子进程 startChild(file) fmt.Println("父进程退出") } ~~~