亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解如何在Go中實現(xiàn)優(yōu)雅停止

 更新時間:2024年04月15日 08:23:29   作者:波羅學  
和其他語言相比,Go 中有相同也有不同,相同的是實現(xiàn)思路上和其他語言沒啥差異,不同在于 Go 采用的是 goroutine + channel 的并發(fā)模型,與傳統(tǒng)的進程線程相比,實現(xiàn)細節(jié)上存在差異,本文將從實際場景和它的一般實現(xiàn)方式展開,逐步討論這個話題,需要的朋友可以參考下

簡介

什么是優(yōu)雅停止?在談優(yōu)雅停止前,我們可以說說什么是優(yōu)雅重啟,或者說熱重啟。

簡言之,優(yōu)雅重啟就是在服務升級、配置更新時,要重新啟動服務,優(yōu)雅重啟就是在服務不中斷或連接不丟失的情況下,重啟服務。優(yōu)雅重啟的整個流程中,新的進程將在舊的進程停止前啟動,舊進程會完成活動中的請求后優(yōu)雅地關(guān)閉進程。

優(yōu)雅重啟是服務開發(fā)中一個非常重要的概念,它讓我們在不中斷服務的情況下,更新代碼和修復問題。它在維持高可用性的生產(chǎn)環(huán)境中尤其關(guān)鍵。

從上面的這段可知,優(yōu)雅重啟是由兩個部分組成,分別是優(yōu)雅停止和啟動。

本文重點介紹優(yōu)雅停止,而優(yōu)雅啟動的整個流程要借助于外部工具控制,如 k8s 的容器編排。

優(yōu)雅停止

優(yōu)雅停止,即要在停止服務的同時,保證業(yè)務的完整性。從目標上看,優(yōu)雅停止經(jīng)歷三個步驟:通知服務停止、服務啟動清理,等待清理確認退出。

要停止一個服務,首先是通過一些機制告知服務要執(zhí)行退出前的工作,最常見的就是基于操作系統(tǒng)信號,我們慣例監(jiān)聽的信號主要是兩個,分別是由 kill PID 發(fā)出的 SIGTERM 和 CTRL+C 發(fā)出的 SIGINT。 其他信號還有,CTRL+/ 發(fā)出的 SIGQUIT。

當接收到指定信號,服務就要停止接受新的請求,且等待當前活動中的請求全部完成后再完全停止服務。

接下來,開始具體的代碼實現(xiàn)部分吧。

從 HTTP 服務開始

談優(yōu)雅重啟,最常被引用的案例就是 HTTP 服務,我將通過代碼逐步演示這個過程。如下是一個常規(guī) HTTP 服務:

func hello(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintf(w, "Hello World\n")
}

func main() {
  http.HandleFunc("/", hello)
  log.Println("Starting server on :8080")
  if err := http.ListenAndServe(":8080", nil); err != nil {
      log.Fatal("ListenAndServe: ", err)
  }
}

我們通過 time.Sleep 增加 hello 的耗時,以便于調(diào)試。

func hello(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintf(w, "Hello World\n")
  time.Sleep(10 * time.Second)
}

運行:

$ go run main.go

通過 curl 請求訪問 http://localhost:8080/ ,它進入到 10 秒的處理階段。假設這時,我們 CTRL+C 請求退出,HTTP 服務會直接退出,我們的 curl 請求被直接中斷。

我們可以使用 Go 標準庫提供的 http.Server 有一個 Shutdown 方法,可以安全地關(guān)閉服務器而不中斷任何活動的連接。而我們要做的,只需在收到停止信號后,執(zhí)行 Shutdown 即可。

信號方面,我們通過 Go 標準庫 signal 實現(xiàn),它提供了一個 Notify 函數(shù),可與 chan nnel 配合傳遞信號消息。我們監(jiān)聽的目標信號是 SIGINTSIGTERM。

重新修改 HTTP 服務入口,使用 http.ServerShutdown 函數(shù)關(guān)閉 Server

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", hello)

  server := http.Server{Addr: ":8080", Handler: mux}
  go server.ListenAndServe()
  
  quit := make(chan os.Signal, 1)
  // 注冊接收信號的 channel
  signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) 
  
  <-quit // 等待停止信號
  
  if err := server.Shutdown(context.Background()); err != nil {
    log.Fatal("Shutdown: ", err)
  }
}

我們將 server.ListenAndServe 運行于另一個 goroutine 中同時忽略了它的返回錯誤。

通過 signal.Notify 注冊信號。當收到如 CTRL+C 或 kill PID 發(fā)出的中斷信號,執(zhí)行 serve.Shutdown,它會通知到 server 停止接收新的請求,并等待活動中的連接處理完成。

現(xiàn)在運行 go run main.go 啟動服務,執(zhí)行 curl 命令測試接口,在請求還沒有返回之時,我們可以通過 CTRL+C 停止服務,它會有一段時間等待,我們可以在這個過程中嘗試 curl 請求,看它是否還接收新的請求。

如果希望防止程序假死,或者其他問題導致服務長時間無法退出,可通過 context.WithTimeout 方法包裝下傳遞給 Shutdown 方法的 ctx 變量。

ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()

