Go 1.8 为我们带来了 http 服务优雅退出的方式:Server.Shutdown
。对于该方法,代码中的解释是:
Shutdown
方法会在不干扰任何活跃连接的情况下关闭服务器。首先,它会关闭所有开着的监听器,然后关闭所有空闲连接,接着无限等待所有连接变成空闲状态,最后关闭。如果提供的
context.Context
对象在关闭完成之前过期了,那么,Shutdown
方法返回该Context
对象的错误信息。否则,它会将正在关闭的服务器的底层监听器的错误返回(如果有的话)。一旦调用了
Shutdown
方法,Serve
、ListenAndServe
和ListenAndServeTLS
会立即返回ErrServerClosed
。需要确保程序不退出,而是等待Shutdown
返回。
Shutdown
并不会像WebSockets
那样尝试关闭或者等待被劫持的连接。Shutdown
的调用者应该在需要的时候,挨个通知这些长期存在的连接关闭,并且等待它们关闭。
接下来我们来看下 Shutdown
方法的实现。
1 | func (srv *Server) Shutdown(ctx context.Context) error { |
基于 go 1.9.2
上面的代码做了几件事: 1. 将 Server 的字段 inShutdown int32
加一。这个字段用于 Server 非公开方法 shuttingDown
中,非零表示 Server 正在关闭。 2. 调用 closeListenersLocked
方法,关闭所有打开的监听器。 3. 调用 closeDoneChanLocked
方法,关闭 doneChan chan struct{}
。从而通知 Serve
、ListenAndServe
和 ListenAndServeTLS
退出并返回 ErrServerClosed
错误。 4. 将所有使用 RegisterOnShutdown
方法注册的方法(保存在 onShutdown []func()
中)放在单独的 goroutine 中调用,并且不等待方法返回。这些方法不应该等待关闭完成。 5. 创建一个定时器,定时时间由 shutdownPollInterval
指定,默认是 500ms。目前没有可以修改该值的方法。 6. 每到步骤 5 创建的定时时间调用一次 closeIdleConns
方法关闭空闲连接。无限循环直到该方法返回 true(表示服务器已经处于静默模式),或者当 ctx
过期了。如果是前者,则返回步骤 2 的执行结果。后者则返回 ctx
的错误信息。
由此可见,Shutdown
方法主要是做了两件事:关闭监听器和关闭空闲连接。
前者直接调用监听器的 Close
方法。这里需要注意的是,一旦该方法调用失败,只会保存错误信息,并且继续调用下一个监听器的 Close
方法。如果存在多个监听器关闭错误,也只会返回其中一个错误。
我们来看看后者的实现:
1 | // closeIdleConns closes all idle connections and reports whether the |
上面的代码做了几件事: 1. 检查 activeConn map[*conn]struct{}
中的每个连接状态 2. 如果连接不是 idle 的,那么设置服务器为非静默模式,然后继续检查下一个连接 3. 如果连接已经是 idle 的了,那么关闭该连接,并且将其从 activeConn
中删除 4. 最后返回服务器是否处于静默状态。
和一般的 Close
方法进行对比:
1 | // Close immediately closes all active net.Listeners and any |
区别如下: 1. Shutdown
方法会调用注册的关闭时执行的方法,而 Close
方法没有。 2. Shutdown
方法会等待所有活跃连接变成 idle 状态才关闭连接,而 Close
方法则直接关闭连接。