PHP Conference Japan 2024

GearmanClient::addTaskBackground

(PECL gearman >= 0.5.0)

GearmanClient::addTaskBackground添加一个在后台并行运行的任务

描述

public GearmanClient::addTaskBackground(
    string $function_name,
    string|int|float $workload,
    mixed $context = null,
    ?string $unique_key = null
): GearmanTask|false

添加一个后台任务,使其与其他任务并行运行。为所有要并行运行的任务调用此方法,然后调用GearmanClient::runTasks() 来执行工作。

参数

function_name

工作进程要执行的已注册函数

workload

要处理的序列化数据

context

与任务关联的应用程序上下文

unique_key

用于标识特定任务的唯一ID

返回值

一个GearmanTask 对象,如果任务无法添加,则返回 false

示例

示例 #1 两个任务,一个后台任务,一个非后台任务

此示例说明了运行后台任务和普通任务之间的区别。客户端添加两个任务来执行相同的函数,但其中一个使用 addTaskBackground() 添加。设置回调以便可以跟踪作业的进度。具有人工延迟的简单工作进程报告作业进度,客户端通过回调获取此进度。此示例运行两个工作进程。请注意,后台任务不会显示在客户端输出中。

<?php

# 客户端脚本

# 创建 Gearman 客户端
$gmc= new GearmanClient();

# 添加默认作业服务器
$gmc->addServer();

# 设置一些回调函数以便跟踪进度
$gmc->setCompleteCallback("reverse_complete");
$gmc->setStatusCallback("reverse_status");

# 为 "reverse" 函数添加一个任务
$task= $gmc->addTask("reverse", "Hello World!", null, "1");

# 添加另一个任务,但这个任务在后台运行
$task= $gmc->addTaskBackground("reverse", "!dlroW olleH", null, "2");

if (!
$gmc->runTasks())
{
echo
"ERROR " . $gmc->error() . "\n";
exit;
}

echo
"DONE\n";

function
reverse_status($task)
{
echo
"STATUS: " . $task->unique() . ", " . $task->jobHandle() . " - " . $task->taskNumerator() .
"/" . $task->taskDenominator() . "\n";
}

function
reverse_complete($task)
{
echo
"COMPLETE: " . $task->unique() . ", " . $task->data() . "\n";
}

?>
<?php

# 工作脚本

echo "启动\n";

# 创建工作对象。
$gmworker= new GearmanWorker();

# 添加默认服务器(localhost)。
$gmworker->addServer();

# 向服务器注册函数“reverse”。
$gmworker->addFunction("reverse", "reverse_fn");

print
"等待任务...\n";
while(
$gmworker->work())
{
if (
$gmworker->returnCode() != GEARMAN_SUCCESS)
{
echo
"返回码: " . $gmworker->returnCode() . "\n";
break;
}
}

function
reverse_fn($job)
{
echo
"收到任务: " . $job->handle() . "\n";

$workload = $job->workload();
$workload_size = $job->workloadSize();

echo
"工作负载: $workload ($workload_size)\n";

# 此状态循环并非必需,仅用于演示其工作原理
for ($x= 0; $x < $workload_size; $x++)
{
echo
"发送状态: " . ($x + 1) . "/$workload_size 完成\n";
$job->sendStatus($x+1, $workload_size);
$job->sendData(substr($workload, $x, 1));
sleep(1);
}

$result= strrev($workload);
echo
"结果: $result\n";

# 返回要发送回客户端的内容。
return $result;
}

?>

两个工作进程的输出

Received job: H:foo.local:65
Workload: !dlroW olleH (12)
1/12 complete
Received job: H:foo.local:66
Workload: Hello World! (12)
Sending status: 1/12 complete
Sending status: 2/12 complete
Sending status: 2/12 complete
Sending status: 3/12 complete
Sending status: 3/12 complete
Sending status: 4/12 complete
Sending status: 4/12 complete
Sending status: 5/12 complete
Sending status: 5/12 complete
Sending status: 6/12 complete
Sending status: 6/12 complete
Sending status: 7/12 complete
Sending status: 7/12 complete
Sending status: 8/12 complete
Sending status: 8/12 complete
Sending status: 9/12 complete
Sending status: 9/12 complete
Sending status: 10/12 complete
Sending status: 10/12 complete
Sending status: 11/12 complete
Sending status: 11/12 complete
Sending status: 12/12 complete
Sending status: 12/12 complete
Result: !dlroW olleH
Result: Hello World!

客户端输出