if err := server.Shutdown(ctx); err != nil {
  log.Fatal("Shutdown: ", err)
}

到這里,我們就介紹完了 Go 標準庫 net/http 的優(yōu)雅停止的使用方案。

抽象出一個常規(guī)方案

如果開發(fā)一個非 HTTP 的服務,如何讓它支持優(yōu)雅停止呢?畢竟不是所有項目都是 HTTP 服務,不是所有項目都有現(xiàn)成的框架。

本文開頭提到的的三步驟,net/http 包的 Shutdown 把最核心的服務停止前的清理和等待都已經(jīng)在內(nèi)部實現(xiàn)了。我們可解讀下它的實現(xiàn)。

進入到 Shutdown 的源碼中,重點是開頭的第一句代碼,如下所示:

// future calls to methods such as Serve will return ErrServerClosed.
func (srv *Server) Shutdown(ctx context.Context) error {
  srv.inShutdown.Store(true)
  // ...其他清理代碼
  // ...等待活動請求完成并將其關(guān)閉
}

inShutdown 是一個標志位,用于標識程序是否已停止。為了解決并發(fā)數(shù)據(jù)競爭,它的底層類型是 atomic.bool,。

在 server.go 中的 Server.Serve 方法中,通過判斷 inShutdown 決定是否繼續(xù)接受新的請求。

