Unix 如何在不删除任何请求的情况下升级Nginx?

Unix 如何在不删除任何请求的情况下升级Nginx?,unix,nginx,internals,Unix,Nginx,Internals,根据: 如果需要替换nginx二进制文件 使用一个新的(升级到 新版本或添加/删除服务器 模块),您可以不使用任何 服务停机-无传入 请求将丢失 我的同事和我试图弄明白:这是怎么回事?。我们知道(我们认为): 一次只能有一个进程监听端口80 Nginx创建一个套接字并将其连接到端口80 父进程及其任何子进程都可以绑定到同一个套接字,这就是Nginx可以让多个工作进程子进程响应请求的方式 我们还使用Nginx做了一些实验,如下所示: 向当前主进程发送一个kill-USR2 反复运行ps-ef

根据:

如果需要替换nginx二进制文件 使用一个新的(升级到 新版本或添加/删除服务器 模块),您可以不使用任何 服务停机-无传入 请求将丢失

我的同事和我试图弄明白:这是怎么回事?。我们知道(我们认为):

  • 一次只能有一个进程监听端口80
  • Nginx创建一个套接字并将其连接到端口80
  • 父进程及其任何子进程都可以绑定到同一个套接字,这就是Nginx可以让多个工作进程子进程响应请求的方式
我们还使用Nginx做了一些实验,如下所示:

  • 向当前主进程发送一个
    kill-USR2
  • 反复运行
    ps-ef | grep unicorn
    以查看任何unicorn进程,以及它们自己的PID和它们的父PID
  • 请注意,新主进程最初是旧主进程的子进程,但当旧主进程消失时,新主进程的ppid为1
显然,新主进程可以在旧进程运行时监听同一套接字,因为在那个时候,新主进程是旧主进程的子进程。但不知何故,新的主流程可以变成。。。嗯。。。没有人的孩子


我假设这是标准的Unix内容,但是我对进程、端口和套接字的理解非常模糊。有人能更详细地解释一下吗?我们的假设有错吗?我不知道nginx是怎么做到的,但基本上,它可以执行新的二进制文件,携带监听套接字和新的进程(实际上,它仍然是相同的进程,只是替换了在其中执行的程序)。侦听套接字有大量传入连接,只要启动速度足够快,就应该能够在溢出之前开始处理它们。如果没有,它可能会先fork,exec,然后等待它启动到可以处理传入请求的位置,然后通过某种内部机制在退出之前移交侦听套接字的命令(fork时继承文件描述符,两者都可以访问它)。注意您的观察结果,这看起来就像它在做什么(如果您的父进程死亡,您的ppid将重新分配给init,即pid 1)

如果它有多个进程在同一个侦听套接字上竞争接受(同样,我不知道nginx是如何做到的,也许它有一个分派进程?),那么您可以通过命令它们执行新程序来逐个替换它们,如上所述,但一次一个,这样就不会丢球。请注意,在这样一个过程中,永远不会有任何新的PID或父/子关系更改

至少,我想我可能会这么做,这是我的想法。

有关详细信息:描述了用于TCP套接字的C库

我认为关键在于,当一个进程在持有套接字文件描述符时分叉后,父进程和子进程都能够对其调用accept()

这就是流程。Nginx,正常启动:

  • 调用socket()和bind()以及listen()来设置由文件描述符(整数)引用的套接字
  • 启动一个线程,该线程在循环中对文件描述符调用accept(),以处理传入连接
  • 然后Nginx分叉。父级保持正常运行,但子级立即执行新的二进制文件。exec()清除旧程序、内存和正在运行的线程,但继承打开的文件描述符:请参阅。我怀疑exec()调用将打开的文件描述符的编号作为命令行参数传递

    作为升级的一部分启动的子项:

  • 从命令行读取打开的文件描述符的编号
  • 启动一个线程,该线程在循环中对文件描述符调用accept(),以处理传入连接
  • 告诉父级排空(停止accept(),并完成现有连接),然后关闭

  • 应该注意的是,根据nginx文档,在向旧主进程发送绞车信号之前,旧主进程和新主进程都在处理连接。