Golang 項目平滑重啟

引言

平滑重啟(Graceful Restart)技術作為一種常用的解決方案,通過允許新進程接管而不中斷現有的請求,確保了系統的穩定運行和業務連續性。同時目前公司的服務重啟絕大部分也都適用的 go 的平滑重啟技術。

本部分將對平滑重啟的概念、應用場景以及實現方式進行詳細的介紹,幫助開發者理解如何在實際應用中實現平滑重啟,保障服務的高可用性。

定義

  • 平滑重啟:平滑重啟是指在不中斷現有服務的前提下,使用新進程替代現有進程的操作。具體來說,平滑重啟包括以下步驟:
    • 啟動新的進程(通常是同一個服務的升級版)。
    • 新進程接管當前的請求和連接。
    • 舊進程完成當前任務后,優雅地退出,避免未處理的請求丟失。
  • 重啟信號:在類 Unix 操作系統中,信號是用來通知進程發生某些事件的一種機制。常用的重啟信號包括:
    • SIGHUP:通常用于通知進程重新加載配置或進行平滑重啟。在很多應用中,SIGHUP 被用來觸發進程的平滑重啟。
    • SIGTERM:表示請求程序終止進程,通常由操作系統或用戶發起,用于平滑關閉進程。
    • SIGINT:通常是用戶在終端輸入 Ctrl+C 時發送的信號,用于終止進程。
  • **PID 文件:**PID 文件是用來存儲正在運行的進程的進程 ID(PID)的文件。在平滑重啟中,PID 文件非常重要,它允許新進程在啟動時找到并與舊進程進行交互。新進程通常會讀取 PID 文件,獲取舊進程的 PID,并通過發送信號來請求舊進程退出。

平滑重啟

示例代碼

package mainimport ("fmt""net/http""os""os/signal""syscall""github.com/cloudflare/tableflip"
)// 首頁 handler
func homeHandler(w http.ResponseWriter, r *http.Request) {w.Header().Set("Content-Type", "text/plain")fmt.Fprintln(w, "Welcome to the Home Page!")
}func main() {// 創建 tableflip 管理器upg, err := tableflip.New(tableflip.Options{PIDFile: "/Users/wepie/Downloads/testGin.pid"})if err != nil {fmt.Printf("Error creating tableflip manager: %v\n", err)os.Exit(1)}defer upg.Stop()// 捕獲 SIGHUP 信號并觸發升級go func() {sig := make(chan os.Signal, 1)signal.Notify(sig, syscall.SIGHUP)for range sig {if err := upg.Upgrade(); err != nil {fmt.Printf("Error during upgrade: %v\n", err)} else {fmt.Println("Upgrade triggered: New process started!")}}}()// 創建一個新的 ServeMux 來管理多個路由mux := http.NewServeMux()mux.HandleFunc("/", homeHandler)           // 首頁// 監聽端口并啟動 HTTP 服務ln, err := upg.Listen("tcp", "localhost:8081")if err != nil {fmt.Printf("Error starting listener: %v\n", err)os.Exit(1)}defer ln.Close()// 啟動 HTTP 服務go func() {if err := http.Serve(ln, mux); err != nil {fmt.Printf("HTTP server error: %v\n", err)}}()// 等待進程準備好if err := upg.Ready(); err != nil {fmt.Printf("Error marking process as ready: %v\n", err)os.Exit(1)}fmt.Println("服務啟動完成 pid: ", os.Getpid())// 等待退出信號<-upg.Exit()fmt.Println("服務退出")
}

上述是一個簡單的平滑重啟的案例,有想試驗的同學可以直接用這段代碼實現。

在命令行 kill -SIGHUP 操作該進程(PID 可以通過定義的 PID 文件位置查閱),可以看到最終的平滑重啟

過程分析

創建 tableflip 管理器 (upg,upgrader)

upg, err := tableflip.New(tableflip.Options{PIDFile: "/Users/wepie/Downloads/testGin.pid"})
if err != nil {fmt.Printf("Error creating tableflip manager: %v\n", err)os.Exit(1)
}
defer upg.Stop()

做了什么?

  • 創建了一個 tableflip 管理器 upg,它負責控制進程的平滑重啟。
  • 使用 tableflip.Options 配置選項來指定 PID 文件,它會記錄當前進程的 PID,通常用于在后續重啟中找到進程。
  • 如果 tableflip.New 返回錯誤,程序會打印錯誤信息并退出。
  • 使用 defer 確保當程序退出時,調用 upg.Stop() 來停止管理器,釋放資源。

