亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Golang > 正文

Golang學習之平滑重啟

2020-04-01 18:54:24
字體:
來源:轉載
供稿:網友

在上一篇博客介紹TOML配置的時候,講到了通過信號通知重載配置。我們在這一篇中介紹下如何的平滑重啟server。

與重載配置相同的是我們也需要通過信號來通知server重啟,但關鍵在于平滑重啟,如果只是簡單的重啟,只需要kill掉,然后再拉起即可。平滑重啟意味著server升級的時候可以不用停止業務。

我們先來看下Github上有沒有相應的庫解決這個問題,然后找到了如下三個庫:

  • facebookgo/grace - Graceful restart & zero downtime deploy for Go servers.
  • fvbock/endless - Zero downtime restarts for go servers (Drop in replacement for http.ListenAndServe)
  • jpillora/overseer - Monitorable, gracefully restarting, self-upgrading binaries in Go (golang)

我們分別來學習一下,下面只講解http server的重啟。

使用方式

我們來分別使用這三個庫來做平滑重啟的事情,之后來對比其優缺點。

這三個庫的官方都給了相應的例子,例子如下:

但三個庫官方的例子不太一致,我們來統一一下:

我們參考官方的例子分別來寫下用來對比的例子:

grace

package mainimport (  "time"  "net/http"  "github.com/facebookgo/grace/gracehttp")func main() {  gracehttp.Serve(    &http.Server{Addr: ":5001", Handler: newGraceHandler()},    &http.Server{Addr: ":5002", Handler: newGraceHandler()},  )}func newGraceHandler() http.Handler {  mux := http.NewServeMux()  mux.HandleFunc("/sleep", func(w http.ResponseWriter, r *http.Request) {    duration, err := time.ParseDuration(r.FormValue("duration"))    if err != nil {      http.Error(w, err.Error(), 400)      return    }    time.Sleep(duration)    w.Write([]byte("Hello World"))  })  return mux}

endless

package mainimport (  "log"  "net/http"  "os"  "sync"  "time"  "github.com/fvbock/endless"  "github.com/gorilla/mux")func handler(w http.ResponseWriter, r *http.Request) {  duration, err := time.ParseDuration(r.FormValue("duration"))  if err != nil {    http.Error(w, err.Error(), 400)    return  }  time.Sleep(duration)  w.Write([]byte("Hello World"))}func main() {  mux1 := mux.NewRouter()  mux1.HandleFunc("/sleep", handler)  w := sync.WaitGroup{}  w.Add(2)  go func() {    err := endless.ListenAndServe(":5003", mux1)    if err != nil {      log.Println(err)    }    log.Println("Server on 5003 stopped")    w.Done()  }()  go func() {    err := endless.ListenAndServe(":5004", mux1)    if err != nil {      log.Println(err)    }    log.Println("Server on 5004 stopped")    w.Done()  }()  w.Wait()  log.Println("All servers stopped. Exiting.")  os.Exit(0)}

overseer

package mainimport (  "fmt"  "net/http"  "time"  "github.com/jpillora/overseer")//see example.sh for the use-case// BuildID is compile-time variablevar BuildID = "0"//convert your 'main()' into a 'prog(state)'//'prog()' is run in a child processfunc prog(state overseer.State) {  fmt.Printf("app#%s (%s) listening.../n", BuildID, state.ID)  http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {    duration, err := time.ParseDuration(r.FormValue("duration"))    if err != nil {      http.Error(w, err.Error(), 400)      return    }    time.Sleep(duration)    w.Write([]byte("Hello World"))    fmt.Fprintf(w, "app#%s (%s) says hello/n", BuildID, state.ID)  }))  http.Serve(state.Listener, nil)  fmt.Printf("app#%s (%s) exiting.../n", BuildID, state.ID)}//then create another 'main' which runs the upgrades//'main()' is run in the initial processfunc main() {  overseer.Run(overseer.Config{    Program: prog,    Addresses: []string{":5005", ":5006"},    //Fetcher: &fetcher.File{Path: "my_app_next"},    Debug:  false, //display log of overseer actions  })}

對比

對比示例的操作步驟

  • 分別構建上面的示例,并記錄pid
  • 調用API,在其未返回時,修改內容(Hello World -> Hello Harry),重新構建。查看舊API是否返回舊的內容
  • 調用新API,查看返回的內容是否是新的內容
  • 查看當前運行的pid,是否與之前一致

下面給一下操作命令

