Прекрасно прекратить работу
У меня есть несколько рабочих Gearman, которые постоянно работают, сохраняя такие вещи, как записи просмотров пользовательских страниц и т.д. Иногда я обновляю PHP-код, который используется рабочими Gearman. Чтобы заставить рабочих перейти на новый код, я убиваю и перезапускаю процессы PHP для рабочих.
Что лучше для этого? Предположительно, я иногда теряю данные (хотя и не очень важные данные), когда я убью один из этих рабочих процессов.
Изменить: я нашел ответ, который работает для меня, и разместил его ниже.
Ответы
Ответ 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-интерфейс администратора для создания пользовательского интерфейса. Когда рабочие места сидят без дела, нет никакого вреда в их убийстве.