Подтвердить что ты не робот

Завершение замедленных заданий + лучшие практики Capistrano

Я просто хотел понять лучшие практики для возобновления замедленных рабочих мест с использованием capistrano. У меня есть куча рабочих, которые обрабатывают длинные задания (до 10 минут).

Я столкнулся с двумя сценариями во время развертывания, пока рабочие обрабатывают -

1)

Я останавливаю замедленных рабочих мест перед развертыванием: перезапустите задачу и запустите их снова после задачи deploy: restart.

Но в этом случае он не перезапустит мое приложение до тех пор, пока не завершится задание с задержкой (это может быть нормально, но развертывание крышки script буквально сидит там, пока работа не будет завершена, и она может остановить всех работников) до выполните задачу перезагрузки приложения.

2) Я также попытался остановить/запустить рабочих замедленных работ после задачи перезапуска, но это вызвало всевозможные драмы, в результате которых задачи были бы остановлены, не дожидаясь, пока таблица замедленных сообщений не будет перечислена и назначена работнику с ПИД-кодом, которого не существует!

Любые другие варианты? Или я вынужден ждать, как упоминалось в сценарии 1.

Большое спасибо.

Изменить: Я только что понял со сценарием 1.. он не ждет! Задача остановки будет принудительно убить моего работника, хотя она еще не закончилась!

 ** [out] delayed_job: trying to stop process with pid 9630...
 ** [out] delayed_job: process with pid 9630 won't stop, we forcefully kill it...
 ** [out] 
 ** [out] delayed_job: process with pid 9630 successfully stopped.
4b9b3361

Ответ 1

Если ваши изменения в развертывании меняют схему базы данных, вы вынуждены ждать.

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

Ответ 2

На самом деле не уверен, что мое решение достаточно хорошее. Но в любом случае у него есть шанс жить. Просто создайте задачу Rake и запустите ее в процессе развертывания:

  desc 'Wait for delayed job completion'
  task wait_for_complete_background_jobs: :environment do
    loop do
      exit unless Delayed::Job.where(locked_at: nil).exists?
      sleep 2
    end
  end

Задача rake ждет заблокированных заданий и выходит сразу после того, как список заблокированных задач пуст. Так я представляю его использование (обратите внимание, что последовательность операций важна):

  • Остановить веб-сервер
  • Запустите задачу rake
  • Задержка с задержкой.
  • Обновить код
  • Запустить задержанное задание
  • Запустить веб-сервер

Ответ 3

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

Ответ 4

Я закончил перенос своих "длинных процессов" на собственный Ruby script, используя драгоценный камень daemons. Спасибо за совет, хотя ребята!

Ответ 5

Хороший вопрос - я пробовал много разных вещей, но моя стратегия заключается в том, чтобы все было просто.

  • Не используйте God или Monit или что-либо, что работает под управлением root, требуя, чтобы тот же пользователь запускал ваше приложение, чтобы иметь привилегии sudo для его перезапуска. Учитывая недавние события в безопасности rails, последнее, что вы хотите, - это пользователь вашего приложения, имеющий хорошую возможность получить root.
  • Вместо этого используйте задачу rake или thor для запуска/остановки/перезапуска задач.
  • Используйте nohup для демонтизации процесса.

Вот несколько основных задач, которые могут выполнять простые фоновые задачи с помощью resque, например:

class Resque < Thor

  desc "start", "Starts resque workers"
  def start
    system('(PIDFILE=/tmp/resque-myapp.pid nohup rake resque:work QUEUES="*" &) &')
  end

  desc "stop", "Stops resque workers"
  def stop
    pidfile = '/tmp/resque-myapp.pid'
    if File.exist?(pidfile)
      pid = File.open(pidfile).first.to_i
      puts "Killing process #{pid}..."
      system("kill -QUIT #{pid}")
      File.delete(pidfile)
      sleep(5)
    else
      puts "No resque pidfile found"
    end
  end
end

Затем в файле deploy.rb:

after 'deploy',         "resque:restart"

namespace :resque do
  task :restart do
    puts "Stopping resque workers.."
    run("cd #{current_path} && thor resque:stop", :options => { :pty => true })
    puts "Starting resque workers.."
    run("cd #{current_path} && thor resque:start > /dev/null 2>&1 &", :options => { :pty => true })
  end
end

Эта стратегия может даже работать с RVM, если это интересно. В дополнение к обычным задачам rvm-capistrano вам понадобится что-то подобное перед командой перезапуска:

after  'deploy:setup', 'thor:install'
namespace :thor do
  task :install do
    puts "Installing thor..."
    run_locally('cap rvm:install_gem GEM=thor')
  end
end

after "deploy",         "rvm:trust_rvmrc"
namespace :rvm do
  task :trust_rvmrc do
    run "rvm rvmrc trust #{release_path}"
  end
end

И последнее, но не менее важное: с RVM лучше всего положить в ~/.bashrc то, что установщик обычно разделяет между этим файлом и .bash_profile, и убедиться, что режим файла равен 0400, поэтому он не будет сбиваться с программа установки RVM:

PATH=$HOME/.rvm/bin:/usr/local/bin/:$PATH # Add RVM to PATH for scripting
export PATH
export RAILS_ENV=production
[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"

Ответ 6

Я бы порекомендовал вам проверить whenever - возможно, лучший камень для управления отложенными заданиями. Я пробовал разные решения, начиная с старого забытого cron + wget, но всякий раз дает лучший контроль над этим. И он отлично сочетается с Capistrano. НТН