PHP Conference Japan 2024

GearmanClient::addTaskBackground

(PECL gearman >= 0.5.0)

GearmanClient::addTaskBackground新增要在背景平行執行的任務

說明

public GearmanClient::addTaskBackground(
    字串 $function_name,
    字串|整數|浮點數 $workload,
    混合 $context = null,
    ?字串 $unique_key = null
): GearmanTask|false

新增一個背景任務,使其與其他任務並行執行。針對所有要並行執行的任務呼叫此方法,然後呼叫 GearmanClient::runTasks() 來執行工作。

參數

function_name

Worker 要執行的已註冊函式

workload

要處理的序列化資料

context

要與任務關聯的應用程式上下文

unique_key

用於識別特定任務的唯一 ID

回傳值

一個 GearmanTask 物件,如果無法新增任務,則為 false

範例

範例 #1 兩個任務,一個背景任務和一個非背景任務

此範例說明了執行背景任務和一般任務之間的差異。客戶端新增兩個任務來執行相同的函式,但其中一個是使用 addTaskBackground() 新增的。設定回呼以便追蹤作業的進度。一個具有模擬延遲的簡單 worker 會回報作業進度,而客戶端會透過回呼接收此進度。此範例會執行兩個 worker。請注意,背景任務不會顯示在客戶端輸出中。

<?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
"錯誤: " . $gmc->error() . "\n";
exit;
}

echo
"完成\n";

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

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

?>
<?php

# The worker script

echo "Starting\n";

# Create our worker object.
$gmworker= new GearmanWorker();

# Add default server (localhost).
$gmworker->addServer();

# Register function "reverse" with the server.
$gmworker->addFunction("reverse", "reverse_fn");

print
"Waiting for job...\n";
while(
$gmworker->work())
{
if (
$gmworker->returnCode() != GEARMAN_SUCCESS)
{
echo
"return_code: " . $gmworker->returnCode() . "\n";
break;
}
}

function
reverse_fn($job)
{
echo
"Received job: " . $job->handle() . "\n";

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

echo
"Workload: $workload ($workload_size)\n";

# This status loop is not needed, just showing how it works
for ($x= 0; $x < $workload_size; $x++)
{
echo
"Sending status: " . ($x + 1) . "/$workload_size complete\n";
$job->sendStatus($x+1, $workload_size);
$job->sendData(substr($workload, $x, 1));
sleep(1);
}

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

# Return what we want to send back to the client.
return $result;
}

?>

兩個 worker 執行的輸出

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 年前
這個方法似乎只在你不需要了解 worker 的任何資訊時才有用,也就是當你可以讓 runTasks() 完成其程式碼並銷毀 GearmanClient 物件而不會產生問題時。

如果你需要透過回呼函式從 worker 取得資料,在 runTasks() 完成後就別想了。

你必須在 GearmanClient 的「主迴圈」中阻塞你的執行,這樣它才能呼叫回呼函式。runTasks() 似乎就是那個「主迴圈」。
To Top