完成了什么?

  • 管理器 upg 被創建并準備好,后續的重啟操作將通過它來進行。

信號捕獲和進程升級

go func() {sig := make(chan os.Signal, 1)signal.Notify(sig, syscall.SIGHUP)for range sig {if err := upg.Upgrade(); err != nil {fmt.Printf("Error during upgrade: %v\n", err)} else {fmt.Println("Upgrade triggered: New process started!")}}
}()

做了什么?

  • 啟動了一個新的 goroutine,負責監聽 SIGHUP 信號(通常用于平滑重啟)。當該信號到達時,觸發進程的重啟。
  • signal.Notify(sig, syscall.SIGHUP) 告訴程序捕獲 SIGHUP 信號,并將其放入 sig 通道。(任何信號都可以作為放入通道的內容,取決于怎么設計)
  • 當 sig 通道接收到 SIGHUP 信號時,程序通過 upg.Upgrade() 來觸發進程重啟。
  • Upgrade() 會啟動一個新的進程,并優雅地停止舊進程。Upgrade 方法會啟動一個新的進程,新進程會從程序的入口點(即 main() 函數)重新開始執行。
  • 舊進程在調用 upg.Upgrade() 后并不會立即退出。它會繼續運行,處理現有的請求,直到新進程完全準備就緒

完成了什么?

  • 新的進程會在接收到 SIGHUP 信號時被啟動,而舊進程會在新的進程啟動后退出,完成平滑重啟。

創建 HTTP 路由和處理函數

這里只是此處服務的示例,期間想加任何其他邏輯都是可行的

mux := http.NewServeMux()
mux.HandleFunc("/", homeHandler)  // 首頁

做了什么?

  • 創建了一個新的 HTTP 路由器 mux,并注冊了 / 路徑的處理函數 homeHandler,它會響應根路徑的請求。

完成了什么?

  • 創建了基本的 HTTP 路由和處理器,為后續的服務啟動做準備。

啟動 HTTP 服務并監聽端口

ln, err := upg.Listen("tcp", "localhost:8081")
if err != nil {fmt.Printf("Error starting listener: %v\n", err)os.Exit(1)
}
defer ln.Close()

做了什么?

  • 使用 upg.Listen() 方法來啟動一個 TCP 監聽器,監聽 localhost:8081 端口。
  • upg.Listen() 會創建一個 listener,并確保即使進程重啟,新的進程會繼續監聽該端口,確保平滑重啟后服務不中斷。
  • 如果發生錯誤,程序會打印錯誤并退出。
  • 使用 defer 來確保程序退出時關閉監聽器,避免資源泄漏。

完成了什么?

  • 監聽 localhost:8081 端口,并準備好接收 HTTP 請求。

啟動 HTTP 服務的 goroutine

go func() {if err := http.Serve(ln, mux); err != nil {fmt.Printf("HTTP server error: %v\n", err)}
}()

做了什么?

  • 在一個新的 goroutine 中啟動 HTTP 服務。http.Serve() 使用前面創建的 ln(TCP 監聽器)和 mux(路由器)來啟動 HTTP 服務。
  • Serve() 會持續運行,直到出現錯誤或者服務器被停止。

完成了什么?

  • 啟動了一個 HTTP 服務器,監聽 localhost:8081 端口并處理請求。

等待新進程準備

if err := upg.Ready(); err != nil {fmt.Printf("Error marking process as ready: %v\n", err)os.Exit(1)
}
fmt.Println("服務啟動完成 pid: ", os.Getpid())

做了什么?

  • 調用 upg.Ready(),告訴 tableflip 管理器進程已經準備好,可以開始接受請求。
  • 如果 Ready() 返回錯誤,程序會打印錯誤并退出。

完成了什么?

  • 程序向 tableflip 發出通知,表明服務已經啟動并準備好接收請求。此時會通知舊進程在完成當前其他任務后關閉。

老進程等待退出信號

<-upg.Exit()
fmt.Println("服務退出")

