声明:本人原创文章,详细内容已发布在我的微信个人技术公众号---网络技术修炼,公众号总结普及网络基础知识,包括基础原理、网络方案、开发经验和问题定位案例等,欢迎关注。
概述软件工程中持续迭代和更新是必不可少的,在服务端软件更新时,保持服务的连续性是一项关键任务。本文将从技术角度解析服务端软件更新过程如何实现不停止服务的重要功能。
在进行热升级时,进程的代码和数据都是非常重要的。为了实现代码的更新,同时又不丢失有用的数据,需要采取一些措施。有用的数据包括内存中的数据和文件描述符。对于内存中的数据,例如配置信息,可以通过将其落盘到配置文件中来实现保留。这样,在升级过程中,新的进程可以读取配置文件并继续使用之前的配置。而对于文件描述符,可以采用一种叫做UNIX域套接字的机制,在进程之间进行迁移。通过这种方式,新进程可以接管原来进程的文件描述符,从而保持之前打开的文件和网络连接的状态。在某些情况下,项目可能会选择不迁移文件描述符,而是通过让新旧进程共同处理一段时间的请求来逐步过渡。这样,新进程可以逐渐接收和处理新的请求,而老进程则继续处理旧的请求,直到所有请求都由新进程处理完毕。
(资料图)
另外,为了减轻对客户端的影响,还可以采用一些HTTP协议的特性。例如,在HTTP1中可以使用"Connection: Close"头部字段,告知客户端断开连接并重新连接。而在HTTP2中,可以使用Goaway帧来类似地通知客户端断开连接。这样一来,客户端就能够及时与新进程建立新的连接,以继续进行请求和响应的处理。
通过这些措施和优化方法,可以实现热升级过程中代码更新和数据保留的目标,并尽可能减少对系统和客户端的影响。
详解通过fork + execve实现无损升级典型项目nginx
nginx为例解析交互流程先不停掉老进程,启动新进程。老进程继续处理仍然没有处理完的请求,但不再接受新请求。新进程接受新请求。老进程处理完所有存量请求,关闭所有连接,退出。信号支持官方文档:http://nginx.org/en/docs/control.html
nginx中master进程为管理进程,woker进程为master进程fork出的子进程,是处理网络的进程。
master进程支持的信号
TERM,INT | 快速退出 |
QUIT | 优雅退出master+worker进程(worker进程处理完存量请求再退出) |
KILL | 强子终止进程 |
HUP | 使用新的的配置启动worker进程,并优雅退出老的worker进程 |
USR1 | 重新打开日志文件 |
USR2 | 升级可执行文件(即启动新的master进程) |
WINCH | 优雅退出woker进程 |
worker进程支持的信号:
TERM,INT | 快速退出 |
QUIT | 优雅退出(处理完存量请求再退出) |
USR1 | 重新打开日志文件 |
#ps -ef | grep nginxroot 82556 1 0 11:58 ? 00:00:00 nginx: master process ./sbin/nginxnginx 82562 82556 0 11:58 ? 00:00:00 nginx: worker processnginx 82563 82556 0 11:58 ? 00:00:00 nginx: worker processnginx 82564 82556 0 11:58 ? 00:00:00 nginx: worker processnginx 82565 82556 0 11:58 ? 00:00:00 nginx: worker processnginx 82566 82556 0 11:58 ? 00:00:00 nginx: worker processnginx 82567 82556 0 11:58 ? 00:00:01 nginx: worker processnginx 82569 82556 2 11:58 ? 00:00:03 nginx: worker processnginx 82570 82556 14 11:58 ? 00:00:24 nginx: worker process#cat /app/nginx/logs/nginx.pid82556可以看出nginx.pid记录的是当前master的进程号。将旧Nginx二进制换成新Nginx二进制(注意备份旧二进制)。向master进程发送USR2信号。
kill -USR2 `cat /app/nginx/logs/nginx.pid`nginx收到信号会创建新master并fork出新worker,此时新老共存,都会处理请求。
执行后结果
#ps -ef | grep nginxroot 82556 1 0 11:58 ? 00:00:00 nginx: master process ./sbin/nginxnginx 82562 82556 0 11:58 ? 00:00:01 nginx: worker processnginx 82563 82556 0 11:58 ? 00:00:01 nginx: worker processnginx 82564 82556 0 11:58 ? 00:00:01 nginx: worker processnginx 82565 82556 0 11:58 ? 00:00:01 nginx: worker processnginx 82566 82556 0 11:58 ? 00:00:01 nginx: worker processnginx 82567 82556 0 11:58 ? 00:00:02 nginx: worker processnginx 82569 82556 2 11:58 ? 00:00:06 nginx: worker processnginx 82570 82556 13 11:58 ? 00:00:43 nginx: worker processroot 85710 82556 0 12:04 ? 00:00:00 nginx: master process ./sbin/nginxnginx 85716 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85717 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85718 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85719 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85720 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85721 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85723 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85724 85710 0 12:04 ? 00:00:00 nginx: worker process#cat /app/nginx/logs/nginx.pid85710可以看出nginx.pid已经变成新master进程号#cat /app/nginx/logs/nginx.pid.oldbin82556nginx.pid.oldbin存放老master进程号。向老master进程发送WINCH信号。
kill -WINCH `cat /app/nginx/logs/nginx.pid.oldbin`nginx的老master进程收到信号会给所有老worker进程发送信号,老worker执行优雅退出。老worker收到优雅退出信号后不再接收新请求,只处理存量请求,处理完后进程退出。
#ps -ef | grep nginxroot 82556 1 0 11:58 ? 00:00:00 nginx: master process ./sbin/nginxnginx 82569 82556 1 11:58 ? 00:00:06 nginx: worker process is shutting downnginx 82570 82556 11 11:58 ? 00:00:43 nginx: worker process is shutting downroot 85710 82556 0 12:04 ? 00:00:00 nginx: master process ./sbin/nginxnginx 85716 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85717 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85718 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85719 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85720 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85721 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85723 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85724 85710 0 12:04 ? 00:00:00 nginx: worker process
此过程要不停有请求访问到nginx才能看到worker优雅退出过程,一段时间后存量请求全部处理完毕。
#ps -ef | grep nginxroot 82556 1 0 11:58 ? 00:00:00 nginx: master process ./sbin/nginxroot 85710 82556 0 12:04 ? 00:00:00 nginx: master process ./sbin/nginxnginx 85716 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85717 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85718 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85719 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85720 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85721 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85723 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85724 85710 0 12:04 ? 00:00:00 nginx: worker process检查是否回滚一段时间后,老woker请求全部处理完,就变成了新老master、新worker共存,此时老master并没有关闭listen sockets,如果新二进制有问题还有办法回滚。回滚方法:方法1向老master发送HUP信号。老master收到HUP信号会创建worker进程。向新master发送QUIT信号。新master收到QUIT会退出所有新worker和新master进程。方法2向新master发送TERM信号。nginx新进程收到这个信号,对应master和worker会退出,同时老master会创建出老worker继续工作。如果不需要回滚,向老master发送QUIT信号。
kill -QUIT `cat /app/nginx/logs/nginx.pid.oldbin`老master收到这个信号会退出。
#ps -ef | grep nginxroot 85710 1 0 12:04 ? 00:00:00 nginx: master process ./sbin/nginxnginx 85716 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85717 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85718 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85719 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85720 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85721 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85723 85710 0 12:04 ? 00:00:00 nginx: worker processnginx 85724 85710 0 12:04 ? 00:00:00 nginx: worker process源码
nginx信号处理函数:ngx_signal_handler
unix domain sockets典型项目envoy
mosn
原理概括linux环境可以使用下面函数在进程间传递fd。
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);mosn为例解析交互流程
ps:下面代码均以v1.5.0版本为例。
listen fd迁移涉及Domain Socket:
reconfig.sock记录老进程的监听
listen.sock记录新进程的监听
流程:
老进程启动时候会执行ReconfigureListener函数,这里面监听reconfig.sock并通过写一个字节(uc.Write([]byte{0}))阻塞,直到有新进程启动并执行read才会继续往下执行。新进程init-->inheritConfig-->IsReconfigure通过uc.Read(buf)触发老进程执行reconfigure流程。老进程通过reconfig.sock向新进程发送fd。ReconfigureHandler
sendInheritListeners:老进程将已经存在的 fd 通过 listen.sock 发送给新进程。
shutdownServers:老进程不再接收新连接,并优雅关闭。
WaitConnectionsDone:处理完存量请求后退出。
新进程接收老进程的fd并处理:GetInheritListeners。长连接迁移涉及Domain Socket:conn.sock
流程:
新进程启动一个协程运行TransferServer,将监听conn.sock。老进程通过transferRead和transferWrite进入长链接迁移过程。参考文档nginx官方文档:http://nginx.org/en/docs/control.html
MOSN 平滑升级原理解析:https://mosn.io/docs/products/structure/smooth-upgrade/
MOSN 源码解析 - reconfig 机制:https://mosn.io/blog/code/mosn-reconfig-mechanism/
浅谈长连接的平滑重启:https://www.infoq.cn/article/Qfkq8Wk4FtVot46LaVkR?source=app_share
Nginx vs Envoy vs Mosn 平滑升级原理解析:https://ms2008.github.io/2019/12/28/hot-upgrade/
上一篇:走进这个群体,感悟“粮”心
下一篇:最后一页
河北20条措施助力开放发展先行区建设
2023年01月06日公告发布
红薯一定不能生吃,尤其是肠胃不适的人更不能生吃红薯。因为红薯中淀粉含量多,淀粉中的淀粉颗粒如果不经高温破坏,很难被人体消化,所以吃红
在12月31号晚上这天,打开朋友圈大家都在告别2022、迎接2023,我却想不到任何值得发的内容。没有外出体会元旦的节日氛围,也没有观看任何跨年活动
1月5日A股收盘,沪指涨1 01%,报收3155 22点;深成指涨2 13%,报收11332 01点;创业板指涨2 76%,报收2399 46点。三大指数今日高
*ST天马(002122)01月03日在投资者关系平台上答复了投资者关心的问题。
1月6日,嘉实基金公告,嘉实京东仓储物流封闭式基础设施证券投资基金(简称:嘉实京东仓储基础设施REIT)公众投资者认购总量已超过公众发售总量
X 关闭
X 关闭