Остановка механиков - PullRequest
       17

Остановка механиков

43 голосов
/ 16 февраля 2010

У меня постоянно работает ряд сотрудников Gearman, сохраняющих такие вещи, как записи просмотров пользовательских страниц и т. Д. Иногда я буду обновлять код PHP, который используется сотрудниками Gearman. Чтобы заставить рабочих переключиться на новый код, я убиваю и перезапускаю процессы PHP для рабочих.

Какой лучший способ сделать это? Предположительно, я иногда теряю данные (хотя и не очень важные), когда убиваю один из этих рабочих процессов.

Изменить: я нашел ответ, который работает для меня, и разместил его ниже.

Ответы [ 12 ]

12 голосов
/ 23 марта 2010

Раствор 1


Обычно я запускаю своих работников с утилитой демона unix с флагом -r и позволяю им истечь после одного задания. Ваш скрипт будет корректно завершаться после каждой итерации, и демон автоматически перезапускается.

Ваши работники будут устареть на одну работу, но это может быть не так уж важно для вас, как потеря данных

Это решение также обладает преимуществом освобождения памяти. Вы можете столкнуться с проблемами с памятью, если выполняете большую работу, так как PHP pre 5.3 имеет ужасный GC.

Раствор 2


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

8 голосов
/ 17 августа 2011
function AutoRestart() {
   static $startTime = time();

   if (filemtime(__FILE__) > $startTime) {
      exit();
   }
}

AutoRestart();  
7 голосов
/ 09 июня 2010

Ну, я отправил этот вопрос, теперь я думаю, что нашел хороший ответ на него.

Если вы заглянете в код для Net_Gearman_Worker, вы обнаружите, что в рабочем цикле отслеживается функция stopWork, и если она возвращает true, она выходит из функции.

Я сделал следующее:
Используя memcache, я создал кэшированное значение gearman_restarttime и использую отдельный скрипт, чтобы установить его в текущую метку времени при каждом обновлении сайта. (Я использовал Memcache, но его можно хранить где угодно - базу данных, файл или что угодно).

Я расширил класс Worker, по сути, до Net_Gearman_Worker_Foo, и заставил всех моих работников это реализовать. В классе Foo я переопределил функцию stopWork, чтобы сделать следующее: во-первых, он проверяет gearman_restarttime; в первый раз он сохраняет значение в глобальной переменной. С тех пор каждый раз он сравнивает кэшированное значение с глобальным. Если он изменился, stopWork возвращает true, и работник завершает работу. Крон проверяет каждую минуту, чтобы увидеть, работает ли каждый работник, и перезапускает любого работника, который ушел.

Возможно, стоит также включить таймер в stopWork и проверять кэш только один раз каждые x минут. В нашем случае Memcache достаточно быстр, так что проверка значения каждый раз не кажется проблемой, но если вы используете какую-то другую систему для сохранения текущей временной метки, проверка реже будет лучше.

1 голос
/ 11 июня 2013

Я столкнулся с этой же проблемой и нашел решение для Python 2.7.

Я пишу скрипт на python, который использует gearman для связи с другими компонентами системы. В сценарии будет несколько рабочих, и каждый рабочий работает в отдельном потоке. Все работники получают данные о механизме, обрабатывают и хранят эти данные в очереди сообщений, и основной поток может по мере необходимости извлекать данные из очереди.

Мое решение для точного завершения работы каждого работника состояло в том, чтобы создать подкласс gearman.GearmanWorker и переопределить функцию work():

from gearman import GearmanWorker
POLL_TIMEOUT_IN_SECONDS = 60.0
class StoppableWorker(GearmanWorker):
    def __init__(self, host_list=None):
        super(StoppableWorker,self).__init__(host_list=host_list)
        self._exit_runloop = False


    # OVERRIDDEN
    def work(self, poll_timeout=POLL_TIMEOUT_IN_SECONDS):
        worker_connections = []
        continue_working = True

        def continue_while_connections_alive(any_activity):
            return self.after_poll(any_activity)

        while continue_working and not self._exit_runloop:
            worker_connections = self.establish_worker_connections()
            continue_working = self.poll_connections_until_stopped(
                worker_connections,
                continue_while_connections_alive,
                timeout=poll_timeout)

        for current_connection in worker_connections:
            current_connection.close()

        self.shutdown()


    def stopwork(self):
        self._exit_runloop = True

