亚洲综合原千岁中文字幕_国产精品99久久久久久久vr_无码人妻aⅴ一区二区三区浪潮_成人h动漫精品一区二区三

  • <i id="mzwzx"><dfn id="mzwzx"></dfn></i>
    主頁 > 知識庫 > Golang學習之平滑重啟

    Golang學習之平滑重啟

    熱門標簽:江西轉化率高的羿智云外呼系統 中國地圖標注省會高清 高德地圖標注口訣 廣州呼叫中心外呼系統 地圖標注的汽車標 南通如皋申請開通400電話 浙江高速公路地圖標注 學海導航地圖標注 西部云谷一期地圖標注

    在上一篇博客介紹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例子 https://github.com/facebookgo/grace/blob/master/gracedemo/demo.go
    • endless例子 https://github.com/fvbock/endless/tree/master/examples
    • overseer例子 https://github.com/jpillora/overseer/tree/master/example

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

    grace

    package main
    
    import (
      "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 main
    
    import (
      "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 main
    
    import (
      "fmt"
      "net/http"
      "time"
    
      "github.com/jpillora/overseer"
    )
    
    //see example.sh for the use-case
    
    // BuildID is compile-time variable
    var BuildID = "0"
    
    //convert your 'main()' into a 'prog(state)'
    //'prog()' is run in a child process
    func 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 process
    func 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為pid
    kill -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為pid
    kill -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為主進程pid
    kill -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服務器》:

    package main
    import (
      "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服務器

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

    您可能感興趣的文章:
    • golang flag簡單用法
    • Golang中定時器的陷阱詳解
    • 在golang中操作mysql數據庫的實現代碼
    • golang設置http response響應頭與填坑記錄
    • 詳解Golang實現http重定向https的方式
    • Golang編譯器介紹

    標簽:吐魯番 德宏 常州 貴州 曲靖 東營 保定 許昌

    巨人網絡通訊聲明:本文標題《Golang學習之平滑重啟》,本文關鍵詞  Golang,學,習之,平滑,重啟,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《Golang學習之平滑重啟》相關的同類信息!
  • 本頁收集關于Golang學習之平滑重啟的相關信息資訊供網民參考!
  • 推薦文章
    尤物视频网站在线观看| a级精品九九九大片免费看| 欧美a级成人淫片免费看| 精品国产一区二区三区久久久蜜臀| 四虎影视精品永久免费网站| 久草免费在线视频| 国产美女在线观看| 日韩专区第一页| 欧美国产日韩在线| 日本久久久久久久 97久久精品一区二区三区 狠狠色噜噜狠狠狠狠97 日日干综合 五月天婷婷在线观看高清 九色福利视频 | 精品视频免费看| 亚洲第一色在线| 欧美日本韩国| 国产成a人片在线观看视频| 国产一区二区精品在线观看| 韩国三级香港三级日本三级| 色综合久久天天综合观看| 九九免费精品视频| 日本伦理片网站| 日本免费乱理伦片在线观看2018| 中文字幕97| 国产成人精品影视| 欧美大片a一级毛片视频| 深夜做爰性大片中文| 亚洲天堂免费观看| 日本久久久久久久 97久久精品一区二区三区 狠狠色噜噜狠狠狠狠97 日日干综合 五月天婷婷在线观看高清 九色福利视频 | 国产视频网站在线观看| 国产91精品一区| 日日日夜夜操| 欧美夜夜骑 青草视频在线观看完整版 久久精品99无色码中文字幕 欧美日韩一区二区在线观看视频 欧美中文字幕在线视频 www.99精品 香蕉视频久久 | 久久成人性色生活片| 欧美日本韩国| 91麻豆精品国产综合久久久| 久草免费资源| 欧美a级成人淫片免费看| 超级乱淫黄漫画免费| 国产网站免费视频| 日本特黄特黄aaaaa大片| 99热精品一区| 你懂的福利视频| 91麻豆精品国产片在线观看| 欧美日本二区| 日本免费乱理伦片在线观看2018| 精品久久久久久综合网| 免费一级片网站| 国产美女在线观看| 欧美1卡一卡二卡三新区| 美国一区二区三区| 国产欧美精品| 欧美激情一区二区三区在线| 日韩av成人| 久久精品欧美一区二区| 99久久精品国产高清一区二区| 国产一区二区精品久久91| 成人高清视频免费观看| 亚欧乱色一区二区三区| 在线观看成人网 | a级毛片免费全部播放| 欧美激情一区二区三区在线播放| 99色视频在线| 天天做日日爱夜夜爽| 欧美18性精品| 深夜做爰性大片中文| 亚洲第一色在线| 黄色免费三级| 九九久久99综合一区二区| 一本伊大人香蕉高清在线观看| 日本在线不卡视频| 日韩专区一区| 韩国毛片 免费| 美女被草网站| 九九久久国产精品| 麻豆系列 在线视频| 亚欧乱色一区二区三区| 国产一区二区精品| 午夜在线影院| 国产伦精品一区二区三区在线观看| 国产激情一区二区三区| 麻豆午夜视频| 日本久久久久久久 97久久精品一区二区三区 狠狠色噜噜狠狠狠狠97 日日干综合 五月天婷婷在线观看高清 九色福利视频 | 国产极品白嫩美女在线观看看| 国产a视频| 成人高清护士在线播放| 日本在线www| 九九精品影院| 国产伦理精品| 中文字幕一区二区三区 精品| 亚洲精品影院| 国产不卡精品一区二区三区| 久久国产精品自线拍免费| 午夜欧美成人久久久久久| 韩国三级视频在线观看| 香蕉视频三级| 国产91丝袜在线播放0| 亚洲 男人 天堂| 色综合久久久久综合体桃花网| 韩国三级一区| 天天做人人爱夜夜爽2020毛片| 韩国三级视频网站| 国产国语对白一级毛片| 日韩av片免费播放| 欧美爱爱动态| 日韩一级精品视频在线观看| 青青青草影院 | 成人免费一级毛片在线播放视频| 免费的黄视频| 韩国三级香港三级日本三级| 欧美a级成人淫片免费看| 国产伦精品一区二区三区无广告| 亚洲第一视频在线播放| 欧美18性精品| 亚洲精品影院久久久久久| 精品视频免费在线| 久草免费在线视频| 成人免费网站久久久| 欧美1卡一卡二卡三新区| 中文字幕一区二区三区精彩视频 | 高清一级做a爱过程不卡视频| 999久久久免费精品国产牛牛| 天天做人人爱夜夜爽2020| 日韩中文字幕一区二区不卡| 台湾美女古装一级毛片| 欧美一区二区三区性| 日本特黄特黄aaaaa大片| 九九久久国产精品| 国产综合91天堂亚洲国产| 午夜欧美成人久久久久久| 久久福利影视| 国产不卡在线观看| 99热热久久| 久久久成人影院| 一a一级片| 美女免费黄网站| 99色精品| 99久久精品国产高清一区二区 | 麻豆网站在线免费观看| 天天做日日干| 国产伦精品一区二区三区在线观看| 国产不卡福利| 美国一区二区三区| 国产视频久久久| 国产不卡在线看| 色综合久久久久综合体桃花网| 二级特黄绝大片免费视频大片| 91麻豆爱豆果冻天美星空| 久久精品大片| 欧美另类videosbestsex久久| 精品国产三级a| 精品国产一区二区三区免费| 精品久久久久久中文| 久久国产精品只做精品| 四虎影视精品永久免费网站| 午夜精品国产自在现线拍| 色综合久久天天综合| 国产视频久久久| 好男人天堂网 久久精品国产这里是免费 国产精品成人一区二区 男人天堂网2021 男人的天堂在线观看 丁香六月综合激情 | 九九免费精品视频| 国产亚洲免费观看| 成人av在线播放| 精品视频在线观看一区二区三区| 尤物视频网站在线| 一本伊大人香蕉高清在线观看| 可以在线看黄的网站| 国产成人女人在线视频观看| 国产网站免费在线观看| 国产欧美精品| 日本在线不卡视频| 色综合久久手机在线| 国产不卡精品一区二区三区| 亚洲女人国产香蕉久久精品 | 精品国产一区二区三区久久久蜜臀| 免费国产在线观看| 亚州视频一区二区| 99久久精品国产国产毛片| 日韩专区亚洲综合久久| 国产高清在线精品一区二区| 精品视频在线观看一区二区三区| 日韩中文字幕一区二区不卡| 好男人天堂网 久久精品国产这里是免费 国产精品成人一区二区 男人天堂网2021 男人的天堂在线观看 丁香六月综合激情 | 国产麻豆精品高清在线播放| 国产原创视频在线| 欧美国产日韩在线| 亚洲天堂在线播放| 国产一区国产二区国产三区| 韩国三级香港三级日本三级| 午夜激情视频在线播放| 成人免费观看的视频黄页| 日韩免费片| 日本免费区| 久草免费在线视频| 亚洲女人国产香蕉久久精品 | 国产国产人免费视频成69堂| 99热精品一区| 九九热精品免费观看| 精品视频免费观看| 国产韩国精品一区二区三区| 久久久久久久免费视频| 天天做日日干| 精品久久久久久综合网| 青青青草影院|