STATUS: 1, H:foo.local:66 - 1/12
STATUS: 1, H:foo.local:66 - 2/12
STATUS: 1, H:foo.local:66 - 3/12
STATUS: 1, H:foo.local:66 - 4/12
STATUS: 1, H:foo.local:66 - 5/12
STATUS: 1, H:foo.local:66 - 6/12
STATUS: 1, H:foo.local:66 - 7/12
STATUS: 1, H:foo.local:66 - 8/12
STATUS: 1, H:foo.local:66 - 9/12
STATUS: 1, H:foo.local:66 - 10/12
STATUS: 1, H:foo.local:66 - 11/12
STATUS: 1, H:foo.local:66 - 12/12
COMPLETE: 1, !dlroW olleH
DONE

参见

添加笔记

用户贡献笔记 3 条笔记

匿名
9 年前
此示例不太可能按预期工作。

前台作业将阻塞,但后台作业不应该阻塞。(如果阻塞,据我所知,这并非 Gearman 的文档化行为,并且对后台作业来说也没有意义)。

因此,如果前台作业完成,那么我们预期 `runTasks()` 将立即返回,而不管后台作业的状态如何。在这个示例中,如果没有其他要做的,php 脚本(客户端)将在此时退出。

为了充分利用后台作业,我们合理地假设我们仍然希望知道它们的状态。为此,您需要一个轮询循环来“检查它们”并等待它们完成。

这当然消除了后台作业的意义——因为客户端仍然会阻塞(在轮询循环中)或多或少地被占用,并且无法退出/完成。

因此,实际上,后台作业意味着您在执行后与客户端分离(因为主要目的是释放客户端,否则只需使用前台执行),这意味着客户端可能会退出,这意味着作业本身应该独立报告其状态和最终结果,而不是留给客户端。

这与前台作业相比是一个完全不同的设置。事实上,这个例子甚至混合使用两者有点愚蠢。

没有人会看到这篇帖子,因为显然世界上没有人评论过 php gearman 客户端,但这仍然是一篇好帖子。
iunknowvb at gmail dot com
7 年前
function run_process($cmd,$outputFile = '/dev/null', $append = false){
$pid=0;
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'这是一个使用 Windows 的服务器!'
$cmd = 'wmic process call create "'.$cmd.'" | find "ProcessId"';
$handle = popen("start /B ". $cmd, "r");
$read = fread($handle, 200); //读取输出
$pid=substr($read,strpos($read,'=')+1);
$pid=substr($pid,0,strpos($pid,';') );
$pid = (int)$pid;
pclose($handle); //关闭
}else{
$pid = (int)shell_exec(sprintf('%s %s %s 2>&1 & echo $!', $cmd, ($append) ? '>>' : '>', $outputFile));
}
return $pid;
}
function is_process_running($pid){
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'这是一个使用 Windows 的服务器!'
//tasklist /FI "PID eq 6480"
$result = shell_exec('tasklist /FI "PID eq '.$pid.'"' );
if (count(preg_split("/\n/", $result)) > 0 && !preg_match('/No tasks/', $result)) {
return true;
}
}else{
$result = shell_exec(sprintf('ps %d 2>&1', $pid));
if (count(preg_split("/\n/", $result)) > 2 && !preg_match('/ERROR: Process ID out of range/', $result)) {
return true;
}
}
return false;
}
function stop_process($pid){
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'这是一个使用 Windows 的服务器!'
$result = shell_exec('taskkill /PID '.$pid );
if (count(preg_split("/\n/", $result)) > 0 && !preg_match('/No tasks/', $result)) {
return true;
}
}else{
$result = shell_exec(sprintf('kill %d 2>&1', $pid));
if (!preg_match('/No such process/', $result)) {
return true;
}
}
}
$cmd='';
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'这是一个使用 Windows 的服务器!'
$cmd= $php_path.'\php.exe '.$path.'\long_process.php' ;
}else{
$cmd='/usr/bin/php -f /var/www/example.com/public/long_process.php';
}

$pid=run_process($cmd);
raitech at gmail dot com
9 年前
此方法似乎仅在您不需要了解有关工作进程的任何信息时有用,当您可以让 `runTasks()` 完成其代码并销毁 GearmanClient 对象而没有任何问题时。

如果您需要通过回调从工作进程获取数据,则在 `runTasks()` 完成后将其忽略。

您必须阻塞 GearmanClient 的“主循环”中的执行,以便它可以调用回调。`runTasks()` 似乎就是那个“主循环”。
To Top