目錄
為什么需要優雅關閉?
什么是 SIGINT 和 SIGTERM?
如何實現優雅關閉(以 C++ 為例)
示例代碼(gRPC 服務 + Boost 信號監聽):
優雅關閉時的清理內容通常包括:
與 SIGKILL 的區別?
總結
在構建后端服務、微服務或守護進程時,“優雅關閉(Graceful Shutdown)” 是一個經常被忽視但極其重要的實踐。本文將從原理、實際應用、最佳實踐等角度,詳細介紹優雅關閉的實現方式,尤其是如何使用 SIGINT
/ SIGTERM
信號來實現它。
為什么需要優雅關閉?
在實際部署中,一個服務可能隨時會被下達“關機”命令,比如:
-
手動
Ctrl + C
(開發調試時常見); -
使用
kill
命令終止服務; -
容器環境下被 Kubernetes 調用
SIGTERM
優雅終止; -
自動部署時滾動重啟、藍綠部署等。
如果不處理關閉邏輯,可能會發生:
-
數據丟失(寫入尚未完成);
-
客戶端連接被強制中斷;
-
文件或資源句柄未釋放;
-
線程或協程未清理,內存泄漏;
-
日志未刷盤;
什么是 SIGINT 和 SIGTERM?
-
SIGINT
:中斷信號,通常是用戶按下Ctrl + C
發出的; -
SIGTERM
:終止信號,默認kill
命令發送(非kill -9
); -
這些信號是可以被捕獲、處理或忽略的,適合用于執行清理操作。
如何實現優雅關閉(以 C++ 為例)
這里我們用 C++ 結合 Boost.Asio 來實現監聽信號并優雅關閉:
示例代碼(gRPC 服務 + Boost 信號監聽):
boost::asio::io_context io_context;
boost::asio::signal_set signals(io_context, SIGINT, SIGTERM);// 綁定信號處理回調
signals.async_wait([&server](const boost::system::error_code& error, int signal_number) {if (!error) {std::cout << "Received signal: " << signal_number << ". Shutting down server..." << std::endl;server->Shutdown(); // 調用 gRPC 的 Shutdown 方法優雅關閉服務}
});// 將 io_context 放入后臺線程中監聽信號
std::thread([&io_context]() {io_context.run();
}).detach();
優雅關閉時的清理內容通常包括:
項目 | 清理操作 |
---|---|
數據庫連接池 | 關閉連接 / 歸還連接 |
文件 | 刷新緩存并關閉句柄 |
日志 | 將緩存日志寫入磁盤 |
gRPC/HTTP 服務器 | 等待當前請求處理完成,優雅關閉監聽 |
線程池 / 協程池 | 通知停止任務、等待處理完畢 |
消息隊列消費者 | 停止訂閱、確認已處理消息 |
臨時文件或資源 | 刪除、釋放鎖等 |
與 SIGKILL 的區別?
信號 | 可處理? | 用途 |
---|---|---|
SIGTERM (15) | ? 可攔截 | 默認 kill 信號,推薦用于優雅終止 |
SIGINT (2) | ? 可攔截 | Ctrl+C,用戶觸發 |
SIGKILL (9) | ? 無法攔截 | 強制終止,立即結束,無機會清理資源 |
總結
-
用
signal()
或更安全的boost::asio::signal_set
監聽信號 -
處理 SIGINT 和 SIGTERM,避免只處理一個
-
服務必須支持異步關閉機制(gRPC 的
Shutdown()
、HTTP 的stop()
) -
保留資源清理接口(連接池、日志等)
-
結合主線程
wait()
和后臺信號監聽線程工作