金字塔、叉子、Java 和插座

Pyramid, fork, Java and sockets

情况复杂:

  1. 金字塔应用程序有时需要 start/restart 一些 Java 进程(例如 Jetty)(例如接收 Jetty 的新 WAR 文件等) .

  2. 在 Pyramid 进程 (pserve) 退出后或 Pyramid 重启之间,Jetty 进程有望继续工作。

  3. Jetty 进程是通过 subprocess.Popen + shell 脚本启动的。当然,作为子进程,它继承了文件描述符,包括套接字。

  4. 现在如果 Pyramid 需要在 Jetty 仍然 运行 时重启,它不能这样做,因为子 Jetty 进程仍然有绑定到 Pyramid 的主 address/port 的套接字。

目前的解决方案:

  1. 要重新启动 Java/Jetty,请执行 fork

  2. 在子进程中关闭每个文件描述符 > 3 和 < resource.RLIMIT_NOFILE

  3. 在 starting/restarting Jetty 之后的子进程中执行 sys.exit

很好,对吧?

没有

这很复杂而且很笨拙。有没有更简单的方法来避免这个 "child process inheriting sockets" 问题?

此外,我有服务(如 APScheduler 等)运行,这需要精心关闭所有 运行 服务(当然在子进程中),否则它们会引发sys.exit.

的例外情况

Is there a simpler way of avoiding this "child process inheriting sockets" problem?

是的,只需将 close_fds=True 传递给 Popen 构造函数或包装函数。这将关闭除 0、1 和 2 之外的所有 fds。*

如果您需要保留一些不同的集合而不是 0、1 和 2,请使用 pass_fds=[0, 2, special_file.fileno()] 而不是 close_fds=True。但希望你不需要那个。**


* 我假设您不关心 Windows 可移植性 — 毕竟,您已经在使用 os.fork。如果我错了,close_fds=True 将无法在具有重定向 stdio 句柄的 non-POSIX 系统上工作,因此如果您需要两者,则需要更复杂的解决方案。

** 如果您这样做:pass_fds 需要 3.2+,或 3.2+ subprocess 模块的反向移植。我很确定你没有 3.2+,因为如果你有,close_fds=True 将已经是 POSIX 系统上的默认行为,你一开始就不会遇到这个问题。 Web 服务需要它的一个常见原因是当您没有绑定端口 80 的权限时,因此您从 suid 程序继承了一个套接字,并且需要将该套接字传递给您自己的 child.

恕我直言,您应该将流程控制与 Web 应用程序流程分离。使用像 supervisord to start/stop/restart long-running applications; this can be done over XML-RPC.

这样的过程控制系统

金字塔的创造者 Chris McDonough 也是 Supervisor 的主要作者。