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

Прекрасно прекратить работу

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

Решение 1


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

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

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

Решение 2


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

Ответ 3

function AutoRestart() {
   static $startTime = time();

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

AutoRestart();  

Ответ 4

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

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

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

Ответ 5

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

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

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

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

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

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

Ответ 9

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

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

Мое решение для чистого закрытия каждого работника состояло в подклассе 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. Когда он выйдет из script, вызовите функцию stopwork(). Он не остановится сразу - он может занять до poll_timeout секунд, прежде чем он выйдет из цикла выполнения.

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

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

Ответ 10

Это прекрасно впишется в вашу систему непрерывной интеграции. Я надеюсь, что у вас есть это или вы должны скоро это получить: -)

При проверке нового кода он автоматически создается и развертывается на сервере. Как часть сборки script, вы убиваете всех работников и запускаете новые.

Ответ 11

Я использую следующий код, который поддерживает как Ctrl-C, так и kill -TERM. По умолчанию supervisor отправляет TERM сигнал, если не изменил настройку signal=. В PHP 5.3+ declare(ticks = 1) устарел, используйте pcntl_signal_dispatch() вместо этого.

$terminate = false;
pcntl_signal(SIGINT, function() use (&$terminate)
{
    $terminate = true;
});
pcntl_signal(SIGTERM, function() use (&$terminate)
{
    $terminate = true;
});

$worker = new GearmanWorker();
$worker->addOptions(GEARMAN_WORKER_NON_BLOCKING);
$worker->setTimeout(1000);
$worker->addServer('127.0.0.1', 4730);
$worker->addFunction('reverse', function(GearmanJob $job)
{
    return strrev($job->workload());
});

$count = 500 + rand(0, 100); // rand to prevent multple workers restart at same time
for($i = 0; $i < $count; $i++)
{
    if ( $terminate )
    {
        break;
    }
    else
    {
        pcntl_signal_dispatch();
    }

    $worker->work();

    if ( $terminate )
    {
        break;
    }
    else
    {
        pcntl_signal_dispatch();
    }

    if ( GEARMAN_SUCCESS == $worker->returnCode() )
    {
        continue;
    }

    if ( GEARMAN_IO_WAIT != $worker->returnCode() && GEARMAN_NO_JOBS != $worker->returnCode() )
    {
        $e = new ErrorException($worker->error(), $worker->returnCode());
        // log exception
        break;
    }

    $worker->wait();
}

$worker->unregisterAll();

Ответ 12

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