校园春色亚洲色图_亚洲视频分类_中文字幕精品一区二区精品_麻豆一区区三区四区产品精品蜜桃

主頁 > 知識庫 > 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學習之平滑重啟的相關信息資訊供網民參考!
  • 推薦文章
    校园春色亚洲色图_亚洲视频分类_中文字幕精品一区二区精品_麻豆一区区三区四区产品精品蜜桃
    在线不卡中文字幕| 婷婷成人综合网| 国产99精品在线观看| 国产三区在线成人av| 国产盗摄女厕一区二区三区| 国产午夜精品一区二区| av不卡一区二区三区| 亚洲男帅同性gay1069| 欧美日韩性生活| 极品少妇xxxx精品少妇偷拍| 亚洲精品在线电影| www.日韩在线| 亚洲亚洲人成综合网络| 91精品国产高清一区二区三区蜜臀| 日韩欧美国产综合| 豆国产96在线|亚洲| 一区二区三区四区在线播放 | 久久在线观看免费| 成人免费观看男女羞羞视频| 亚洲日本在线视频观看| 欧美一区二区三区免费视频| 国产乱码精品一区二区三区五月婷| 国产精品对白交换视频| 欧美性感一类影片在线播放| 极品少妇xxxx精品少妇| 亚洲日本电影在线| 在线成人小视频| 成人免费黄色在线| 日本91福利区| 中文字幕一区二区三区精华液| 欧美日韩一级二级三级| 成人av小说网| 久草精品在线观看| 一区二区三区四区精品在线视频| 日韩情涩欧美日韩视频| 99综合电影在线视频| 久久国产精品免费| 亚洲综合激情网| 国产精品久久久久一区二区三区| 欧美亚男人的天堂| 国产宾馆实践打屁股91| 秋霞午夜鲁丝一区二区老狼| 亚洲男人天堂av网| 国产精品婷婷午夜在线观看| 4438成人网| 欧美系列日韩一区| 成人av在线影院| 国产九色精品成人porny| 午夜欧美视频在线观看| 亚洲天堂久久久久久久| 久久精品欧美一区二区三区不卡| 欧美高清一级片在线| 一本色道综合亚洲| fc2成人免费人成在线观看播放 | 激情综合色丁香一区二区| 亚洲男人的天堂av| 中文av一区特黄| 日韩欧美国产三级| 欧美精品色综合| 欧美熟乱第一页| 在线视频国内自拍亚洲视频| 成人国产在线观看| 粗大黑人巨茎大战欧美成人| 国产一区二区三区精品欧美日韩一区二区三区| 亚洲成人1区2区| 亚洲综合自拍偷拍| 亚洲综合色婷婷| 亚洲国产色一区| 亚洲一区av在线| 亚洲一二三专区| 亚洲成a人片在线不卡一二三区 | 国产高清亚洲一区| 国产美女主播视频一区| 国产综合色视频| 国产高清在线精品| 成人av综合一区| 色综合 综合色| 欧美日韩精品欧美日韩精品一 | 色综合久久综合中文综合网| www.99精品| 91美女片黄在线| 欧美亚洲尤物久久| 欧美日韩aaaaaa| 日韩欧美电影在线| 欧美亚洲动漫制服丝袜| 欧美人动与zoxxxx乱| 国产在线播放一区三区四| 精久久久久久久久久久| 国产精品综合在线视频| 成人国产视频在线观看| 91老师片黄在线观看| 日本韩国欧美一区| 在线播放/欧美激情| 精品999久久久| 亚洲欧洲性图库| 亚洲一级不卡视频| 精品在线视频一区| 国产91精品一区二区| 一本久久精品一区二区| 欧美日韩综合不卡| 久久先锋影音av鲁色资源网| 欧美国产一区视频在线观看| 中文字幕一区二| 丝袜a∨在线一区二区三区不卡| 久久激情五月激情| 91蜜桃网址入口| 欧美一区二区在线不卡| 久久精品夜色噜噜亚洲aⅴ| 亚洲日本青草视频在线怡红院| 亚洲v日本v欧美v久久精品| 中文在线资源观看网站视频免费不卡 | 国产亚洲精品bt天堂精选| 国产三级一区二区| 亚洲一区二区欧美| 日本午夜一本久久久综合| 不卡一卡二卡三乱码免费网站| 97精品久久久久中文字幕| 欧美一区二区三区成人| 中日韩免费视频中文字幕| 日韩精品成人一区二区在线| 国产在线麻豆精品观看| 91豆麻精品91久久久久久| 欧美sm极限捆绑bd| 亚洲国产精品视频| 高清国产一区二区| 666欧美在线视频| 中文字幕第一区二区| 毛片av一区二区| 欧美性感一类影片在线播放| 久久久99久久| 蜜臀av一区二区三区| 在线免费不卡视频| 久久久久久久免费视频了| 日本成人中文字幕| 色综合天天狠狠| 国产人伦精品一区二区| 日本vs亚洲vs韩国一区三区二区| 91亚洲国产成人精品一区二三| 日韩欧美www| 日韩精品欧美精品| 欧美亚洲另类激情小说| ...av二区三区久久精品| 国产高清不卡一区二区| 日韩欧美视频在线| 日本欧美一区二区| 欧美另类videos死尸| 亚洲人成影院在线观看| 成人国产精品免费网站| 国产性做久久久久久| 极品少妇xxxx精品少妇偷拍| 日韩一区二区影院| 日韩电影在线免费| 欧美日韩和欧美的一区二区| 亚洲一级在线观看| 91福利视频久久久久| 一区二区视频在线看| 99久久婷婷国产精品综合| 国产精品色噜噜| 国产成人激情av| 欧美激情一区不卡| 丁香婷婷综合色啪| 欧美韩日一区二区三区四区| 国产成人午夜视频| 中文字幕免费观看一区| 国产激情偷乱视频一区二区三区| 久久女同互慰一区二区三区| 国产一区二区不卡老阿姨| 日韩一卡二卡三卡国产欧美| 免费人成在线不卡| 欧美成人一级视频| 国产美女av一区二区三区| 国产亚洲欧美日韩日本| 成人免费高清在线观看| 亚洲三级久久久| 欧美在线一二三| 首页国产丝袜综合| 日韩欧美视频一区| 国产高清成人在线| 亚洲欧美综合另类在线卡通| 99久久国产综合色|国产精品| 国产精品久久久久一区二区三区 | 日韩一区二区三区免费观看| 免费成人av在线| 久久久五月婷婷| 成人黄色网址在线观看| 亚洲黄色小视频| 欧美一级理论片| 成人免费视频播放| 亚洲一区日韩精品中文字幕| 欧美日韩国产精品成人| 九九九久久久精品| 国产精品久久久久永久免费观看| 色综合天天综合网国产成人综合天| 怡红院av一区二区三区| 91麻豆精品国产| 国产凹凸在线观看一区二区| 一区二区三区中文字幕精品精品| 7777精品伊人久久久大香线蕉最新版 | 日韩主播视频在线| 久久久99免费|