做了什么?

  • upg.Exit() 返回一個只讀通道,<-upg.Exit() 會阻塞,直到進程退出信號到來。
  • 進程會一直等待,直到 tableflip 通知進程可以退出(即新進程啟動并完成任務,會在 ready 后通過進程間通信找到舊進程的 PID,發送系統信號通知它退出)。
  • 當接收到退出信號時,程序會繼續執行并打印 “服務退出”。

完成了什么?

  • 進程阻塞在這里,等待退出信號。upg.Exit() 會在新進程啟動后通過 tableflip 觸發進程退出。

總結

平滑重啟適用于需要精確控制進程重啟時機、避免中斷服務的場景。相比容器化環境的重啟機制,平滑重啟提供了更高的控制性和靈活性。在 CICD 流程中,也是一個不錯的選擇。

引用

https://github.com/cloudflare/tableflip

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/75908.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/75908.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/75908.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SQL SELECT DISTINCT 語句詳解:精準去重的藝術

在數據驅動的時代&#xff0c;數據質量直接影響決策的準確性。面對海量數據時&#xff0c;重復記錄如同沙礫中的金屑&#xff0c;既占用存儲空間&#xff0c;又干擾分析結果。SELECT DISTINCT 語句便是那把高效的篩子&#xff0c;助您快速剔除冗余&#xff0c;提取唯一值。本文…

16-產品經理-需求的評審

在創建需求的時候&#xff0c;有一個"不需要評審"的復選框&#xff0c;如果選中該復選框的話&#xff0c;需求的創建成功后狀態是激活的。 但大部分情況下面&#xff0c;需求還是需要評審的。 即使產品完全由一個人負責&#xff0c;也可以將一些不成熟的想法存為草…

計算機網絡學習前言

前言 該部分說明計算機網絡是什么&#xff1f;它有什么作用和功能&#xff1f;值不值得我們去學習&#xff1f;我們該如何學習&#xff1f;這幾個部分去大概介紹計算機網絡這門課程&#xff0c;往后會介紹計算機網絡的具體知識點。 1.計算機網絡是什么&#xff1f; 計算機網…

python全棧-JavaScript

python全棧-js 文章目錄 js基礎變量與常量JavaScript引入到HTML文件中JavaScript注釋與常見輸出方式 數據類型typeof 顯示數據類型算數運算符之加法運算符運算符之算術運算符運算符之賦值運算符運算符之比較運算符運算符之布爾運算符運算符之位運算符運算符優先級類型轉換 控制…

C語言一個偶數能表示為兩個素數之和

我們可以先找到其中的一個素數&#xff0c;然后用這個偶數減去這個素數就可以求得了。 運行結果:

vue實現大轉盤抽獎

用vue實現一個簡單的大轉盤抽獎案例 大轉盤 一 轉盤布局 <div class"lucky-wheel-content"><div class"lucky-wheel-prize" :style"wheelStyle" :class"isStart ? animated-icon : "transitionend"onWheelTransitionE…

Docker 核心組件

一、前言 Docker 已成為現代 DevOps 和微服務架構中的核心工具。為了更深入地理解它的工作機制&#xff0c;本文將系統介紹 Docker 的核心組件&#xff0c;配合結構圖直觀展示架構&#xff0c;同時拓展高級用法&#xff0c;幫助讀者全面掌握容器化技術的內核。 二、Docker 核心…

ModuleNotFoundError: No module named ‘pandas‘

在使用Python繪制散點圖表的時候&#xff0c;運行程序報錯&#xff0c;如圖&#xff1a; 報錯顯示Python 環境中可能沒有安裝 pandas 庫&#xff0c;執行pip list命令查看&#xff0c;果然沒有安裝pandas 庫&#xff0c;如圖&#xff1a; 執行命令&#xff1a;python -m pip in…

(51單片機)矩陣按鍵密碼鎖表白(C語言代碼編撰)(矩陣按鍵教程)(LCD1602淺教程)

目錄 源代碼 main.c MatrixKey.c MatrixKey.h LCD1602.c LCD1602.h Delay.c Delay.h 運行效果圖&#xff1a; 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a; 代碼解析與教程&#xff1a; 延時函數Delay LCD1602 MatrixKey模塊 源代…

檢測手機插入USB后,自動啟動scrcpy的程序

博主寫了一個小工具scrcpyAuto&#xff0c;檢測手機插入電腦USB后&#xff0c;自動啟動scrcpy。 這樣只要程序運行&#xff0c;手機接入主機就會有scrcpy大屏出現&#xff0c;方便了很多。 1、程序會最小化到系統托盤中。 2、博主沒有設計得太復雜&#xff0c;所以程序開機啟動…

