首页 > 其他分享 >服务器无损升级技术解析

服务器无损升级技术解析

时间:2023-07-01 19:46:55浏览次数:52  
标签:00 12 85710 process worker nginx 服务器 解析 无损

声明:本人原创文章,详细内容已发布在我的微信个人技术公众号---网络技术修炼,公众号总结普及网络基础知识,包括基础原理、网络方案、开发经验和问题定位案例等,欢迎关注。

概述

软件工程中持续迭代和更新是必不可少的,在服务端软件更新时,保持服务的连续性是一项关键任务。本文将从技术角度解析服务端软件更新过程如何实现不停止服务的重要功能。

在进行热升级时,进程的代码和数据都是非常重要的。为了实现代码的更新,同时又不丢失有用的数据,需要采取一些措施。有用的数据包括内存中的数据和文件描述符。对于内存中的数据,例如配置信息,可以通过将其落盘到配置文件中来实现保留。这样,在升级过程中,新的进程可以读取配置文件并继续使用之前的配置。而对于文件描述符,可以采用一种叫做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 nginx
root      82556      1  0 11:58 ?        00:00:00 nginx: master process ./sbin/nginx
nginx     82562  82556  0 11:58 ?        00:00:00 nginx: worker process
nginx     82563  82556  0 11:58 ?        00:00:00 nginx: worker process
nginx     82564  82556  0 11:58 ?        00:00:00 nginx: worker process
nginx     82565  82556  0 11:58 ?        00:00:00 nginx: worker process
nginx     82566  82556  0 11:58 ?        00:00:00 nginx: worker process
nginx     82567  82556  0 11:58 ?        00:00:01 nginx: worker process
nginx     82569  82556  2 11:58 ?        00:00:03 nginx: worker process
nginx     82570  82556 14 11:58 ?        00:00:24 nginx: worker process

#cat /app/nginx/logs/nginx.pid
82556
可以看出nginx.pid记录的是当前master的进程号。
  • 将旧Nginx二进制换成新Nginx二进制(注意备份旧二进制)。
  • 向master进程发送USR2信号。
kill -USR2 `cat /app/nginx/logs/nginx.pid`
    • nginx收到信号会创建新master并fork出新worker,此时新老共存,都会处理请求。

执行后结果

#ps -ef | grep nginx
root      82556      1  0 11:58 ?        00:00:00 nginx: master process ./sbin/nginx
nginx     82562  82556  0 11:58 ?        00:00:01 nginx: worker process
nginx     82563  82556  0 11:58 ?        00:00:01 nginx: worker process
nginx     82564  82556  0 11:58 ?        00:00:01 nginx: worker process
nginx     82565  82556  0 11:58 ?        00:00:01 nginx: worker process
nginx     82566  82556  0 11:58 ?        00:00:01 nginx: worker process
nginx     82567  82556  0 11:58 ?        00:00:02 nginx: worker process
nginx     82569  82556  2 11:58 ?        00:00:06 nginx: worker process
nginx     82570  82556 13 11:58 ?        00:00:43 nginx: worker process
root      85710  82556  0 12:04 ?        00:00:00 nginx: master process ./sbin/nginx
nginx     85716  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85717  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85718  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85719  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85720  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85721  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85723  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85724  85710  0 12:04 ?        00:00:00 nginx: worker process

#cat /app/nginx/logs/nginx.pid
85710
可以看出nginx.pid已经变成新master进程号

#cat /app/nginx/logs/nginx.pid.oldbin
82556
nginx.pid.oldbin存放老master进程号。
  • 向老master进程发送WINCH信号。
kill -WINCH `cat /app/nginx/logs/nginx.pid.oldbin`
    • nginx的老master进程收到信号会给所有老worker进程发送信号,老worker执行优雅退出。
    • 老worker收到优雅退出信号后不再接收新请求,只处理存量请求,处理完后进程退出。
#ps -ef | grep nginx
root      82556      1  0 11:58 ?        00:00:00 nginx: master process ./sbin/nginx
nginx     82569  82556  1 11:58 ?        00:00:06 nginx: worker process is shutting down
nginx     82570  82556 11 11:58 ?        00:00:43 nginx: worker process is shutting down
root      85710  82556  0 12:04 ?        00:00:00 nginx: master process ./sbin/nginx
nginx     85716  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85717  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85718  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85719  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85720  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85721  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85723  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85724  85710  0 12:04 ?        00:00:00 nginx: worker process

此过程要不停有请求访问到nginx才能看到worker优雅退出过程,一段时间后存量请求全部处理完毕。

