Rails: системный процесс не запускается на сервере rails, но запускается в консоли rails

#ruby-on-rails #ruby #ruby-on-rails-6

#ruby-on-rails #ruby #ruby-on-rails-6

Вопрос:

Я хочу запустить процесс 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 amp;")
        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, если она запущена, это не так!

введите описание изображения здесь

Но когда я набираю Ngrok.instance.start в консоли rails, вот результат:

введите описание изображения здесь

И это начинается!

введите описание изображения здесь

Итак, мой вопрос: ПОЧЕМУ, ЧЕРТ system("ngrok start --all -log=stdout > #{ENV['APP_ROOT']}/log/ngrok.log amp;") ВОЗЬМИ, НЕ работает на rails serve, но он находится на консоли rails?

Обновить

Если я использую ‘byebug’ в ngrok.rb и использую rails serve, когда я выхожу из byebug с помощью «continue», процесс ngrok создается и работает

Комментарии:

1. Тот факт, что он работает, когда вы устанавливаете точку останова, заставляет меня думать, что это проблема синхронизации между асинхронными процессами. Попробуйте добавить еще немного сна и посмотреть, что это изменит

2. @maxpleaner уверен. Это то, что я подумал. Я поставил бесчисленное количество спящих мест в бесчисленных местах! Тем не менее, я не думаю, что это проблема, поскольку Ngrok.instance.start работал в rails c, но не в rails s, независимо от внутренних спящих

3. Вы работаете на macOS или Linux или еще?

4. У вас нет проверки ошибок. Используйте exception: true или child_process или ручную fork exec с проверкой ошибок.

5. @kisch Я использую Linux, Ubuntu 20

Ответ №1:

Вы создаете потерянный процесс так, как вы используете system() для запуска процесса ngrok в фоновом режиме:

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

Обратите amp; внимание на конец командной строки.

Мне нужно больше информации о вашей среде выполнения, чтобы точно указать, какая системная политика уничтожает потерянный процесс ngrok сразу после его запуска (какая ОС? если Linux, основан ли он на systemd? как вы запускаете rails server, с терминала или как системную службу?).

Но что происходит, так это:

  • system() запускает экземпляр /bin/sh для интерпретации командной строки
  • /bin/sh запускает процесс ngrok в фоновом режиме и завершает
  • ngrok теперь «осиротел», что означает, что его родительский процесс /bin/sh завершен, так что процесс ngrok не может быть wait(2) отредактирован для
  • в зависимости от среды завершение /bin/sh может убить ngrok сигналом SIGHUP
  • или ОС повторно запускает ngrok, обычно для процесса инициализации (но это зависит)

Когда вы используете консоль rails или byebug, в обоих случаях вы входите в интерактивную среду, которая подготавливает «группы процессов», «идентификаторы сеансов» и «управляющие терминалы» способом, подходящим для интерактивного выполнения. Эти свойства наследуются дочерними процессами, такими как ngrok. Это влияет на системные политики в отношении обработки потерянного фонового процесса.

При запуске ngrok с rails server эти свойства будут отличаться (в зависимости от способа запуска rails server).

Вот хорошая статья о некоторых механизмах ОС, которые могут быть задействованы: https://www.jstorimer.com/blogs/workingwithcode/7766093-daemon-processes-in-ruby

Вероятно, вы добились бы большего успеха, используя Ruby Process.spawn для запуска фонового процесса в сочетании с Process.detach в вашем случае. Это позволит избежать потери процесса ngrok.

Комментарии:

1. Это то, что я подозревал. Ваше понимание очень уместно. Я попробую Process.spawn. На самом деле, что сработало для меня, так это запустить задание CRON @ reboot со временем ожидания 2 минуты. Но я и другие читатели моего вопроса ищут Rails-способ сделать это, и ваши идеи очень помогли! Спасибо!!!

2. Хотя это имеет смысл, в статье здесь говорится, что процессы UNIX принимаются инициализацией при потере. Я посмотрю дальше en.m.wikipedia.org/wiki/Orphan_process