平滑升級udp研究

平滑升級的一般思路

  1. 發佈新的bin文件去覆蓋老的bin文件(如只需優雅重啓,可以跳過這一步)
  2. 發送一個(USR2)信號量,告訴正在運行的進程,進行重啓
  3. 正在運行的進程收到信號後,會以子進程的方式啓動新的bin文件
  4. 子進程啓動成功之後,老進程停止接收新的連接,等待舊連接處理完成(或超時)
  5. 父進程退出,升級完成

golang庫

tcp應該有很多成熟的方案,可參照facebook庫

注意點

平滑重啓有個特點:兩個相互獨立的進程存在同時 bind、listen 相同的 IP + 端口,對於各個操作系統及版本驗證如下:

  1. linux 內核 4.13.0
    設置參數SO_REUSEADDR、SO_REUSEPORT,可以bind、listen成功,新的連接由系統分配到某一個進程;
  2. linux 內核 2.6.32
    不支持SO_REUSEPORT,僅設置參數SO_REUSEADDR時,新進程 bind 失敗。經查資料linux 3.9及以後開始支持參數SO_REUSEPORT;

參考資料

  1. fork新進程的方式
  2. Go 語言實現優雅的服務器重啓
  3. 平滑重啓的系統設置
  4. http熱重啓
  5. golang平滑重啓分析

udp

這裏主要總結下udp碰到的坑

udp繼承套接字

	if *graceful {
		log.Print("main: Listening to existing file descriptor 3.")
		// cmd.ExtraFiles: If non-nil, entry i becomes file descriptor 3+i.
		// when we put socket FD at the first entry, it will always be 3(0+3)
		f := os.NewFile(3, "")
		listener, err = net.FilePacketConn(f)
		f.Close()
	} else {
		log.Print("main: Listening on a new file descriptor.")
		// listener, err = net.Listen("tcp", addr)
		listener, err = net.ListenPacket("udp", addr)
	}

udp啓動子進程繼承套接字

	tl, ok := listener.(*net.UDPConn)
	if !ok {
		return errors.New("listener is not tcp listener")
	}

	f, err := tl.File()
	if err != nil {
		return err
	}

	args := []string{"-graceful", "-seq", "1"}
	cmd := exec.Command(os.Args[0], args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	// put socket FD at the first entry
	cmd.ExtraFiles = []*os.File{f}
	cmd.Start()

由於udp無連接,作爲服務端,在讀取客戶端舊連接發送的數據包時,新舊進程會搶佔式讀取。對單個服務進程來說,有丟包的假象
如果你在其上定製了一層維持連接的協議層,可能由於丟包假象,無法維持連接。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章