什么是平滑重启 当线上代码需要更新时,我们平时一般的做法需要先关闭服务然后再重启服务. 这时线上可能存在大量正在处理的请求, 这时如果我们直接关闭服务会造成请求全部 中断, 影响用户体验; 在重启重新提供服务之前, 新请求进来也会502. 这时就出现两个需要解决的问题:
老服务正在处理的请求必须处理完才能退出(优雅退出) 新进来的请求需要正常处理,服务不能中断(平滑重启) 本文主要结合linux和Golang中相关实现来介绍如何选型与实践过程.
优雅退出 在实现优雅重启之前首先需要解决的一个问题是如何优雅退出:
我们知道在go 1.8.x后,golang在http里加入了shutdown方法,用来控制优雅退出。
社区里不少http graceful动态重启,平滑重启的库,大多是基于http.shutdown做的。
http shutdown 源码分析 先来看下http shutdown的主方法实现逻辑。用atomic来做退出标记的状态,然后关闭各种的资源,然后一直阻塞的等待无空闲连接,每500ms轮询一次。
var shutdownPollInterval = 500 * time.Millisecond func (srv *Server) Shutdown(ctx context.Context) error { // 标记退出的状态 atomic.StoreInt32(&srv.inShutdown, 1) srv.mu.Lock() // 关闭listen fd,新连接无法建立。 lnerr := srv.closeListenersLocked() // 把server.go的done chan给close掉,通知等待的worekr退出 srv.closeDoneChanLocked() // 执行回调方法,我们可以注册shutdown的回调方法 for _, f := range srv.onShutdown { go f() } // 每500ms来检查下,是否没有空闲的连接了,或者监听上游传递的ctx上下文。 ticker := time.NewTicker(shutdownPollInterval) defer ticker.