Go項目實現(xiàn)優(yōu)雅關(guān)機(jī)與平滑重啟功能
前言
優(yōu)雅關(guān)機(jī)就是服務(wù)端關(guān)機(jī)命令發(fā)出后不是立即關(guān)機(jī),而是等待當(dāng)前還在處理的請求全部處理完畢后再退出程序,是一種對客戶端友好的關(guān)機(jī)方式。而執(zhí)行Ctrl+C關(guān)閉服務(wù)端時,會強(qiáng)制結(jié)束進(jìn)程導(dǎo)致正在訪問的請求出現(xiàn)問題。
什么是優(yōu)雅關(guān)機(jī)?
優(yōu)雅關(guān)機(jī)就是服務(wù)端關(guān)機(jī)命令發(fā)出后不是立即關(guān)機(jī),而是等待當(dāng)前還在處理的請求全部處理完畢后再退出程序,是一種對客戶端友好的關(guān)機(jī)方式。而執(zhí)行Ctrl+C關(guān)閉服務(wù)端時,會強(qiáng)制結(jié)束進(jìn)程導(dǎo)致正在訪問的請求出現(xiàn)問題。
實現(xiàn)原理
Go 1.8版本之后, http.Server 內(nèi)置的 Shutdown() 方法就支持優(yōu)雅地關(guān)機(jī),說明一下Shutdown工作的機(jī)制:當(dāng)程序檢測到中斷信號時,我們調(diào)用http.server種的shutdown方法,該方法將阻止新的請求進(jìn)來,同時保持當(dāng)前的連接,知道當(dāng)前連接完成則終止程序!
實現(xiàn)優(yōu)雅重啟
package main
import (
"context"
"fmt"
"github.com/spf13/viper"
"go.uber.org/zap"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
//啟動服務(wù)(優(yōu)雅關(guān)機(jī))
srv := &http.Server{
Addr: fmt.Sprintf(":%d", viper.GetInt("app.port")),
Handler: r,
}
go func() {
// 開啟一個goroutine啟動服務(wù)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// 等待中斷信號來優(yōu)雅地關(guān)閉服務(wù)器,為關(guān)閉服務(wù)器操作設(shè)置一個5秒的超時
quit := make(chan os.Signal, 1) // 創(chuàng)建一個接收信號的通道
// kill 默認(rèn)會發(fā)送 syscall.SIGTERM 信號
// kill -2 發(fā)送 syscall.SIGINT 信號,我們常用的Ctrl+C就是觸發(fā)系統(tǒng)SIGINT信號
// kill -9 發(fā)送 syscall.SIGKILL 信號,但是不能被捕獲,所以不需要添加它
// signal.Notify把收到的 syscall.SIGINT或syscall.SIGTERM 信號轉(zhuǎn)發(fā)給quit
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 此處不會阻塞
<-quit // 阻塞在此,當(dāng)接收到上述兩種信號時才會往下執(zhí)行
zap.L().Info("Shutdown Server ...")
// 創(chuàng)建一個5秒超時的context
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// 5秒內(nèi)優(yōu)雅關(guān)閉服務(wù)(將未處理完的請求處理完再關(guān)閉服務(wù)),超過5秒就超時退出
if err := srv.Shutdown(ctx); err != nil {
zap.L().Fatal("Server Shutdown: ", zap.Error(err))
}
zap.L().Info("Server exiting")
}實現(xiàn)平滑重啟
import (
"log"
"net/http"
"time"
"github.com/fvbock/endless"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "hello xiaosheng !")
})
// 默認(rèn)endless服務(wù)器會監(jiān)聽下列信號:
// syscall.SIGHUP,syscall.SIGUSR1,syscall.SIGUSR2,syscall.SIGINT,syscall.SIGTERM和syscall.SIGTSTP
// 接收到 SIGHUP 信號將觸發(fā)`fork/restart` 實現(xiàn)優(yōu)雅重啟(kill -1 pid會發(fā)送SIGHUP信號)
// 接收到 syscall.SIGINT或syscall.SIGTERM 信號將觸發(fā)優(yōu)雅關(guān)機(jī)
// 接收到 SIGUSR2 信號將觸發(fā)HammerTime
// SIGUSR1 和 SIGTSTP 被用來觸發(fā)一些用戶自定義的hook函數(shù)
if err := endless.ListenAndServe(":8080", router); err!=nil{
log.Fatalf("listen: %s\n", err)
}
log.Println("Server exiting...")測試
我們通過執(zhí)行kill -1 pid命令發(fā)送syscall.SIGINT來通知程序優(yōu)雅重啟,具體做法如下:
- 打開終端,go build -o graceful_restart編譯并執(zhí)行./graceful_restart,終端輸出當(dāng)前pid(假設(shè)為43682)
- 將代碼中處理請求函數(shù)返回的hello gin!修改為hello q1mi!,再次編譯go build -o graceful_restart
- 打開一個瀏覽器,訪問127.0.0.1:8080/,此時瀏覽器白屏等待服務(wù)端返回響應(yīng)。
- 在終端迅速執(zhí)行kill -1 43682命令給程序發(fā)送syscall.SIGHUP信號
- 等第3步瀏覽器收到響應(yīng)信息hello gin!后再次訪問127.0.0.1:8080/會收到hello q1mi!的響應(yīng)。
- 在不影響當(dāng)前未處理完請求的同時完成了程序代碼的替換,實現(xiàn)了優(yōu)雅重啟。
但是需要注意的是,此時程序的PID變化了,因為endless 是通過fork子進(jìn)程處理新請求,待原進(jìn)程處理完當(dāng)前請求后再退出的方式實現(xiàn)優(yōu)雅重啟的。所以當(dāng)你的項目是使用類似supervisor的軟件管理進(jìn)程時就不適用這種方式了。
總結(jié)
無論是優(yōu)雅關(guān)機(jī)還是優(yōu)雅重啟歸根結(jié)底都是通過監(jiān)聽特定系統(tǒng)信號,然后執(zhí)行一定的邏輯處理保障當(dāng)前系統(tǒng)正在處理的請求被正常處理后再關(guān)閉當(dāng)前進(jìn)程。使用優(yōu)雅關(guān)機(jī)還是使用優(yōu)雅重啟以及怎么實現(xiàn),這就需要根據(jù)項目實際情況來決定了。
到此這篇關(guān)于Go實現(xiàn)優(yōu)雅關(guān)機(jī)與平滑重啟 的文章就介紹到這了,更多相關(guān)Go關(guān)機(jī)與重啟 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一文帶你了解Go語言標(biāo)準(zhǔn)庫math和rand的常用函數(shù)
這篇文章主要為大家詳細(xì)介紹了Go語言標(biāo)準(zhǔn)庫math和rand中的常用函數(shù),文中的示例代碼講解詳細(xì), 對我們學(xué)習(xí)Go語言有一定的幫助,感興趣的小伙伴可以了解一下2022-12-12
go-zero接入skywalking實現(xiàn)鏈路追蹤的詳細(xì)教程
SkyWalking是一個開源的服務(wù)追蹤系統(tǒng),它專注于分布式系統(tǒng)的可觀測性和服務(wù)性能分析,它的主要目標(biāo)是幫助開發(fā)者理解和優(yōu)化微服務(wù)架構(gòu)中的服務(wù)間交互情況,本文介紹了go-zero接入skywalking鏈路追蹤的詳細(xì)教程,需要的朋友可以參考下2024-08-08
go內(nèi)存緩存如何new一個bigcache對象示例詳解
這篇文章主要為大家介紹了go內(nèi)存緩存如何new一個bigcache對象示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
golang實現(xiàn)openssl自簽名雙向認(rèn)證的詳細(xì)步驟
這篇文章主要介紹了golang實現(xiàn)openssl自簽名雙向認(rèn)證的詳細(xì)步驟,本文分步驟給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-03-03