使用Scade實現神經網絡算法

在ERTS2022中&#xff0c;ANSYS 發表了使用Scade實現神經網絡AI算法的相關工作。論文題目為《Programming Neural Networks Inference in a Safety-Critical Simulation-based Framework》 背景與挑戰 神經網絡在安全關鍵系統中的應用&#xff1a;隨著嵌入式系統中自主性的引入…

Next.js + SQLite 項目 Docker 生產環境部署方案

以下是完整的 Next.js SQLite 項目 Docker 生產環境部署方案&#xff1a; 1. 項目結構準備 your-project/ ├── prisma/ │ ├── schema.prisma │ └── migrations/ ├── app/ ├── lib/ ├── Dockerfile ├── docker-compose.yml ├── .dockerignore └…

MCU軟件開發使用指針有哪些坑?

目錄 1、空指針訪問 2、野指針&#xff08;未初始化的指針&#xff09; 3、指針越界 4、內存泄漏 5、懸空指針 6、指針類型不匹配 7、多任務環境中的指針訪問 8、對齊問題 在MCU軟件開發中&#xff0c;使用指針雖然可以提高程序的靈活性和性能&#xff0c;但也存在許多…

【SPSS/EXCEl】主成分分析構建__綜合評價指數

學習過程中實驗操作的記錄 1.數據準備和標準化&#xff1a; (1)區分正負相關性:判斷每個因子是正向指標還是負向指標,計算每個的最大值和最小值 (2) 標準化: Min-Max標準化 Min-Max標準化&#xff08;最大最小值法&#xff09;&#xff1a; 將數據映射到指定的區間&#xff…

selenium安裝,以及瀏覽器驅動下載詳細步驟

1.下載谷歌瀏覽器Chromedriver 查看谷歌瀏覽器版本 2.去官網下載Chromedriver 114之前的版本鏈接chromedriver.storage.googleapis.com/index.html 選擇和瀏覽器版本較接近的點擊進行下載 125之后的版本鏈接Chrome for Testing availability (googlechromelabs.github.io)&a…

LabVIEW 油井動液面在線監測系統?

項目背景 傳統油井動液面測量依賴人工現場操作&#xff0c;面臨成本高、效率低、安全風險大等問題。尤其在偏遠地區或復雜工況下&#xff0c;測量準確性與時效性難以保障。本系統通過LabVIEW虛擬儀器技術實現硬件與軟件深度融合&#xff0c;為油田智能化轉型提供實時連續監測解…

C++標準庫 —— round 函數用法詳解

round 是 C/C 標準庫中的一個數學函數&#xff0c;用于對浮點數進行四舍五入取整。以下是它的詳細用法說明&#xff1a; 目錄 1. 基本語法 2. 功能描述 3. 使用示例 示例1&#xff1a;基本用法 示例2&#xff1a;保留小數位 4. 相關函數對比 5. 注意事項 6. 實際應用場景…

嵌入式C語言11(宏/程序的編譯過程)

宏 ? 基本概念 C語言中可以利用宏定義實現文本的快速替換&#xff0c;注意&#xff1a;宏定義是單純的文本替換&#xff0c;不檢查語法是否合法。 C語言標準中提供了很多的預處理指令&#xff0c;比如#include、#pragma…以#開頭的都屬于預處理指令。 預處理指令指的是在…

【湖南大學】2025我們該如何看待DeepSeek

大家好&#xff0c;我是櫻木。 DeepSeek 官方網站&#xff1a;https://www.deepseek.com/ 一、DeepSeek 到底是什么&#xff1f; TA 到底厲害在哪里&#xff1f; 故事從 ChatGPT 說起 去年我們看到 Open AI 發布ChatGPT 后&#xff0c;全球的注意力到了 AI 身上。 我們來拆…

【區塊鏈安全 | 第三十三篇】備忘單

文章目錄 備忘單操作符優先級備忘單ABI 編碼和解碼函數bytes 和 string 的成員Address 的成員區塊與交易屬性校驗和斷言數學和加密函數合約相關類型信息函數可見性說明符修飾符備忘單 操作符優先級備忘單 以下是操作符的優先級順序,按評估順序列出: 優先級描述操作符1后綴遞…