# 第一次構建項目go build grace.go# 運行項目,這時就可以做內容修改了./grace &# 請求項目,60s后返回curl "http://127.0.0.1:5001/sleep?duration=60s" &# 再次構建項目,這里是新內容go build grace.go# 重啟,2096為pidkill -USR2 2096# 新API請求curl "http://127.0.0.1:5001/sleep?duration=1s"# 第一次構建項目go build endless.go# 運行項目,這時就可以做內容修改了./endless &# 請求項目,60s后返回curl "http://127.0.0.1:5003/sleep?duration=60s" &# 再次構建項目,這里是新內容go build endless.go# 重啟,22072為pidkill -1 22072# 新API請求curl "http://127.0.0.1:5003/sleep?duration=1s"# 第一次構建項目go build -ldflags '-X main.BuildID=1' overseer.go# 運行項目,這時就可以做內容修改了./overseer &# 請求項目,60s后返回curl "http://127.0.0.1:5005/sleep?duration=60s" &# 再次構建項目,這里是新內容,注意版本號不同了go build -ldflags '-X main.BuildID=2' overseer.go# 重啟,28300為主進程pidkill -USR2 28300# 新API請求curl http://127.0.0.1:5005/sleep?duration=1s

對比結果

 

示例 舊API返回值 新API返回值 舊pid 新pid 結論
grace Hello world Hello Harry 2096 3100 舊API不會斷掉,會執行原來的邏輯,pid會變化
endless Hello world Hello Harry 22072 22365 舊API不會斷掉,會執行原來的邏輯,pid會變化
overseer Hello world Hello Harry 28300 28300 舊API不會斷掉,會執行原來的邏輯,主進程pid不會變化

 

原理分析

可以看出grace和endless是比較像的。

  • 監聽信號
  • 收到信號時fork子進程(使用相同的啟動命令),將服務監聽的socket文件描述符傳遞給子進程
  • 子進程監聽父進程的socket,這個時候父進程和子進程都可以接收請求
  • 子進程啟動成功之后,父進程停止接收新的連接,等待舊連接處理完成(或超時)
  • 父進程退出,升級完成

overseer是不同的,主要是overseer加了一個主進程管理平滑重啟,子進程處理鏈接,能夠保持主進程pid不變。

如下圖表示的很形象

Golang,平滑重啟

自己實現

我們下面來嘗試自己實現下第一種處理,代碼如下,代碼來自《熱重啟golang服務器》:

