有什么方法可以更新和重启服务器,使其套接字保持在 "suspended" 状态吗?

Is there any approach to update and restart a server keeping its socket in a "suspended" state?

有一个程序在 TCP/IP 端口中侦听和应答请求(专有二进制协议)。但是这个程序需要更新,所以需要重新启动,然后它才能继续在同一个端口上工作。

根据其协议,所有当前连接都可以关闭,因为所有客户端在关闭后都会立即重新建立新连接,但是新连接应该保留(但不能拒绝)直到程序重新启动(对于几秒),怎么做到的?

因此,一旦再次 运行,给定端口上所有保留的连接都可以释放以到达侦听套接字。

让我们想象一下以下步骤:

  1. 一个服务器程序是运行并且监听给定的端口,让我们 说端口 A.
  2. 它要求外部资源(如操作系统或任何第三方模块)保留所有到达端口 A 的连接。
  3. 它会关闭当前与端口 A 建立的所有当前连接 - 这可能需要一些时间(可能需要几分钟,因为它将首先完成所有请求的服务)
  4. 它已重新启动,一个全新的可执行文件开始运行并开始侦听端口 A。
  5. 它要求外部资源释放所有保留的连接,这样它们现在就可以到达端口 A,该端口现在已准备好接收连接。

步骤 2 和 4 只是假设。

在 POSIXy 系统(Linux、Mac、BSD)中,服务进程有一种相当简单但巧妙的方法来实现这一点。这样做甚至不需要任何特权。

核心思想很简单:当服务知道它会重启时,它会创建一个分离的子进程(在一个新的会话和进程组中,所以它会被重新设置为init)持有监听套接字(秒)。然后,父级将不再 accept() 任何新连接,完成任何不完整的响应,并使用更新的二进制文件重新执行。

holder 进程还将侦听 Unix 域(流或 seqpacket;面向连接)套接字上的传入连接。更新后的服务器实例将连接到 holder 进程,辅助负载为 SCM_CREDENTIALS,其中包括内核验证的用户和进程运行的组,以及 holder 进程可以用来检查连接方是否连接的进程 ID是二进制文件的更新版本。 (在 Linux 中,这可以通过比较 /proc/PID/exestat() 和预期的可执行文件来完成。)如果另一端被授权,持有者将监听套接字描述符传回,使用SCM_RIGHTS 辅助载荷。最后,更新的服务发送最后的感谢,告诉持有者进程退出(它也关闭它的监听套接字描述符的副本)。

只要积压(参见listen())足够(或在Linux中启用syncookies,这使得积压基本上是无限的),这应该是相当稳健的方法。

如果需要,我可以提供示例代码,说明它在 Linux 中的工作原理。 (我认为这里的安全方面很重要,所以我肯定会做 Linux-only 的事情,比如检查 /proc/PID/exe,以验证只有更新的二进制文件才能重新获取监听套接字。)

侦听套接字与连接无关。诀窍是让新服务器启动并与旧服务器协调以接管服务端口上的侦听。一旦旧服务器关闭了它的监听套接字,新服务器就可以打开它自己的。然后继续处理新的连接。

与此同时,旧服务器继续为新服务器接管之前接受的连接提供服务,并在完成连接后关闭每个连接。全部关闭后,旧服务器就可以退出了。