通常應用在進行發布,重啟或者服務器遷移時都會讓應用進程關閉,我們更希望在可預知的進程關閉時,讓正常處理的任務盡量處理完成后再關閉,比如數據庫操作,數據同步等等,讓進程關閉重啟更加的平滑優雅;
Orange 框架已封裝了優雅退出相關處理邏輯,按照如下方式即可實現優雅退出。
### 實現說明
- http服務基于 http 的 shutdown 方法實現;
- 自定義業務邏輯基于 `signal.Notify` 信號監聽,等待處理來實現;
### 快速開始
- 退出后,等待處理完成
~~~
// 通過該方法添加相關處理邏輯, 進程退出后會等待 `ExitWaitFunDo` 中的邏輯處理完成后再退出;
app.ExitWaitFunDo(func() {
// run something
})
~~~
- 進程退出后置操作
~~~
// 通過該方法可以在進程退出后調用到 `AppDefer` 中的方法
app.AppDefer(func() {
// run when app shutdown
})
~~~
### 防止僵尸進程
如果在 `ExitWaitFunDo` 和 `AppDefer` 中定義了死循環或長時間的任務,會導致進程不能正常退出或重啟形成僵尸進程,為了防止此類情況,框架從底層設計上會防止該情況,進程監聽到退出信號后,等待配置的指定超時時間還沒處理完成進程也會強制退出;
~~~
[app]
maxWaitSecond=120 # 最大等待時間
~~~
### 捕獲退出信號
如上述等待退出相關方法無法滿足特定需求時,可以通過獲取退出信號的方法完成自定義需求;
Orange 框架通過 golang 系統包 `signal.Notify` 方法獲取退出信號,如應用中重復使用該方法會導致框架獲取退出信號異常,因此框架對退出信號進行了封裝,通過框架中的方法去 `app.ListenStop` 獲取退出信號;
退出信號通過監聽方法調用時傳入的 channel 類型來傳遞,通過一個新的協程 `select` 方法來獲取信號,具體代碼如下。
~~~
// 監聽退出信號
stopSig := make( chan app.StopSignal, 1)
go app.ListenStop(stopSig)
// 當應用退出時,會收到一個信號
go func() {
select {
case <-stopSig:
fmt.Println("get stopSign ")
}
}()
~~~
### 使用建議
- 不建議在控制器中使用 `AppDefer` 和 `ListenStop` 方法,因為調用 http 服務時每次請求都是一個新的 `goroutine` 每次請求都會新增一個重復的后置操作和監聽信號;
- `AppDefer` 和 `ExitWaitFunDo` 中的處理時長應當是可預知的,避免因超時原因導致中斷正常的處理邏輯。