如何在同时运行多个cron作业时克服服务器负载问题?

时间:2016-05-27 00:52:06

标签: php cron zeromq background-process

我有一个显示游戏服务器数据的网站。游戏有不同的域名和#34; (用户实际上只是单独的服务器)。

现在,我每隔6小时以不同的时间间隔运行14个cron个工作。运行的所有14个文件几乎相同,每个文件需要大约75分钟(一小时15分钟)才能完成它的运行。

我曾想过只使用从cron运行的1个文件并循环遍历每个服务器,但这只会导致一个文件运行18小时左右。 我当前的VPS设置为仅允许1 vCPU ,因此我尝试完成任务并保持在分配的服务器负载范围内。

看到该网站需要每6小时更新更新数据,这是不可行的。

我开始研究消息队列并将一些信息传递给将执行相关工作的后台进程。我开始尝试使用 resque php-resque ,但我的后台工作人员一旦启动就会死亡。所以,我转到 ZeroMQ ,这似乎更符合我的需要。

我已经通过Composer设置了ZMQ,安装过程中的一切都很顺利。在我的工作脚本中(每6小时将由cron调用),我得到了:

$dataContext = new ZMQContext();
$dataDispatch = new ZMQSocket($dataContext, ZMQ::SOCKET_PUSH);
$dataDispatch->bind("tcp://*:50557");

$dataDispatch->send(0);

foreach($filesToUse as $filePath){
    $dataDispatch->send($filePath);
    sleep(1);
}

$filesToUse = array();
$blockDirs = array_filter(glob('mapBlocks/*'), 'is_dir');
foreach($blockDirs as $k => $blockDir){
    $files = glob($rootPath.$blockDir.'/*.json');
    $key = array_rand($files);
    $filesToUse[] = $files[$key];
}

$mapContext = new ZMQContext();
$mapDispatch = new ZMQSocket($mapContext, ZMQ::SOCKET_PUSH);
$mapDispatch->bind("tcp://*:50558");

$mapDispatch->send(0);

foreach($filesToUse as $blockPath){
    $mapDispatch->send($blockPath);
    sleep(1);
}

$filesToUse是用户提交的文件数组,包含用于查询游戏服务器的信息。如您所见,我循环遍历数组并将每个文件发送到ZeroMQ listener 文件,该文件包含:

$startTime = time();

$context = new ZMQContext();

$receiver = new ZMQSocket($context, ZMQ::SOCKET_PULL);
$receiver->connect("tcp://*:50557");

$sender = new ZMQSocket($context, ZMQ::SOCKET_PUSH);
$sender->connect("tcp://*:50559");

while(true){
    $file = $receiver->recv();

    // -------------------------------------------------- do all work here
    // ... ~ 75:00 [min] DATA PROCESSING SECTION foreach .recv()-ed WORK-UNIT
    // ----------------------------------------------------------------------

    $endTime = time();
    $totalTime = $endTime - $startTime;
    $sender->send('Processing of domain '.listener::$domain.' competed on '.date('M-j-y', $endTime).' in '.$totalTime.' seconds.');
}

然后,在最终 listener 文件中:

$context = new ZMQContext();
$receiver = new ZMQSocket($context, ZMQ::SOCKET_PULL);
$receiver->bind("tcp://*:50559");

while(true){
    $log = fopen($rootPath.'logs/sink_'.date('F-jS-Y_h-i-A').'.txt', 'a');
    fwrite($log, $receiver->recv());
    fclose($log);
}

当工作脚本从cron运行时,我的日志中没有确认文本。

Q1) 这是最有效的方式来做我想做的事情吗?
Q2) 我是否尝试在此处错误地使用或实施 ZeroMQ

并且,看起来,使用cron同时调用14个文件会导致负载远远超过分配。我知道我可能只是将工作设置为在一天中的不同时间运行,但如果可能的话,我希望将所有更新保留在相同的时间表上。

UPDATE:

我已经将VPS升级到2个CPU核心,因此问题的负载方面不再具有相关性。

上面的代码也已更改为当前设置。

在代码更新后,我现在收到来自cron的电子邮件,其中包含错误:

  

Fatal error: Uncaught exception 'ZMQSocketException' with message 'Failed to bind the ZMQ: Address already in use'

2 个答案:

答案 0 :(得分:6)

通过cron或ZeroMQ运行脚本将使您需要多少CPU完全没有区别。两者之间的唯一区别是cron作业每隔一段时间启动一次脚本,消息队列将根据某些用户操作启动脚本。

在一天结束时,您需要更多可用线程来运行脚本。但在你走这条路之前,你可能想看看你的脚本。也许有一种更有效的方式来编写它们以便它们不会占用太多资源?你看过你的CPU利用率了吗?大多数Web托管服务都有内置指标,您可以通过其控制台进行调整。您可能没有使用您想象的那么多资源。