Используйте это так же, как GearmanWorker. Когда пришло время выйти из скрипта, вызовите функцию stopwork(). Он не остановится немедленно - может пройти до poll_timeout секунд, прежде чем он выйдет из цикла выполнения.

Может быть несколько умных способов вызвать функцию stopwork(). В моем случае я создаю временного клиента gearman в основном потоке. Для работника, которого я пытаюсь выключить, я посылаю специальную команду STOP через сервер gearman. Когда рабочий получает это сообщение, он знает, что должен выключиться.

Надеюсь, это поможет!

1 голос
/ 29 марта 2011

Учитывая тот факт, что рабочие написаны на PHP, было бы хорошей идеей перерабатывать их по известному расписанию. Это может быть статическое количество времени с момента запуска или может быть выполнено после того, как было выполнено определенное количество заданий.

Это по существу убивает (без каламбура) двух зайцев одним выстрелом. Вы снижаете вероятность утечек памяти, и у вас есть последовательный способ определить, когда ваши работники пойдут на любой потенциально новый код.

Обычно я пишу работникам так, чтобы они сообщали свой интервал в stdout и / или в средство ведения журналов, чтобы было легко проверить, где находится работник.

1 голос
/ 12 августа 2010

Если кто-то ищет ответ для работника, работающего на Perl, это часть того, для чего предназначена библиотека GearmanX :: Starter . Остановить работников после выполнения текущего задания можно двумя способами: внешне, отправив рабочему процессу SIGTERM, или программно, установив глобальную переменную.

1 голос
/ 03 июня 2010

Я также недавно смотрел на это (хотя в perl с Gearman :: XS). Мой сценарий использования был таким же, как и у вас, - позволяйте долго работающему механику периодически проверять наличие новой версии и перезагружать.

Моей первой попыткой было просто заставить работника отслеживать, как долго он последний раз проверял версию рабочего скрипта (также сработала бы md5sum). Затем по истечении N секунд между заданиями он проверяет, доступна ли новая версия самого себя, и перезапускает себя (fork () / exec ()). Это сработало нормально, но работники, зарегистрированные на редкие вакансии, могли в конечном итоге ждать часы для возврата work () и, следовательно, для проверки текущего времени.

Так что теперь я устанавливаю довольно короткий тайм-аут при ожидании заданий с работой (), чтобы я мог проверять время более регулярно. Интерфейс PHP предполагает, что вы можете установить это значение времени ожидания при регистрации на работу. Я использую SIGALRM для запуска проверки новой версии. Интерфейс perl блокируется на work (), поэтому изначально сигнал тревоги не сработал. При установке тайм-аута на 60 секунд SIGALRM заработал.

1 голос
/ 03 марта 2010

http://phpscaling.com/2009/06/23/doing-the-work-elsewhere-sidebar-running-the-worker/

Как показано в приведенной выше статье, я запустил работника внутри сценария оболочки BASH, периодически выходя из него между заданиями для очистки (или перезагружая рабочий скрипт) - или, если ему было дано задание, он может выйти с определенным кодом выхода и для выключения.

1 голос
/ 16 февраля 2010

Хм, Вы могли бы внедрить код в рабочих, чтобы иногда проверять, был ли изменен исходный код, если да, тогда просто убивайте себя, когда сочтете нужным. То есть проверяйте, когда они находятся в середине работы, и если работа очень большая.

Другим способом было бы реализовать какое-то прерывание, возможно, через сеть, чтобы сказать «остановка», когда у вас есть такая возможность, и перезапустить.

Последнее решение помогает изменить источник Gearman для включения этой функции.

0 голосов
/ 11 июня 2016

Я использую gearmadmin, чтобы проверить, выполняются ли какие-либо задания. Я использовал API администратора, чтобы сделать пользовательский интерфейс для этого. Когда рабочие места сидят без дела, убивать их не вредно.

...