package mainimport (  "context"  "errors"  "flag"  "log"  "net"  "net/http"  "os"  "os/exec"  "os/signal"  "syscall"  "time")var (  server  *http.Server  listener net.Listener  graceful = flag.Bool("graceful", false, "listen on fd open 3 (internal use only)"))func sleep(w http.ResponseWriter, r *http.Request) {  duration, err := time.ParseDuration(r.FormValue("duration"))  if err != nil {    http.Error(w, err.Error(), 400)    return  }  time.Sleep(duration)  w.Write([]byte("Hello World"))}func main() {  flag.Parse()  http.HandleFunc("/sleep", sleep)  server = &http.Server{Addr: ":5007"}  var err error  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.FileListener(f)  } else {    log.Print("main: Listening on a new file descriptor.")    listener, err = net.Listen("tcp", server.Addr)  }  if err != nil {    log.Fatalf("listener error: %v", err)  }  go func() {    // server.Shutdown() stops Serve() immediately, thus server.Serve() should not be in main goroutine    err = server.Serve(listener)    log.Printf("server.Serve err: %v/n", err)  }()  signalHandler()  log.Printf("signal end")}func reload() error {  tl, ok := listener.(*net.TCPListener)  if !ok {    return errors.New("listener is not tcp listener")  }  f, err := tl.File()  if err != nil {    return err  }  args := []string{"-graceful"}  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}  return cmd.Start()}func signalHandler() {  ch := make(chan os.Signal, 1)  signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR2)  for {    sig := <-ch    log.Printf("signal: %v", sig)    // timeout context for shutdown    ctx, _ := context.WithTimeout(context.Background(), 100*time.Second)    switch sig {    case syscall.SIGINT, syscall.SIGTERM:      // stop      log.Printf("stop")      signal.Stop(ch)      server.Shutdown(ctx)      log.Printf("graceful shutdown")      return    case syscall.SIGUSR2:      // reload      log.Printf("reload")      err := reload()      if err != nil {        log.Fatalf("graceful restart error: %v", err)      }      server.Shutdown(ctx)      log.Printf("graceful reload")      return    }  }}

代碼可參考:https://github.com/CraryPrimitiveMan/go-in-action/tree/master/ch4

關于這一部分,個人的理解也不是特別深入,如果又不正確的地方請大家指正。

參考文章

熱重啟golang服務器 

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲欧美中文日韩v在线观看| 午夜精品福利电影| 亚洲欧美国产精品久久久久久久| 亚洲影院色在线观看免费| 亚洲成年网站在线观看| 久久五月天综合| 一本大道香蕉久在线播放29| 性视频1819p久久| 人人爽久久涩噜噜噜网站| 国产精品直播网红| 青青精品视频播放| 国产精品直播网红| 久久频这里精品99香蕉| 欧洲日韩成人av| 日韩精品视频在线播放| 久久综合久久美利坚合众国| 欧美xxxx14xxxxx性爽| 欧美午夜激情视频| 国内精品久久久久伊人av| 一区二区三区视频免费在线观看| 亚洲女人天堂av| 中文字幕日韩av电影| 色yeye香蕉凹凸一区二区av| 韩剧1988在线观看免费完整版| 欧美一级片免费在线| 美女黄色丝袜一区| 一区二区欧美日韩视频| 欧美日本啪啪无遮挡网站| 成人福利在线观看| 亚洲欧美日本伦理| 欧美激情日韩图片| 国产精品黄色av| 成人深夜直播免费观看| 黄色一区二区三区| 国产精品成av人在线视午夜片| 亚洲香蕉在线观看| 亚洲护士老师的毛茸茸最新章节| 国产成人精品在线视频| 亚洲精品综合精品自拍| 色中色综合影院手机版在线观看| 国产精品久久久久久久一区探花| 91av在线看| 亚洲乱码国产乱码精品精| 久久av红桃一区二区小说| 国产97在线|日韩| 日韩网站在线观看| 欧美亚洲在线播放| 亚洲精品美女久久久久| 国产精品福利在线观看| 亚洲午夜久久久影院| 日韩经典中文字幕| 欧洲亚洲妇女av| 色一区av在线| 久久影视免费观看| 日韩国产高清污视频在线观看| 亚洲国产一区二区三区在线观看| 国产极品精品在线观看| 97在线看福利| 亚洲性日韩精品一区二区| 亚洲日本成人女熟在线观看| 久久精品视频免费播放| 午夜美女久久久久爽久久| 久久久久免费精品国产| 欧美与黑人午夜性猛交久久久| 久久久久久久久久亚洲| 久久久女女女女999久久| 亚洲欧美日韩中文视频| 尤物九九久久国产精品的分类| 国产在线拍偷自揄拍精品| 欧美午夜精品久久久久久人妖| 这里只有视频精品| 欧美午夜性色大片在线观看| 国产午夜精品一区二区三区| 亚洲欧洲激情在线| 欧美性猛交xxxx偷拍洗澡| 久久视频在线播放| 日韩欧美一区二区在线| 亚洲色图偷窥自拍| 91九色视频导航| 国产成人小视频在线观看| 人体精品一二三区| 国产精品久久久久秋霞鲁丝| 最近中文字幕mv在线一区二区三区四区| 亚洲小视频在线观看| 中文字幕av一区二区| 亚洲国语精品自产拍在线观看| 日韩免费在线视频| 久久精品国产综合| 亚洲直播在线一区| 精品国产欧美一区二区三区成人| 亚洲视频国产视频| 国产精品久久久久av免费| 亚洲精品v欧美精品v日韩精品| 欧美成人免费一级人片100| 国产女精品视频网站免费| 欧洲日本亚洲国产区| 国内偷自视频区视频综合| 精品日本高清在线播放| 国产精品视频久久久| 亚洲精品一区二区三区不| 欧美大片免费观看在线观看网站推荐| 欧美性xxxxx极品娇小| 97色在线视频观看| 欧美视频一二三| 成人午夜小视频| 欧美在线视频网| 92裸体在线视频网站| 国产精品aaa| 国产91精品不卡视频| 国产成人亚洲综合91| 久久久久久久久久久久久久久久久久av| 亚洲人成电影网站色xx| 日韩在线高清视频| 中文字幕欧美日韩va免费视频| 国产亚洲免费的视频看| 国产亚洲精品美女久久久| 亚洲电影天堂av| 91日韩在线视频| 亚洲亚裔videos黑人hd| 久久精品成人一区二区三区| 国产精品一久久香蕉国产线看观看| 国产精品亚洲аv天堂网| 日韩精品视频在线免费观看| 久久久久久久久久国产精品| 亚洲色图欧美制服丝袜另类第一页| 欧美激情在线一区| 久久人人爽亚洲精品天堂| www国产精品com| 国产视频丨精品|在线观看| 欧美国产精品日韩| 97在线视频免费看| 欧美亚洲激情视频| 久久久精品国产一区二区| 日韩精品在线观| 亚洲精品大尺度| 视频一区视频二区国产精品| 亚洲欧美日韩国产成人| 日本一欧美一欧美一亚洲视频| 亚洲精品中文字幕女同| 国产精品久久久久久久7电影| 日韩av在线导航| 久久99久久久久久久噜噜| 午夜欧美大片免费观看| 欧美成人sm免费视频| 欧美专区国产专区| 欧美在线欧美在线| 国产欧美一区二区三区视频| 国产精品第二页| www.99久久热国产日韩欧美.com| 亚洲国产精品久久| 亚洲精品小视频在线观看| 国产精品偷伦免费视频观看的| 久久精品国产精品| 国产精品99久久99久久久二8| 欧美性极品少妇精品网站| 91久久久久久国产精品| 国产精品美女主播| 国产欧美一区二区三区在线| 在线中文字幕日韩| 97国产精品人人爽人人做| 欧美日韩在线免费| 亚洲乱亚洲乱妇无码| 国产精品1区2区在线观看| 国产91精品黑色丝袜高跟鞋|