<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                [TOC] # 簡介 按照一般的設計原則, 每個 HTTP 請求都是無狀態的,因此大多情況下 Web 應用都很容易做水平擴展。“無狀態”也意味著 HTTP 請求發起重試的成本是很低的,從而使得 Web 接口的開發很少關注優雅中止(一部分也因為 Web 框架做了這部分的考慮)。 不過,業務中 ① 總會存在對中止比較敏感的接口(比如支付相關),并且 ② 總會存在一些帶狀態的服務,此時優雅中止就顯得比較重要了。 本文通過一個Go 定時任務示例來簡單介紹 Go 技術棧中優雅中止的處理思路。 # k8s中pod的終止機制 作為高可靠的服務平臺,k8s 定義了終止 Pod (業務進程在 Pod 中運行)的基本步驟:當主動刪除 pod 時,系統會在強制終止 Pod 之前將 TERM 信號發送到每個容器中的主進程,過一段時間后(默認為 30 秒),再把 KILL 信號發送到這些進程。除此之外, k8s 還通過鉤子方法提供了對 容器生命周期 的管理能力,允許用戶通過自定義的方式配置容器啟動后或終止前執行的操作。 當打包進鏡像的應用運行在 k8s 中的時候,如果應用實現了優雅中止的機制,就可以充分利用上面提到的 k8s 的能力,在升級應用(發新版本)和管理 Pod (宿主機維護時把 Pod 漂移到另一個宿主機,或者在閑時動態地收縮 Pod 數量從而把資源省出來另作他用)的過程中實現服務的零中斷。 # 優雅中止的 Go 代碼示例 下面的代碼定義了兩個定時任務:mySecondJobs 每秒鐘會觸發一次,每次持續約 1 秒鐘;myMinuteJobs 每分鐘會觸發一次,每次持續約 2 秒鐘。具體地可以閱讀下面的代碼(可以直接復制下面的代碼到自己的環境中運行): ~~~ package main import ( "fmt" "os" "os/signal" "syscall" "time" ) func main() { c := make(chan os.Signal) // Go 不允許監聽 SIGKILL/SIGSTOP 信號 // 參考 https://github.com/golang/go/issues/9463 signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) second := time.NewTicker(time.Second) minute := time.NewTicker(time.Minute) A: // 由于 for-select 嵌套使用,設置跳出 for 循環的標記 for { select { case s := <-c: // 收到 SIGTERM/SIGINT 信號,跳出 for 循環結束進程 fmt.Printf("get signal %s, graceful ending...\n", s) break A case <-second.C: go mySecondJobs() case <-minute.C: go myMinuteJobs() } } fmt.Println("graceful ending") // 做一些操作讓異步任務正常結束,這里偷懶地采取簡單等待的方式 ?? time.Sleep(time.Second * 10) fmt.Println("graceful ended.") } func mySecondJobs() { tS := time.Now().String() fmt.Printf("starting second job: %s \n", tS) time.Sleep(time.Second * 1) // 假設每個任務消耗 1 秒時間 fmt.Printf("second job %s are done. \n", tS) } func myMinuteJobs() { tS := time.Now().String() fmt.Printf("starting minute job: %s \n", tS) time.Sleep(time.Second * 2) // 假設每個任務消耗 2 秒時間 fmt.Printf("minute job %s are done. \n", tS) } ~~~ # 源碼解讀-優雅中止的處理思路 * 通過 signal.Notify 捕獲特定的信號; * 通過 for + select 來實現循環任務,同時檢測上步中欲捕獲的信號; * 如果定時器被觸發,則執行對應的任務; * 如果發現收到了指定的信號,則跳出 for 循環,并采取一定措施結束異步任務。 # 源碼解讀-值得關注的幾個點 代碼中采用了 go mySecondJobs() 和 go myMinuteJobs() 異步任務的方式;如果采用同步的方式將無法捕獲信號,因為此時主線程在處理業務邏輯,沒有空閑處理信號捕獲邏輯。 源碼中偷懶地采取簡單等待的方式來保證異步任務正常結束,非普適方法,實際開發中需要根據情況做定制。 time.Ticker 的使用是有注意事項的,當 select 語句中同一時刻有多個分支滿足條件時會隨機取一個執行,從而導致信息丟失,不過本文的代碼不會觸發這個問題,大家可以思考一下原因。 # http的shutdown 如何優雅的關閉http服務在Go Web開發中一直被提及和討論的話題,今天Go 1.8的發布終于為我們帶來了這個特性。 文檔中是這樣介紹的: ~~~ func (srv *Server) Shutdown(ctx context.Context) error ~~~ `Shutdown`將無中斷的關閉正在活躍的連接,然后平滑的停止服務。處理流程如下: * 首先關閉所有的監聽 * 然后關閉所有的空閑連接 * 然后無限期等待連接處理完畢轉為空閑,并關閉 * 如果提供了 帶有超時的`Context`,將在服務關閉前返回`Context`的超時錯誤 需要注意的是,`Shutdown`并不嘗試關閉或者等待`hijacked`連接,如`WebSockets`。如果需要的話調用者需要分別處理諸如長連接類型的等待和關閉。 其實,你只要調用`Shutdown`方法就好了。 ~~~ // main.go package main import ( "fmt" "log" "net/http" "os" "os/signal" "syscall" "time" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World, %v\n", time.Now()) }) s := &http.Server{ Addr: ":8080", Handler: http.DefaultServeMux, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } go func() { log.Println(s.ListenAndServe()) log.Println("server shutdown") }() // Handle SIGINT and SIGTERM. ch := make(chan os.Signal) signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) log.Println(<-ch) // Stop the service gracefully. log.Println(s.Shutdown(nil)) // Wait gorotine print shutdown message time.Sleep(time.Second * 5) log.Println("done.") } ~~~ 運行程序: ~~~ go run main.go ~~~ 然后`ctrl + c`終止該程序,會打印出如下信息: ~~~ 2017/02/17 11:36:28 interrupt 2017/02/17 11:36:28 <nil> 2017/02/17 11:36:28 http: Server closed 2017/02/17 11:36:28 server shutdown ~~~ 可以看到,服務被正確關閉了。 在沒有`Shutdown`方法之前,`ctrl + c`就硬生生的終止了,然后就沒有然后了 # 小結 默認情況下,Go 應用在接收到 TERM 信號后直接退出主進程,如果此時有過程沒處理完(比如 接收到外部請求后尚未返回響應,或者內部的異步任務尚未結束),則會導致過程的異常中斷,影響服務質量。通過在代碼中顯式地捕獲 TERM 信號及其他信號,感知操作系統對進程的處理,可以主動采取措施優雅地結束應用進程。 隨著 k8s 的普及,考慮到其對進程生命周期的規范化管理,應用支持代碼級的優雅中止(尤其是容器化的應用)有必要成為一種開發規范,值得引起每一位開發者的注意
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看