很好地停止gearman工人
我有一些Gearman工作人员不停地运行,保存用户页面浏览logging等。偶尔,我会更新Gearman工作人员使用的PHP代码。 为了让工人切换到新的代码,我杀了并重新启动PHP进程的工人。
什么是更好的方法来做到这一点? 据推测,当我杀死其中一个工作进程时,我有时会丢失数据(虽然不是非常重要的数据)。
编辑:我find了一个适合我的答案,并在下面贴出来。
解决scheme1
通常我使用-r标志运行带有unix守护程序实用程序的工作人员,并让他们在一个工作后过期。 您的脚本将在每次迭代之后正常结束,守护进程将自动重启。
你的工作人员将会失去一份工作,但这对你来说可能不如丢失数据那么重要
该解决scheme还具有释放内存的优点。 如果您正在做大型工作,您可能会遇到内存问题,因为PHP 5.3之前的版本有太糟糕的GC。
解决scheme2
您还可以将退出function添加到退出脚本的所有工作人员。 当您想要重新启动时,只需简单地给予减速机呼叫即可退出。
function AutoRestart() { static $startTime = time(); if (filemtime(__FILE__) > $startTime) { exit(); } } AutoRestart();
那么,我发布了这个问题,现在我想我已经find了一个很好的答案。
如果您查看Net_Gearman_Worker的代码,您会发现在工作循环中监视函数stopWork,如果它返回true,则退出该函数。
我做了以下几点:
使用memcache,我创build了一个caching的值,gearman_restarttime,我使用一个单独的脚本将其设置为当前时间戳,每当我更新网站。 (我使用Memcache,但是这可以存储在任何地方 – 数据库,文件或任何东西)。
我将Worker类扩展为Net_Gearman_Worker_Foo,并让我的所有工作者都实例化了这个类。 在Foo类中,我将覆盖stopWork函数来执行以下操作:首先,检查gearman_restarttime; 第一次通过,它将值保存在一个全局variables中。 从此,每一次,它都将caching值与全局进行比较。 如果它已经改变,stopWork返回true,并且工人退出。 一个cron会检查每一分钟是否每个worker都在运行,并重新启动已经退出的worker。
在stopWork中放置一个计时器也是值得的,并且每x分钟只检查一次caching。 在我们的例子中,Memcache足够快,每次检查值似乎都不成问题,但如果您使用其他系统存储当前时间戳,则检查次数会更less。
嗯,你可以在工作人员中实现一个代码,以便偶尔检查源代码是否被修改,如果是,那么只是在他们认为合适的时候自杀。 也就是说,在工作中间检查,如果工作量很大。
其他的方法是实现某种中断,也许通过networking来说停止,只要有机会重启。
最后的解决scheme是帮助修改Gearman的源代码来包含这个function。
我最近也在看这个(尽pipe在使用Gearman :: XS的perl中)。 我的用例和你的一样 – 让一个长期工作的齿轮工人定期检查自己的新版本并重新加载。
我的第一个尝试只是让工作人员跟踪自上次检查工作人员脚本版本(md5sum也可以)的时间。 然后,在作业之间经过N秒,它将检查是否有新版本的自身可用,并重新启动(fork()/ exec())。 这确实没有问题,但注册稀有工作的工人可能最终会等待工作时间()返回,从而检查当前时间。
所以我现在正在等待工作()时设置一个相当短的超时,所以我可以更经常地检查时间。 PHP界面build议您在注册作业时可以设置此超时值。 我正在使用SIGALRM来触发新版本的检查。 perl接口在工作()上阻塞,所以最初没有触发报警。 将超时设置为60秒,SIGALRM正常工作。
如果有人在寻找一个运行perl的工作人员的答案,这是GearmanX :: Starter库的一部分。 在完成当前工作后,您可以通过两种不同的方式来阻止工作人员:外部通过发送工作进程SIGTERM,或通过设置全局variables以编程方式。
鉴于工作人员是用PHP编写的,最好是按照已知的时间表回收这些工作。 这可能是自开始以来的静态时间量,或者是在尝试了一定数量的作业之后可以完成的。
这实质上是一箭双雕(没有双关语)。 你正在减轻内存泄漏的可能性,而且你有一个一致的方法来确定你的工作人员什么时候会拿起任何潜在的新代码。
我通常会编写一些工作人员,以便将他们的时间间隔报告给stdout和/或日志logging工具,以便检查工作人员在哪里。
我遇到了同样的问题,并提出了一个解决scheme的Python 2.7。
我正在写一个Python脚本,它使用齿轮工与系统上的其他组件进行通信。 该脚本将有多个工作人员,我有每个工人在单独的线程运行。 工作人员都会收到员工数据,他们将这些数据处理并存储在一个消息队列中,主线程可以根据需要从队列中提取数据。
我彻底closures每个工人的解决scheme是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()
函数。 就我而言,我在主线程中创build了一个临时的gearman客户端。 对于要closures的工作人员,我通过齿轮服务器发送一个特殊的STOP命令。 当工人得到这个信息时,它知道要closures自己。
希望这可以帮助!
http://phpscaling.com/2009/06/23/doing-the-work-elsewhere-sidebar-running-the-worker/
就像上面的文章演示的那样,我已经在BASH shell脚本中运行了一个worker,偶尔在作业之间退出来清理(或者重新载入worker-script) – 或者如果给定了一个给定的任务,它可以退出退出代码并closures。
这将非常适合您的持续集成系统。 我希望你有它,或者你应该很快就会有:-)
在您检入新代码时,它会自动生成并部署到服务器上。 作为构build脚本的一部分,你杀死所有的工人,并启动新的。
我使用下面的代码同时支持Ctrl-C
和kill -TERM
。 默认情况下,如果没有修改signal=
setting, supervisor
发送TERM
信号。 在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();
我所做的是使用gearmadmin
来检查是否有任何作业正在运行。 我使用pipe理API来为此做一个UI。 当工作闲置时,杀死他们没有任何伤害。