【问题标题】:Rails: system process won't start in rails server, but will in rails consoleRails:系统进程不会在 Rails 服务器中启动,但会在 Rails 控制台中启动
【发布时间】:2020-10-07 19:46:21
【问题描述】:

我想在服务器启动时启动一个 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 &gt; #{ENV['APP_ROOT']}/log/ngrok.log &amp;") 不在 rails serve 上工作,但它在 rails 控制台上?

更新

如果我在 ngrok.rb 中使用 'byebug' 并使用 rails serve,当我使用 "continue" 退出 byebug 时,ngrok 进程会创建并运行

【问题讨论】:

  • 放置断点时它可以工作的事实让我认为这是异步进程之间的时间问题。乱七八糟地增加一些睡眠,看看是否会改变什么
  • @maxpleaner 确定。那正是我所想。我在无数个地方睡了无数次!我仍然认为这不是问题,因为 Ngrok.instance.start 在 rails c 但不在 rails s 中工作,无论内部睡眠如何
  • 您是在 macOS 还是 Linux 上运行?
  • 您没有任何错误检查。使用 exception: true 或 child_process 或手动 fork+exec 进行错误检查。
  • @kisch 我正在运行 Linux,Ubuntu 20

标签: ruby-on-rails ruby ruby-on-rails-6


【解决方案1】:

您正在以使用system() 在后台启动ngrok 进程的方式创建一个孤立进程

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

注意命令行末尾的&amp;

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

但是发生了什么是这样的:

  • system() 启动 /bin/sh 的实例来解释命令行
  • /bin/sh在后台启动ngrok进程并终止
  • ngrok 现在是“孤立的”,这意味着它的父进程/bin/sh 被终止,因此ngrok 进程不能被wait(2)ed for
  • 根据环境,终止 /bin/sh 可能会使用 SIGHUP 信号杀死 ngrok
  • 或操作系统重新创建 ngrok,通常是 init 进程(但这取决于)

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

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

这是一篇很好的文章,介绍了可能涉及的一些操作系统机制:https://www.jstorimer.com/blogs/workingwithcode/7766093-daemon-processes-in-ruby

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

【讨论】:

  • 这是我怀疑的。你的洞察力非常中肯。我会尝试 Process.spawn。实际上,对我有用的是在等待时间为 2 分钟的情况下启动 CRON 作业 @reboot。但是我和我的问题的其他读者正在寻找一种 Rails 方式来做到这一点,你的见解很有帮助!谢谢!!!
  • 虽然有道理,但这里的一篇文章告诉我,UNIX 进程在孤立时会被 init 掺杂。我会进一步看en.m.wikipedia.org/wiki/Orphan_process
猜你喜欢
  • 2016-02-23
  • 1970-01-01
  • 1970-01-01
  • 2023-04-05
  • 2012-04-20
  • 2015-03-30
  • 1970-01-01
  • 2013-05-04
相关资源
最近更新 更多