golang中的web服务平滑重启

新进来的请求怎么办?

  • 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("父进程退出")
}

猜你喜欢

转载自blog.csdn.net/ma2595162349/article/details/113003709