#ps -ef | grep nginx
root      82556      1  0 11:58 ?        00:00:00 nginx: master process ./sbin/nginx
root      85710  82556  0 12:04 ?        00:00:00 nginx: master process ./sbin/nginx
nginx     85716  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85717  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85718  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85719  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85720  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85721  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85723  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     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 nginx
root      85710      1  0 12:04 ?        00:00:00 nginx: master process ./sbin/nginx
nginx     85716  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85717  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85718  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85719  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85720  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85721  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     85723  85710  0 12:04 ?        00:00:00 nginx: worker process
nginx     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/

 

标签:00,12,85710,process,worker,nginx,服务器,解析,无损
From: https://www.cnblogs.com/bewolf/p/17468122.html

相关文章

  • 云服务器 搭建NFS 文件系统 用于解决负载均衡session 文件共享等
    解决的问题:如果多台服务器负载均衡用户没有根据TCP设置分配在一台服务器那么session无法共享文件无法共享一台服务器创建了文件另外一台也会同步创建1.搭建NFSServe注意:共享系统的服务端和客户端不能安装在同一台服务器上例如你做负载均衡的2台服务器内网ip 12.20.10......
  • 部署java的linux服务器远程报错:[USM] Channel request shell failed
    问题部署很多个微服务的linux服务器ssh远程突然进不去,远程工具提示:[USM]Channelrequestshellfailed排查分析因为部署的是很多个java进程,有可能线程数占满导致系统可创建线程耗尽,排查步骤如下:使用非远程方式进入服务器,使用top-H命令查看系统创建的线程数:查看系统允许创......
  • 使用 ABAP 正则表达式提高字符串解析的执行效率
    在ABAP(AdvancedBusinessApplicationProgramming)中,正则表达式(RegularExpressions)是一种强大的工具,可用于处理字符串和文本数据。正则表达式可以帮助您执行各种任务,如查找和替换文本、验证输入格式或拆分字符串。本文将介绍在ABAP中使用正则表达式的几种方法。使用CL_ABAP......
  • 游戏服务器被攻击怎么办?绍兴高防服务器租用203.135.102.x
    游戏服务器遭受攻击的原因可能有很多。攻击者可能会利用多种方式来入侵服务器,如通过计算机病毒、木马程序、蠕虫程序和社交工程等方式。这些攻击可以让服务器瘫痪,造成用户数据丢失、业务中断,甚至影响到公司的声誉。今天我就来和大家说原因和解决方法一、竞争对手来攻击你的服务器,让......
  • vue中封装服务器地址/接口与设置请求头
    设置请求头首先创建一个放置服务器地址的js,如http.js,然后在http.js中引入axiosimportaxiosfrom"axios";如果没有axios,需要先安装,npmiaxios或者yarnaddaxois,然后重启服务器...直接上代码点击查看代码importaxiosfrom"axios";//导入axios//创建请求实......
  • HashMap底层实现原理解析
    我们常见的有数据结构有三种结构: 数组结构 链表结构 哈希表结构下面我们来看看各自的数据结构的特点:1)数组结构:存储区间连续、内存占用严重、空间复杂度大优点:随机读取和修改效率高,原因是数组是连续的(随机访问性强,查找速度快)缺点:插入和删除数据效率低,因插入数据,这个位置后......
  • 《Linux C/C++ 服务器开发实践》记录
    《LinuxC/C++服务器开发实践》记录序言:该记录是一份读书笔记,因为主题需要和计算机操作系统有关,自然而然的想到Linux的学习,刚好最近找实习发现很多C++服务器方向需要熟悉Windows/Linux的多线程开发,所以就选了这本《LinuxC/C++服务器开发实践》来看,这本书有许多工作用得上的知......
  • 如何在多个 Linux 服务器上运行多个命令
    动动发财的小手,点个赞吧!如果你正在管理多台Linux服务器,并且你想在所有Linux服务器上运行多个命令,但你不知道该怎么做。不用担心,在这个简单的服务器管理指南中,我们将向您展示如何在多个Linux服务器上同时运行多个命令。为此,您可以使用pssh(并行ssh)程序,这是一个用于在多个......
  • .NET 7 新特性全面解析
    在2021年11月8日发布的.NET6当前已经广泛使用。微软团队已经开始着手为.NET7制定计划和新特性。本文将为您全面解析.NET7的新特性,并提供源代码示例。1.更好的性能.NET7将继续提高运行时性能,改进JIT编译器,减少内存分配,优化GC,以及提高ASP.NETCore和EntityF......
  • String解析及其方法
    String解析及其方法1.前言2.什么是字符串(String)3.字符串(String)的两种创建方式及其区别4.字符串(String)的方法及其部分原码解析5.字符串(String)的弊端1.前言String类代表字符串。Java程序中的所有字符串字面值(如"abc")都作为此类的实例实现。字符串是常量;它们的值......