在上一篇博客介绍TOML配置的时候,讲到了通过信号通知重载配置。我们在这一篇中介绍下如何的平滑重启server。
与重载配置相同的是我们也需要通过信号来通知server重启,但关键在于平滑重启,如果只是简单的重启,只需要kill掉,然后再拉起即可。平滑重启意味着server升级的时候可以不用停止业务。
我们先来看下Github上有没有相应的库解决这个问题,然后找到了如下三个库:
- facebookgo/grace - Graceful restart & zero downtime deploy for Go servers.
- fvbock/endless - Zero downtime restarts for go servers (Drop in replacement for http.ListenAndServe)
- jpillora/overseer - Monitorable, gracefully restarting, self-upgrading binaries in Go (golang)
我们分别来学习一下,下面只讲解http server的重启。
使用方式
我们来分别使用这三个库来做平滑重启的事情,之后来对比其优缺点。
这三个库的官方都给了相应的例子,例子如下:
但三个库官方的例子不太一致,我们来统一一下:
- grace例子 https://github.com/facebookgo/grace/blob/master/gracedemo/demo.go
- endless例子 https://github.com/fvbock/endless/tree/master/examples
- overseer例子 https://github.com/jpillora/overseer/tree/master/example
我们参考官方的例子分别来写下用来对比的例子:
grace
|
endless
|
overseer
|
对比
对比示例的操作步骤
- 分别构建上面的示例,并记录pid
- 调用API,在其未返回时,修改内容(Hello World -> Hello Harry),重新构建。查看旧API是否返回旧的内容
- 调用新API,查看返回的内容是否是新的内容
- 查看当前运行的pid,是否与之前一致
下面给一下操作命令
|
对比结果
示例 | 旧API返回值 | 新API返回值 | 旧pid | 新pid | 结论 |
---|---|---|---|---|---|
grace | Hello world | Hello Harry | 2096 | 3100 | 旧API不会断掉,会执行原来的逻辑,pid会变化 |
endless | Hello world | Hello Harry | 22072 | 22365 | 旧API不会断掉,会执行原来的逻辑,pid会变化 |
overseer | Hello world | Hello Harry | 28300 | 28300 | 旧API不会断掉,会执行原来的逻辑,主进程pid不会变化 |
原理分析
可以看出grace和endless是比较像的。
- 监听信号
- 收到信号时fork子进程(使用相同的启动命令),将服务监听的socket文件描述符传递给子进程
- 子进程监听父进程的socket,这个时候父进程和子进程都可以接收请求
- 子进程启动成功之后,父进程停止接收新的连接,等待旧连接处理完成(或超时)
- 父进程退出,升级完成
overseer是与grace和endless有些不同,主要是两点:
- overseer添加了Fetcher,当Fetcher返回有效的二进位流(io.Reader) 时,主进程会将它保存到临时位置并验证它,替换当前的二进制文件并启动。
Fetcher运行在一个goroutine中,预先会配置好检查的间隔时间。Fetcher支持File、GitHub、HTTP和S3的方式。详细可查看包package fetcher - overseer添加了一个主进程管理平滑重启。子进程处理连接,能够保持主进程pid不变。
如下图表示的很形象
自己实现
我们下面来尝试自己实现下第一种处理,代码如下,代码来自《热重启golang服务器》:
|
代码可参考:https://github.com/CraryPrimitiveMan/go-in-action/tree/master/ch4
关于这一部分,个人的理解也不是特别深入,如果又不正确的地方请大家指正。
参考文章
热重启golang服务器
overseer, 進入(golang),Monitorable,正常重新啟動,自升級二進位文件
许可
本文章系孙广君著,采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议许可。