Ruby 派生进程侦听父服务器端口

Ruby spawned process listening on parent server port

我是 运行 fedora 32 上的 puma 服务器 ruby 应用程序。在我的服务器中,由于各种原因,我有一些调用会产生新的长 运行 进程。我遇到了一个问题,我的派生进程在与我的服务器相同的端口上 运行 和 listening。这导致在部署时重新启动我的服务器时出现问题,因为服务器无法启动,因为进程正在侦听所需的端口。这怎么可能?根据我的理解,当我生成一个进程时,它应该具有与父进程完全不同的内存,并且不共享任何文件描述符。我的 spawn 命令很简单

my_pid = Process.spawn(my_cmd, %i[out err] => log_file)

Ruby 版本 2.7.0

编辑:我在我的部署过程和我最初的问题描述中忽略了一些东西,服务器重启并不是真正的拆除并重新启动一个新进程,而是通过向 puma 服务器发送 USR2 信号(如所述 here)

一个快速的解决方法/解决方案是调用 fork, close Puma's socket within the forked process and then call exec, which replaces the running process... 但是,此解决方法仅限于 Unix 系统。在 windows 上,您可能可以使用更复杂的方法实现类似的效果。

遗憾的是,我不确定如何关闭 Puma 的监听套接字。或许 this will helps,但更有可能还有其他一些技巧。

我相信我已经找到了造成这种情况的原因。似乎是我使用的 puma restart process 的问题。通过使用 USR2 信号重新启动服务器,它会更改套接字打开的 fd 上的标志。

[me@home puma_testing]$ cat /proc/511620/fdinfo/5
pos:    0
flags:  02000002
mnt_id: 10
[me@home puma_testing]$ kill -s USR2 511620
[me@home puma_testing]$ cat /proc/511620/fdinfo/5
pos:    0
flags:  02
mnt_id: 10

这是在 fedora 32 上使用非常简单的 puma 和 sinatra 设置进行测试的,如下所示: puma.rb

# frozen_string_literal: true

rackup File.join(File.dirname(File.realpath(__FILE__)), './server.ru')

# https://www.rubydoc.info/gems/puma/Puma/DSL#prune_bundler-instance_method
# This allows us to install new gems with just a phased-restart. Otherwise you
# need to take the master process down each time.
prune_bundler

port 11111

environment 'production'

pidfile File.join(File.dirname(File.realpath(__FILE__)), '../', 'server.pid')

tag 'test'

并且server.ru像这样

require 'sinatra'

class App < Sinatra::Base
  get "/" do
    "Hello World!"
  end

  get "/spawn" do
    spawn "sleep 500"
  end
end

run App

运行 使用捆绑器 bundle exec puma -C puma.rb。请注意,您可以使用 /spawn 获取请求来测试在重新启动之前和之后生成新进程,以查看它是否正在使用 lsof -itcp:11111

侦听套接字