通常应用在进行发布,重启或者服务器迁移时都会让应用进程关闭,我们更希望在可预知的进程关闭时,让正常处理的任务尽量处理完成后再关闭,比如数据库操作,数据同步等等,让进程关闭重启更加的平滑优雅; Orange 框架已封装了优雅退出相关处理逻辑,按照如下方式即可实现优雅退出。

# 实现说明

  • http服务基于 http 的 shutdown 方法实现;
  • 自定义业务逻辑基于 signal.Notify 信号监听,等待处理来实现;

# 快速开始

  • 退出后,等待处理完成
// 通过该方法添加相关处理逻辑, 进程退出后会等待  `ExitWaitFunDo`  中的逻辑处理完成后再退出;
app.ExitWaitFunDo(func() {
   // run something
})
  • 进程退出后置操作
// 通过该方法可以在进程退出后调用到 `AppDefer` 中的方法
app.AppDefer(func() {
    // run when app shutdown
})

# 防止僵尸进程

如果在 ExitWaitFunDoAppDefer 中定义了死循环或长时间的任务,会导致进程不能正常退出或重启形成僵尸进程,为了防止此类情况,框架从底层设计上会防止该情况,进程监听到退出信号后,等待配置的指定超时时间还没处理完成进程也会强制退出;

[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 ")
   }
}()

# 使用建议

  • 不建议在控制器中使用 AppDeferListenStop 方法,因为调用 http 服务时每次请求都是一个新的 goroutine 每次请求都会新增一个重复的后置操作和监听信号;
  • AppDeferExitWaitFunDo 中的处理时长应当是可预知的,避免因超时原因导致中断正常的处理逻辑。