获取Gearman中多个作业的响应,但在超时后中止

时间:2018-02-28 19:06:42

标签: php gearman

简而言之:我希望在Gearman客户端中调用runTasks()的总时间超时。

我觉得自己不能成为第一个想要这个的人,但我找不到如何把它放在一起的例子。

这就是我想要实现的目标:

  1. 在PHP脚本中,使用Gearman客户端并行启动一系列作业
  2. 每个作业都会产生一些搜索结果,PHP脚本需要处理这些结果
  3. 有些工作可能需要一段时间才能运行,但我不想等待最慢的工作。相反,在N毫秒之后,我想处理已完成的所有作业的结果,并中止或忽略那些尚未完成的作业。
  4. 使用PHP GearmanClient的addTask()runTasks()方法,要求1和2很简单,但这会阻止所有提交的作业完成,所以不符合要求3。

    以下是我迄今为止尝试过的一些方法:

    • 使用setTimeout()设置的超时测量连接空闲的时间,这不是我感兴趣的内容。
    • 使用后台作业或任务,似乎没有任何方法可以检索工作人员返回的数据。有几个问题已涵盖此问题:1 2
    • addTaskStatus()示例中的自定义轮询循环几乎是我需要的,但它使用后台作业,因此再也看不到任何结果。它还包括模糊的评论"更好的方法是使用事件回调",而不解释它意味着什么回调,或者他们要替换的示例的哪一部分。
    • 客户端选项包括GEARMAN_CLIENT_NON_BLOCKING模式,但我不明白如何使用此模式,如果非阻止runTasks()与使用setTaskBackground()而不是{{1}不同}}。

    我已经看到回复通信的建议可能只是使用不同的机制,比如共享数据存储,但在这种情况下,我不妨放弃Gearman并使用RabbitMQ构建自定义解决方案。

2 个答案:

答案 0 :(得分:0)

我认为我找到了一个可行的解决方案,尽管我仍然对替代方案感兴趣。

关键是在I / O超时后再次调用runTasks()继续等待先前的同步任务,因此您可以从这些部分构建轮询循环:

  • 使用addTask()设置的同步,并行任务。
  • 使用setCompleteCallback()设置的完成回调设置,用于跟踪已完成的任务以及尚未处理的任务数。
  • 设置为setTimeout()的低I / O超时,作为您的轮询频率。
  • 在循环中重复调用runTasks(),在完成所有任务或达到总体超时时退出。这也可能有更复杂的退出条件,例如“在N秒之后,或至少X结果”等等。

最大的缺点是超时会发出PHP警告,因此您必须使用自定义错误处理程序或@运算符来压缩它。

这是一个经过充分测试的例子:

// How long should we wait each time around the polling loop if nothing happens
define('LOOP_WAIT_MS', 100);
// How long in total should we wait for responses before giving up
define('TOTAL_TIMEOUT_MS', 5000);

$client= new GearmanClient();
$client->addServer();

// This will fire as each job completes.
// In real code, this would save the data for later processing,
// as well as tracking which jobs were completed, tracked here with a simple counter.
$client->setCompleteCallback(function(GearmanTask $task) use (&$pending) {
        $pending--;
        echo "Complete!\n";
        echo $task->data();
});

// This array can be used to track the tasks created. This example just counts them.
$tasks = [];
// Sample tasks; the workers sleep for specified number of seconds before returning some data.
$tasks[] = $client->addTask('wait', '2');
$tasks[] = $client->addTask('wait', '2');
$tasks[] = $client->addTask('wait', '2');
$tasks[] = $client->addTask('wait', '2');
$tasks[] = $client->addTask('wait', '2');
$tasks[] = $client->addTask('wait', '2');

$pending = count($tasks);

// This is the key polling loop; runTasks() here acts as "wait for a notification from the server"
$client->setTimeout(LOOP_WAIT_MS);
$start = microtime(true);
do {
        // This will abort with a PHP Warning if no data is received in LOOP_WAIT_MS milliseconds
        // We ignore the warning, and try again, unless we've reached our overall time limit
        @$client->runTasks();
} while (
        // Exit the loop once we run out of time
        microtime(true) - $start < TOTAL_TIMEOUT_MS / 1000
        // Additional loop exit if all tasks have been completed
        // This counter is decremented in the complete callback
        && $pending > 0
);

echo "Finished with $pending tasks unprocessed.\n";

答案 1 :(得分:0)

您的用例听起来就像为CAN_DO_TIMEOUT创建的那样:

  

CAN_DO_TIMEOUT

 Same as CAN_DO, but with a timeout value on how long the job
 is allowed to run. After the timeout value, the job server will
 mark the job as failed and notify any listening clients.

 Arguments:
 - NULL byte terminated Function name.
 - Timeout value.

因此,对于任何(Worker,Function)元组,您可以定义Worker处理作业的最长时间,否则它将被丢弃。

不幸的是,C服务器中似乎有bug,其中超时在1000秒时被硬编码。

一种解决方法是,如果您能够在gearman之外实现超时逻辑。例如,如果您使用卷曲,肥皂,插座等,通常可以通过调整这些设置来达到预期的效果。