func (srv *Server) Serve(l net.Listener)  error {
  // ...
  for {
    rw, err := l.Accept()
    if err != nil {
      if srv.shuttingDown() {
        return ErrServerClosed
      }
  // ...
}

我們可以從如上的分析中得知,要讓 HTTP 服務支持優(yōu)雅停止要啟動兩個 goroutine,Shutdown 運行與 main goroutine 中,當接收中停止信號,通過 inShutdown 標志位通知運行中的 goroutine。

用簡化的代碼表示這個一般模式。

var inShutdown bool

func Start() {
  for !inShutdown {
    // running
    time.Sleep(10 * time.Second)
  }
}

func Shutdown() {
  inShutdown = true
}

func main() {
  go Start()

  quit = make(chan os.Signal, 1)
  signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
  <- quit

  Shutdown()
}

大概看起來是那么回事,但這里的代碼少了一個步驟,即 Shutdown 沒有等待 Start 完成。

標準庫 net/http 是通過 for 循環(huán)不斷檢查是否有活動中的連接,如果連接沒有進行中請求會將其關(guān)閉,直到將所有連接關(guān)閉,便會退出 Shutdown。

核心代碼如下:

func (srv *Server) Shutdown(ctx context.Context) {
  // ...之前的代碼

  timer := time.NewTimer(nextPollInterval())
  defer timer.Stop()
  for {
    if srv.closeIdleConns() {
      return lnerr
    }
    select {
    case <-ctx.Done():
      return ctx.Err()
    case <-timer.C:
      timer.Reset(nextPollInterval())
    }
  }
}

重點就是那句 closeIdleConns,它負責檢查是否還有執(zhí)行中的請求。我就不把這部分的源代碼貼出來了。而檢查頻率是通過 timer 控制的。

現(xiàn)在讓簡化版等待 Start 完成后才退出。我們引入一個名為 isStop 的標志位以監(jiān)控停止狀態(tài)。

var inShutdown bool
var isStop bool

func Start() {
  for !inShutdown {
    // running
    time.Sleep(10 * time.Second)
  }
  isStop = true
}

func Shutdown() {
  inShutdown = true

  timer := time.NewTimer(time.Millisecond)
  defer timer.Stop()
  for {
    if isStop {
      return
    }
    <- timer.C
    timer.Reset(time.Millisecond))
  }
}

如上的代碼中,Start 函數(shù)退出時會執(zhí)行 isStop = true 表明已退出,在 Shutdown 中,通過定期檢查 isStop 等待 Start 退出完成。

此外,net/httpShutdown 方法還接收了一個 context.Context 參數(shù),允許實現(xiàn)超時控制,從而防止程序假死或強制關(guān)閉。

需要特別指出的是,示例中用的 isStopinShutdown 標志位為非原子類型,在正式場景中,為避免數(shù)據(jù)競爭,要使用原子操作或其他同步機制。

除了可用共享內(nèi)存標志位在不同協(xié)程間傳遞狀態(tài),也可以通過 channel 實現(xiàn),或你看到過類似如下的形式。

var inShutdown bool

func Start(stop chan struct{}) {
	for !inShutdown {
		// running
		time.Sleep(10 * time.Second)
	}
	stop <- struct{}{}
}

func Shutdown() {
	inShutdown = true
}

func main() {
	stop := make(chan struct{})
	defer close(stop)

	go Start(stop)

	go func() {
		quit := make(chan os.Signal, 1)
		signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
		<-quit
		Shutdown()
	}()

	<-stop
}

如上的代碼中,Start 通過 channel 通知主 goroutine,當觸發(fā)停止信號,isShutdown 通知 Start 要停止退出,它成功退出后,通過 stop <- struct{} 通知主函數(shù),結(jié)束等待。

總的來說,channel 的優(yōu)勢很明顯,避免了單獨管理一個 isStop 標志位來標識服務狀態(tài),并且免去了基于定時器的定期輪詢檢查的過程,還更加實時和高效。當然,net/http 使用輪詢檢查機制,是它的場景所決定,和我們這里不完全一樣。

一點思考

Go 語言支持多種方式在 Goroutine 間傳遞信息,這催生了多樣的優(yōu)雅停止實現(xiàn)方式。如果是在涉及多個嵌套 Goroutine 的場景中,我們可以引入 context 來實現(xiàn)多層級的狀態(tài)和信息傳遞,確保操作的連貫性和安全性。

然盡管實現(xiàn)方式眾多,但其核心思路是一致的,而底層目標始終是我們要保證處理邏輯的完整性。

另外,通過將優(yōu)雅停止與容器編排技術(shù)結(jié)合,并為服務添加健康檢查,我們能夠確保服務總有實例在活躍狀態(tài),實現(xiàn)真正意義上的優(yōu)雅重啟。這不僅提高了服務的可靠性,也優(yōu)化了資源的利用效率。

總結(jié)

本文探索了 Go 語言中優(yōu)雅重啟的實現(xiàn)方法,展示了如何通過 http.Server 的 Shutdown 方法安全地重啟服務,以及使用 context 控制超時。基于此,我們抽象出了一般服務優(yōu)雅停止的核心思路。

以上就是詳解如何在Go中實現(xiàn)優(yōu)雅停止的詳細內(nèi)容,更多關(guān)于在Go中實現(xiàn)停止的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • golang 生成對應的數(shù)據(jù)表struct定義操作

    golang 生成對應的數(shù)據(jù)表struct定義操作

    這篇文章主要介紹了golang 生成對應的數(shù)據(jù)表struct定義操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • GoLang channel底層代碼實現(xiàn)詳解

    GoLang channel底層代碼實現(xiàn)詳解

    Channel和goroutine的結(jié)合是Go并發(fā)編程的大殺器。而Channel的實際應用也經(jīng)常讓人眼前一亮,通過與select,cancel,timer等結(jié)合,它能實現(xiàn)各種各樣的功能。接下來,我們就要梳理一下GoLang channel底層代碼實現(xiàn)
    2022-10-10
  • Mac下Vs code配置Go語言環(huán)境的詳細過程

    Mac下Vs code配置Go語言環(huán)境的詳細過程

    這篇文章給大家介紹Mac下Vs code配置Go語言環(huán)境的詳細過程,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-07-07
  • golang?pprof?監(jiān)控系列?go?trace統(tǒng)計原理與使用解析

    golang?pprof?監(jiān)控系列?go?trace統(tǒng)計原理與使用解析

    這篇文章主要為大家介紹了golang?pprof?監(jiān)控系列?go?trace統(tǒng)計原理與使用解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04
  • Golang使用反射的動態(tài)方法調(diào)用詳解

    Golang使用反射的動態(tài)方法調(diào)用詳解

    Go是一種靜態(tài)類型的語言,提供了大量的安全性和性能。這篇文章主要和大家介紹一下Golang使用反射的動態(tài)方法調(diào)用,感興趣的小伙伴可以了解一下
    2023-03-03
  • Golang實現(xiàn)超時機制讀取文件的方法示例

    Golang實現(xiàn)超時機制讀取文件的方法示例

    讀寫文件是Go程序的基本任務,包括使用程序查看文件內(nèi)容、創(chuàng)建或修改文件,Go提供了os,ioutil,io以及bufio包實現(xiàn)文件操作,本文介紹如果在讀文件過程中增加超時機制,避免文件太大一直占用資源,需要的朋友可以參考下
    2025-01-01
  • Go?gRPC服務客戶端流式RPC教程

    Go?gRPC服務客戶端流式RPC教程

    這篇文章主要為大家介紹了Go?gRPC服務客戶端流式RPC教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • 用Go語言編寫一個簡單的分布式系統(tǒng)

    用Go語言編寫一個簡單的分布式系統(tǒng)

    這篇文章主要介紹了用Go語言編寫一個簡單的分布式系統(tǒng),文中的代碼示例講解的非常詳細,對我們的學習或工作有一定的幫助,感興趣的小伙伴跟著小編一起來看看吧
    2023-08-08
  • Go語言kube-scheduler深度剖析與開發(fā)之pod調(diào)度

    Go語言kube-scheduler深度剖析與開發(fā)之pod調(diào)度

    這篇文章主要為大家介紹了Go語言kube-scheduler深度剖析與開發(fā),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04
  • 詳解Go語言中new和make關(guān)鍵字的區(qū)別

    詳解Go語言中new和make關(guān)鍵字的區(qū)別

    本篇文章來介紹一道非常常見的面試題,到底有多常見呢?可能很多面試的開場白就是由此開始的。那就是 new 和 make 這兩個內(nèi)置函數(shù)的區(qū)別,希望對大家有所幫助
    2023-03-03

最新評論