事实上,运行循环遍历所有服务器的文件比单独运行文件的累积时间花费更长时间,这表明您的脚本没有正确地进行多线程处理。脚本的单个实例没有耗尽所有可用资源,因此当您运行多个脚本实例时,您只能看到速度提升。

答案 1 :(得分:4)

是的,这种方式似乎不是ZeroMQ权力的最新用法。

好消息是,可以重新设计解决方案,使其更接近最佳实践。

<强>动机

对于可扩展,轻量级的分布式处理系统设计,控制及其性能和事件管理而言,ZeroMQ是一个非常强大且非常智能的工具箱。关于设计ZeroMQ系统的最佳工程实践,已发布了许多资源。

轻量级并不意味着具有零开销的金色子弹或永久移动设备。

ZeroMQ仍然消耗额外的资源,对于目标生态系统来说,对于那些资源极少的人来说,消费更多。足迹(一些VPS系统隐藏的超线程限制vCPU / vCPU-core仿真,这里只是一个明亮的例子),人们可能会意识到,没有任何好处可以调整线程并发性影响每个Context()实例消耗额外的ZeroMQ I / O线程(1+)的成本。

异常处理?
不,防止异常和阻止是生产级,不间断,分布式处理系统的alpha / omega。您的体验变得痛苦和苦涩,但您将在ZeroMQ的软件设计实践中学到很多东西。要学习的课程之一是资源管理和优雅终止。每个流程都负责释放在其上分配的所有资源,因此被相应的 .bind() -s阻止的端口必须系统地免费并在一个清晰的方式。

(另外一个人很快就会意识到,由于操作系统开销,端口释放即时,不在代码控制之外 - 所以不要依赖在这样的端口上立即成为下一个端口重用的RTO(人们可能会在端口上找到很多帖子阻止这种方式)。)

关于资源利用率信封的事实[第一次]:

虽然目前仍然缺少有关处理效果 /资源利用率信封的定量事实,但附加图片可能有助于确定此类知识的关键重要性。

vCPU - 工作负载信封一旦市场在Sunday 22:00 GMT+0000下一天24/5开始

enter image description here

仍有+ 55%的CPU功率可用 vCPU - 工作负载和其他资源使用信封

enter image description here

Cron,queues&amp;相对优先级设置hack [NEXT]:

如果75分钟持续 WORK-UNIT 受到CPU限制或I / O限制问题的影响,那么系统配置可能会减轻cron-jobs&#39;相对优先级,以便您的系统性能得到关注&#34;在高峰时段的主要工作。有可能创建一个具有适当nice优先级的单独队列。 @Pederabo提出了一个很好的伎俩:

  

cron 通常与nice 2一起运行,但这由 queuedefs 文件控制。队列abc适用于 at batch cron
- 应该可以为队列添加一行,例如 Z ,它定义了一个特殊队列和nice值为零
- 应该能够使用at -q Z ...从控制台运行脚本。
- 如果效果很好,请将 at
中的> crontab 命令。
&#34; at&#34;命令本身将以cron的默认优先级运行,但只需几秒钟。然后,它创建的作业将使用您在队列queuedefs的{​​{1}}文件中设置的任何内容运行。

避免不必要的开销[ALWAYS]:

总有一个理由不浪费CPU-clks。简约系统设计越多。在完全相同的Z上使用 tcp:// 传输类可能是原型设计阶段的localhost练习,但从未进入24/7生产阶段。尝试避免使用从未使用过的所有服务 - 为什么要使用更多的操作系统资源(在此阶段,ZeroMQ 不是零拷贝 - 在此处出现双重分配)只是在同一个PoC localhost ipc:// 传输类对于这种操作方式来说要好得多(下面也参考下面的参考文献)

主要问题(处理设计,使用ZeroMQ工具)

基于对意图的给定,高级描述,似乎有一种方法可以完全避免 inproc:// 机制并允许整个处理管道/分发/集合成为一个不间断的ZeroMQ分布式处理系统,你可以在这里建立一个自治的 CLI界面(一个 cron 终端与ad-hoc与不间断处理系统进行通信)以便:

  • 删除对操作系统功能/限制的依赖
  • 降低与并发系统级流程维护相关的总体开销
  • 共享一个单独的,r/KBD(因此只支付一个额外的I / O线程的最低成本),因为处理似乎不是消息密集型/超低延迟敏感性

您的ZeroMQ生态系统可以帮助您构建正确缩放甚至自适应缩放功能,因为可扩展的分布式处理不仅限制您的VPS Context() 设备(如果您的{ {1}}超线程限制不允许此类共置处理满足您localhost性能范围的全天候流量。

只需将适当的传输类从 VPS 修改为 WORK-UNIT ,就可以分配任务( {{ 1}} -s)字面上全球范围内的任何处理节点,你可以&#34;插件&#34;提高处理能力......所有这些都没有源代码中的SLOC更改。

enter image description here

现在值得重新决定设计策略,不是吗?