網站首頁 編程語言 正文
前言
我們編寫的Web項目部署之后,經常會因為需要進行配置變更或功能迭代而重啟服務,單純的kill -9 pid的方式會強制關閉進程,這樣就會導致服務端當前正在處理的請求失敗,那有沒有更優雅的方式來實現關機或重啟呢?
閱讀本文需要了解一些UNIX系統中信號的概念,請提前查閱資料預習。
優雅地關機
什么是優雅關機?
優雅關機就是服務端關機命令發出后不是立即關機,而是等待當前還在處理的請求全部處理完畢后再退出程序,是一種對客戶端友好的關機方式。而執行Ctrl+C
關閉服務端時,會強制結束進程導致正在訪問的請求出現問題。
如何實現優雅關機?
Go 1.8版本之后, http.Server 內置的?Shutdown()?方法就支持優雅地關機,具體示例如下:
// +build go1.8 package main import ( "context" "log" "net/http" "os" "os/signal" "syscall" "time" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() router.GET("/", func(c *gin.Context) { time.Sleep(5 * time.Second) c.String(http.StatusOK, "Welcome Gin Server") }) srv := &http.Server{ Addr: ":8080", Handler: router, } go func() { // 開啟一個goroutine啟動服務 if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("listen: %s\n", err) } }() // 等待中斷信號來優雅地關閉服務器,為關閉服務器操作設置一個5秒的超時 quit := make(chan os.Signal, 1) // 創建一個接收信號的通道 // kill 默認會發送 syscall.SIGTERM 信號 // kill -2 發送 syscall.SIGINT 信號,我們常用的Ctrl+C就是觸發系統SIGINT信號 // kill -9 發送 syscall.SIGKILL 信號,但是不能被捕獲,所以不需要添加它 // signal.Notify把收到的 syscall.SIGINT或syscall.SIGTERM 信號轉發給quit signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 此處不會阻塞 <-quit // 阻塞在此,當接收到上述兩種信號時才會往下執行 log.Println("Shutdown Server ...") // 創建一個5秒超時的context ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // 5秒內優雅關閉服務(將未處理完的請求處理完再關閉服務),超過5秒就超時退出 if err := srv.Shutdown(ctx); err != nil { log.Fatal("Server Shutdown: ", err) } log.Println("Server exiting") }
如何驗證優雅關機的效果呢?
上面的代碼運行后會在本地的8080端口開啟一個web服務,它只注冊了一條路由/
,后端服務會先sleep 5秒鐘然后才返回響應信息。
我們按下Ctrl+C時會發送syscall.SIGINT來通知程序優雅關機,具體做法如下:
- 打開終端,編譯并執行上面的代碼
- 打開一個瀏覽器,訪問127.0.0.1:8080/,此時瀏覽器白屏等待服務端返回響應。
- 在終端迅速執行
Ctrl+C
命令給程序發送syscall.SIGINT
信號 - 此時程序并不立即退出而是等我們第2步的響應返回之后再退出,從而實現優雅關機。
優雅地重啟
優雅關機實現了,那么該如何實現優雅重啟呢?
我們可以使用?fvbock/endless?來替換默認的?ListenAndServe
啟動服務來實現, 示例代碼如下:
package main 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) { time.Sleep(5 * time.Second) c.String(http.StatusOK, "hello gin!") }) // 默認endless服務器會監聽下列信號: // syscall.SIGHUP,syscall.SIGUSR1,syscall.SIGUSR2,syscall.SIGINT,syscall.SIGTERM和syscall.SIGTSTP // 接收到 SIGHUP 信號將觸發`fork/restart` 實現優雅重啟(kill -1 pid會發送SIGHUP信號) // 接收到 syscall.SIGINT或syscall.SIGTERM 信號將觸發優雅關機 // 接收到 SIGUSR2 信號將觸發HammerTime // SIGUSR1 和 SIGTSTP 被用來觸發一些用戶自定義的hook函數 if err := endless.ListenAndServe(":8080", router); err!=nil{ log.Fatalf("listen: %s\n", err) } log.Println("Server exiting") }
如何驗證優雅重啟的效果呢?
我們通過執行kill -1 pid
命令發送syscall.SIGINT
來通知程序優雅重啟,具體做法如下:
- 打開終端,go build -o graceful_restart編譯并執行./graceful_restart,終端輸出當前pid(假設為43682)
- 將代碼中處理請求函數返回的
hello gin!
修改為hello q1mi!
,再次編譯go build -o graceful_restart - 打開一個瀏覽器,訪問127.0.0.1:8080/,此時瀏覽器白屏等待服務端返回響應。
- 在終端迅速執行kill -1 43682命令給程序發送syscall.SIGHUP信號
- 等第3步瀏覽器收到響應信息
hello gin!
后再次訪問127.0.0.1:8080/會收到hello q1mi!
的響應。 - 在不影響當前未處理完請求的同時完成了程序代碼的替換,實現了優雅重啟。
但是需要注意的是,此時程序的PID變化了,因為endless
?是通過fork
子進程處理新請求,待原進程處理完當前請求后再退出的方式實現優雅重啟的。所以當你的項目是使用類似supervisor
的軟件管理進程時就不適用這種方式了。
總結
無論是優雅關機還是優雅重啟歸根結底都是通過監聽特定系統信號,然后執行一定的邏輯處理保障當前系統正在處理的請求被正常處理后再關閉當前進程。使用優雅關機還是使用優雅重啟以及怎么實現,這就需要根據項目實際情況來決定了。
原文鏈接:https://www.cnblogs.com/guyouyin123/p/14499893.html
相關推薦
- 2022-05-13 檢測到調試后執行的代碼
- 2022-06-28 react18中react-redux狀態管理的實現_React
- 2022-06-28 深入解析docker文件分層原理_docker
- 2023-12-26 Description:Web server failed to start. Port 8080
- 2022-06-30 Redis三種常用的緩存讀寫策略步驟詳解_Redis
- 2023-07-04 Spring中@Transactional注解事務傳播行為propagation參數說明
- 2022-03-22 docker安裝RabbitMQ詳細步驟_docker
- 2022-05-31 詳解python學習筆記之解釋器_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支