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

cha*_*tor 5 php cron background-process zeromq

我有一个显示游戏服务器数据的网站.游戏具有不同的"域"(实际上只是单独的服务器),用户可以使用它们.

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

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

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

我开始研究消息队列并将一些信息传递给将执行相关工作的后台进程.我开始试图使用resquephp-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);
}
Run Code Online (Sandbox Code Playgroud)

$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.');
}
Run Code Online (Sandbox Code Playgroud)

然后,在最终 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);
}
Run Code Online (Sandbox Code Playgroud)

运行worker脚本时cron,我的日志中没有确认文本.

Q1)这是我做的最有效的方式吗?
Q2)ZeroMQ在这里尝试使用或实施不正确吗?

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


更新:

我已经将VPS升级到2个CPU内核,所以问题的负载方面实际上并不是那么相关.

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

在代码更新后,我从cron现在收到一封错误邮件:

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

Llo*_*nks 6

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

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

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