golang 程序启动一个 http 服务时,若服务被意外终止或中断,会让现有请求链接忽然中断,未处理完成的任务也会出现不可预知的错误,这样即会形成服务硬终止;为了解决硬终止问题咱们但愿服务中断或退出时将正在处理的请求正常返回而且等待服务中止前做的一些必要的处理工做。golang

咱们能够看一个硬终止的例子:浏览器

启动服务后,咱们能够访问 http://127.0.0.1:8080 页面等待 5s 会输出一个 “Hello world!”, 咱们能够尝试 Ctrl+C 终止程序,能够看到浏览器马上就显示没法链接,这表示链接马上就中断了,退出前的请求也未正常返回。函数

在 Golang1.8 之后 http 服务有个新特性 Shutdown 方法能够优雅的关闭一个 http 服务, 该方法须要传入一个 Context 参数,当程序终止时其中不会中断活跃的链接,会等待活跃链接闲置或 Context 终止(手动 cancle 或超时)最后才终止程序,官方文档详见:https://godoc.org/net/http#Server.Shutdownspa

 

在具体用应用中咱们能够配合 signal.Notify 函数来监听系统退出信号来完成程序优雅退出;code

特别注意:server.ListenAndServe() 方法在 Shutdown 时会马上返回,Shutdown 方法会阻塞至全部链接闲置或 context 完成,因此 Shutdown 的方法要写在主 goroutine 中。server

 

优雅退出实验1:blog

咱们建立了一个 listenSignal 函数来监听程序退出信号 listenSignal 函数中的 select 会一直阻塞直到收到退出信号,而后执行 Shutdown(ctx) 。事件

能够看到,咱们是从新开启了一个 goroutine 来启动 http 服务监听,而 Shutdown(ctx) 在主 goroutine 中,这样才能等待全部链接闲置后再退出程序。文档

启动上述程序,咱们访问  http://127.0.0.1:8080 页面等待 5s 会输出一个 “Hello world!” 在等待期间,咱们能够尝试 Ctrl+C 关闭程序,能够看程序控制台会等待输出后才打印 http shutdown 同时浏览器会显示输出内容;而关闭程序以后再新开一个浏览器窗口访问 http://127.0.0.1:8080 则新开的窗口直接断开没法访问。(这些操做须要在 5s 内完成,能够适当调整处理时间方便咱们观察实验结果)get

经过该实验咱们能看到,Shutdown(ctx) 会阻止新的链接进入并等待活跃链接处理完成后再终止程序,达到优雅退出的目的。

固然咱们还能够进一步证实 Shutdown(ctx) 除了等待活跃链接的同时也会监听 Context 完成事件,两者有一个触发都会触发程序终止;

咱们将代码稍做修改以下:

咱们将 http 服务处理修改为等待 10s, 监听到退出事件后 ctx 修改为 3s 超时的 Context,运行上述程序,而后 Ctrl+C 发送结束信号,咱们能够直观的看到,程序在等待 3s 后就终止了,此时即便 http 服务中的处理还没完成,程序也终止了,浏览器中也直接中断链接了。

须要注意的问题:咱们在 HandleFunc 中编写的处理逻辑都是在主 goroutine 中完成的和 Shotdown 方法是一个同步操做,所以 Shutdown(ctx) 会等待完成,若是咱们的处理逻辑是在新的 goroutine 中或是一个像 Websock 这样的长链接,则Shutdown(ctx) 不会等待处理完成,若是须要解决这类问题仍是须要利用 sync.WaitGroup 来进行同步等待。

 

技术总结:

1. Shutdown 方法要写在主 goroutine 中;

2.在主 goroutine 中的处理逻辑才会阻塞等待处理;

3.带超时的 Context 是在建立时就开始计时了,所以须要在接收到结束信号后再建立带超时的 Context。