Rails: 系统进程不会在 rails 服务器中启动,但会在 rails 控制台中启动

Rails: system process won't start in rails server, but will in rails console

我想在服务器启动时启动一个 ngrok 进程。为此,我编写了一个 ngrok.rb 库,并在初始化程序

中调用它

app/lib/ngrok.rb

require "singleton"
class Ngrok
    include Singleton

    attr_accessor :api_url, :front_url


    def start
        if is_running?
            return fetch_urls
        end

        authenticate
        started = system("ngrok start --all -log=stdout > #{ENV['APP_ROOT']}/log/ngrok.log &")
        system("sleep 1")
        if !started
            return { api: nil, front: nil }
        end

        urls = fetch_urls
        sync_urls(urls["api_url"], urls["front_url"])
        return urls
    end

    def sync_urls(api_url, front_url)
        NgrokSyncJob.perform_later(api_url, front_url)
    end

    def is_running?
        return system("ps aux | grep ngrok")
    end
    def restart
        stop
        return start
    end
    def stop
        return system("pkill ngrok")
    end

    def authenticate
        has_file = system("ls ~/.ngrok2/ngrok.yml")
        if has_file
            return true
        else
            file_created = system("ngrok authtoken #{ENV['NGROK_TOKEN']}")
            if file_created
                return system("cat " + ENV['APP_ROOT'] + '/essentials/ngrok/example.yml >> ~/.ngrok2/ngrok.yml')
            else
                return false
            end
        end
    end

    def fetch_urls
        logfile = ENV['APP_ROOT'] + '/log/ngrok.log'

        file = File.open logfile
        text = file.read

        api_url = nil
        front_url = nil

        text.split("\n").each do |line|
            next if !line.include?("url=") || !line.include?("https")

            if line.split("name=")[1].split(" addr=")[0] == "ncommerce-api"
                api_url = line.split("url=")[1]
            elsif line.split("name=")[1].split(" addr=")[0] == "ncommerce"
                front_url = line.split("url=")[1]
            end
        end

        file.close

        self.api_url = api_url
        self.front_url = front_url

        res = {}
        res["api_url"] = api_url
        res["front_url"] = front_url

        return res
    end
end

config/initializers/app-init.rb

module AppModule
    class Application < Rails::Application
        config.after_initialize do
            puts "XXXXXXXXXXXXXXXXXXXXXXX"
            Ngrok.instance.start
            puts "XXXXXXXXXXXXXXXXXXXXXXX"

        end
    end
end

当我键入 rails serve 时,这里是输出示例

所以我们肯定知道我的初始化程序被调用了,但是当我查看 rails 控制台时,如果它是 运行,它不是!

但是当我在 rails 控制台中输入 Ngrok.instance.start 时,输出如下:

开始了!

所以,我的问题是:为什么地球上 system("ngrok start --all -log=stdout > #{ENV['APP_ROOT']}/log/ngrok.log &") 没有在 rails 服务上运行,但它在 rails 控制台上运行?

更新

如果我在 ngrok.rb 中使用 'byebug' 并使用 rails 服务,当我使用“continue”退出 byebug 时,ngrok 进程被创建并运行

您正在创建一个孤立进程,其方式与您使用system() 在后台启动 ngrok 进程的方式相同:

system("ngrok start --all -log=stdout > #{ENV['APP_ROOT']}/log/ngrok.log &")

注意命令行末尾的 &

我需要有关您的运行时环境的更多详细信息,以便准确地告诉 哪个 系统策略在启动后立即杀死孤立的 ngrok 进程(哪个 OS?如果 Linux,它是基于 systemd 的吗?如何启动 rails 服务器,从终端还是作为系统服务?)。

但实际情况是这样的:

  • system() 启动一个 /bin/sh 的实例来解释命令行
  • /bin/sh在后台启动ngrok进程并终止
  • ngrok 现在是“孤立的”,这意味着它的父进程 /bin/sh 已终止,因此 ngrok 进程无法 wait(2)ed 为
  • 根据环境,终止 /bin/sh 可能会用 SIGHUP 信号杀死 ngrok
  • 或 OS re-parents ngrok,通常为 init-process(但这取决于)

当您使用 rails 控制台或 byebug 时,在这两种情况下您都进入了一个交互式环境,它以适合的方式准备“进程组”、“会话 ID”和“控制终端”交互式执行。这些属性由子进程继承,例如 ngrok。这会影响有关处理孤立后台进程的系统策略。

当 ngrok 从 rails 服务器启动时,这些属性会有所不同(取决于 rails 服务器的启动方式)。

这是一篇关于可能涉及的一些 OS 机制的好文章:https://www.jstorimer.com/blogs/workingwithcode/7766093-daemon-processes-in-ruby

如果使用 Ruby 的 Process.spawn 启动后台进程,在您的情况下结合 Process.detach,您可能会取得更好的成功。这将避免孤立 ngrok 进程。