日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

exec go 重启_如何用 Go 实现热重启

發布時間:2025/3/11 编程问答 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 exec go 重启_如何用 Go 实现热重启 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

熱重啟

熱重啟(Zero Downtime),指新老進程無縫切換,在替換過程中可保持對 client 的服務。

原理

父進程監聽重啟信號

在收到重啟信號后,父進程調用 fork ,同時傳遞 socket 描述符給子進程

子進程接收并監聽父進程傳遞的 socket 描述符

在子進程啟動成功之后,父進程停止接收新連接,同時等待舊連接處理完成(或超時)

父進程退出,熱重啟完成

實現

package main

import (

"context"

"errors"

"flag"

"log"

"net"

"net/http"

"os"

"os/exec"

"os/signal"

"syscall"

"time"

)

var (

server *http.Server

listener net.Listener = nil

graceful = flag.Bool("graceful", false, "listen on fd open 3 (internal use only)")

message = flag.String("message", "Hello World", "message to send")

)

func handler(w http.ResponseWriter, r *http.Request) {

time.Sleep(5 * time.Second)

w.Write([]byte(*message))

}

func main() {

var err error

// 解析參數

flag.Parse()

http.HandleFunc("/test", handler)

server = &http.Server{Addr: ":3000"}

// 設置監聽器的監聽對象(新建的或已存在的 socket 描述符)

if *graceful {

// 子進程監聽父進程傳遞的 socket 描述符

log.Println("listening on the existing file descriptor 3")

// 子進程的 0, 1, 2 是預留給標準輸入、標準輸出、錯誤輸出,故傳遞的 socket 描述符

// 應放在子進程的 3

f := os.NewFile(3, "")

listener, err = net.FileListener(f)

} else {

// 父進程監聽新建的 socket 描述符

log.Println("listening on a new file descriptor")

listener, err = net.Listen("tcp", server.Addr)

}

if err != nil {

log.Fatalf("listener error: %v", err)

}

go func() {

err = server.Serve(listener)

log.Printf("server.Serve err: %v\n", err)

}()

// 監聽信號

handleSignal()

log.Println("signal end")

}

func handleSignal() {

ch := make(chan os.Signal, 1)

// 監聽信號

signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR2)

for {

sig :=

log.Printf("signal receive: %v\n", sig)

ctx, _ := context.WithTimeout(context.Background(), 20*time.Second)

switch sig {

case syscall.SIGINT, syscall.SIGTERM: // 終止進程執行

log.Println("shutdown")

signal.Stop(ch)

server.Shutdown(ctx)

log.Println("graceful shutdown")

return

case syscall.SIGUSR2: // 進程熱重啟

log.Println("reload")

err := reload() // 執行熱重啟函數

if err != nil {

log.Fatalf("graceful reload error: %v", err)

}

server.Shutdown(ctx)

log.Println("graceful reload")

return

}

}

}

func reload() error {

tl, ok := listener.(*net.TCPListener)

if !ok {

return errors.New("listener is not tcp listener")

}

// 獲取 socket 描述符

f, err := tl.File()

if err != nil {

return err

}

// 設置傳遞給子進程的參數(包含 socket 描述符)

args := []string{"-graceful"}

cmd := exec.Command(os.Args[0], args...)

cmd.Stdout = os.Stdout // 標準輸出

cmd.Stderr = os.Stderr // 錯誤輸出

cmd.ExtraFiles = []*os.File{f} // 文件描述符

// 新建并執行子進程

return cmd.Start()

}

我們在父進程執行 cmd.ExtraFiles = []*os.File{f} 來傳遞 socket 描述符給子進程,子進程通過執行 f := os.NewFile(3, "") 來獲取該描述符。值得注意的是,子進程的 0 、1 和 2 分別預留給標準輸入、標準輸出和錯誤輸出,所以父進程傳遞的 socket 描述符在子進程的順序是從 3 開始。

測試

編譯上述程序為 main ,執行 ./main -message "Graceful Reload" ,訪問 http://localhost:3000/test ,等待 5 秒后,我們可以看到 Graceful Reload 的響應。

通過執行 kill -USR2 [PID] ,我們即可進行 Graceful Reload 的測試。

通過執行 kill -INT [PID] ,我們即可進行 Graceful Shutdown 的測試。

參考資料

有疑問加站長微信聯系(非本文作者)

總結

以上是生活随笔為你收集整理的exec go 重启_如何用 Go 实现热重启的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。