1. 程式人生 > 程式設計 >golang API開發過程的中的自動重啟方式(基於gin框架)

golang API開發過程的中的自動重啟方式(基於gin框架)

概要

基於 golang Gin 框架開發 web 服務時,需要時不時的 go build,然後重啟服務檢視執行結果.
go build 的過程整合在編輯器中(emacs),可以通過快捷鍵迅速完成,但是每次重啟服務都切換到命令列中操作.
因此,希望能夠編譯通過之後自動重啟服務.

這裡並不是部署階段的服務重啟,所以不用過多考慮是否正常退出其中的協程.

實現方式

在開源的 illuminant 專案中,已經將相應的程式碼整合到 gin 的 debug mode 中.

程式碼檔案: https://gitee.com/wangyubin/illuminant/blob/dev/server_cmd.go

 func setupWatcher() (chan struct{},error) {
  file,err := osext.Executable()
   if err != nil {
   return nil,err
   }
  log.Printf("watching %q\n",file)
   w,err := fsnotify.NewWatcher()
   if err != nil {
   return nil,err
  }
  done := make(chan struct{})
  go func() {
   select {
   case e := <-w.Events:
    log.Printf("watcher received: %+v",e)
    err := syscall.Exec(file,os.Args,os.Environ())
    if err != nil {
     log.Fatal(err)
    }
   case err := <-w.Errors:
    log.Printf("watcher error: %+v",err)
   case <-done:
    log.Print("watcher shutting down")
    return
   }
  }()
  err = w.Add(file)
  if err != nil {
   return nil,err
  }
  return done,nil
 }

在 gin debug mode 下,使用此方法自動重啟服務

if c.Bool("prod") {
   gin.SetMode(gin.ReleaseMode)
   // start route
   return routes.Routes(cnf.Server.Port)
  } else {
   gin.SetMode(gin.DebugMode)
   watcher,err := setupWatcher()
   if err != nil {
    // do something sensible
   log.Fatal(err)
  }
  defer close(watcher)
  return routes.Routes(cnf.Server.Port)
 }

補充

上面函式的核心有以下兩點:

  • w,err := fsnotify.NewWatcher(): 建立監控檔案變化的 watcher,err = w.Add(file) 並將當前二進位制檔案加入到監控檔案列表中
  • err := syscall.Exec(file,os.Environ()) 接受到檔案變化的事件時,重新呼叫一次自己,使用上次一樣的引數和環境變數

syscall.Exec

對於這個函式,一般可能用的比較少,這裡稍微介紹下. 它有 3 個引數:

  • args[0]: 可執行檔案的路徑(相對路徑,絕對路徑或者 PATH 中的路徑都可以)
  • args[1]: 命令的引數
  • args[2]: 命令的執行的環境變數,os.Environ() 表示繼承 caller 的環境變數

當 syscall.Exec 執行時,在它之前的所有未執行完的程式都會被中止(包括在 go routine 中執行的程式),
然後執行 syscall.Exec 呼叫的命令,該命令還保持在之前程式的 PID 下執行.

syscall.Exec 是最後一條執行的程式碼,重啟時在它之後可以有程式碼,但是都不會被執行到,包括 defer 中的程式碼.

下面是個小例子(通過這個例子可以驗證上面的結論):

package main
  
  import (
  "fmt"
  "log"
  "os"
  "syscall"
  "time"
  
  "github.com/fsnotify/fsnotify"
  "github.com/kardianos/osext"
 )
 
 func syscallExec() {
  watcher,err := setupWatcher()
  if err != nil {
   log.Fatal(err)
  }
  defer finally(watcher)
 
  fmt.Printf("current pid: %d\n",os.Getpid())
  var count = 0
 
  go func(count int) {
   for {
    fmt.Printf(">>> count in GO ROUTINE: %d\n",count)
    count++
    time.Sleep(1 * time.Second)
   }
  }(count)
 
  for {
   fmt.Printf(">>> count in MAIN: %d\n",count)
   count++
   time.Sleep(1 * time.Second)
  }
 }
 
 func finally(watcher chan struct{}) {
  // 重啟時沒有執行此函式
  fmt.Println("exit original exec")
  close(watcher)
 }
 
 func setupWatcher() (chan struct{},err := osext.Executable()
  if err != nil {
   return nil,err
  }
  log.Printf("watching %q\n",file)
  w,err := fsnotify.NewWatcher()
  if err != nil {
   return nil,err
  }
  done := make(chan struct{})
  go func() {
   select {
   case e := <-w.Events:
    log.Printf("watcher received: %v",nil
 }

到此這篇關於golang API開發過程的中的自動重啟方式(基於gin框架)的文章就介紹到這了,更多相關golang API 自動重啟內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!