服务如何实现优雅重启

前言

平滑重启主要考虑两点:

  • 如何做到不中断接收新请求

  • 老的请求不能被中断

 

实现思路

主要思路就是要解决新老进程切换过程中listener文件描述符和现有连接描述符的迁移,之前有一篇文章讲了如何通过unixSocket来传递文件描述符,这里也会用到这个技术, 具体的迁移流程如下:

 

1、老进程在启动时,启动tcp listen监听,就是开启对外服务的端口,如果有请求过来,开启一个协程处理这个请求。

2、老进程启动一个unix Socket监听,通过switch.sock和新进程进行通信,一旦由请求过来,就把自己的listener文件描述符发给新进程,然后通过发送信号触发迁移流程,同时等待一段时间关闭自己的listener,进程退出。

3、新进程在启动前,会尝试连接旧进程的switch.sock,如果通信成功,就知道它需要开始接收旧的listener 和 connection了,此时会分别启动listen.sock监听和conn.sock监听,来获取旧进程的listener信息和conn信息。如果收到了listener信息,则会用这个listener启动服务。

4、接步骤1,协程在处理请求的过程中,如果收到了迁移信号,则会判断是否将现有的客户端连接发给新进程,如果连接里还有可读的数据,则需要迁移。

 

到此整个迁移流程就完毕了。

 

测试

代码在 https://gitee.com/zqwlai/proxy,服务端在处理TCP请求时,是以“#”为分隔符来处理的,会返回“进程标志 reply:截断部分#”。如果客户端没有发送“#”,则连接会一直保持(阻塞),这样可以模拟TCP的长连接,方便连接迁移的测试。

 

先启动服务:

go run main.go -l :8081 -p hello  #-p表示进程标识符

 

 

客户端采用telnet的方式与服务端通信

 

 

可以看到当发送“123#456”时,会返回“hello reply:123#”,同时查看连接状态:

$ netstat -an |grep 8081
tcp        0      0 127.0.0.1:38514         127.0.0.1:8081          ESTABLISHED
tcp6       0      0 :::8081                 :::*                    LISTEN     
tcp6       0      0 127.0.0.1:8081          127.0.0.1:38514         ESTABLISHED

 

 

然后再次启动服务端(也可以用fork子进程的方式来实现,需要代码做下改动):

go run main.go -l :8081 -p BBB


再次发送一个“#”,可以看到结果能被正确返回

 

 

 

posted @ 2021-09-20 11:46  独揽风月  阅读(239)  评论(0编辑  收藏  举报