Dutch PHP Conference 2021 - Call for Papers

GearmanClient::addTaskBackground

(PECL gearman >= 0.5.0)

GearmanClient::addTaskBackgroundДобавить фоновую задачу для работы в параллельном режиме

Описание

public GearmanClient::addTaskBackground ( string $function_name , string $workload , mixed &$context = ? , string $unique = ? ) : GearmanTask

Добавляет фоновую задачу для параллельной работы с другими задачами. Вызовите этот метод для всех задач, которые будут работать параллельно, а затем вызовите GearmanClient::runTasks() для выполнения работ.

Список параметров

function_name

Зарегистрированная функция, вызываемая рабочим процессом

workload

Сериализованные данные, подлежащие обработке

context

Контекст приложения, связываемый с задачей

unique

Уникальный ID, назначаемый определённой задаче

Возвращаемые значения

Объект GearmanTask или false, если задача не может быть добавлена.

Примеры

Пример #1 Две задачи, одна в фоновом режиме, а другая нет

Этот пример иллюстрирует различие между выполнением фоновой задачи и обычной задачей. Клиент добавляет две задачи для выполнения одних и тех же функций, но одна добавлена с помощью addTaskBackground(). Callback-функция установлена так, чтобы выполнение задания можно было проследить. Простой обработчик с искусственной задержкой сообщает статус выполнения задания, и клиент понимает это посредством вызова callback-функции. Два обработчика запущены для этого примера. Обратите внимание, что фоновая задача не показывается в клиентском выводе.

<?php

# Клиентский скрипт

# Создание нашего клиента
$gmc= new GearmanClient();

# Добавление сервера задач по умолчанию
$gmc->addServer();

# Установка нескольких callback-функций. Таким образом, мы сможем отслеживать выполнение
$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

# Скрипт обработчика

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 ($x0$x $workload_size$x++)
  {
    echo 
"Отправка статуса: " . ($x 1) . "/$workload_size выполнено\n";
    
$job->sendStatus($x+1$workload_size);
    
$job->sendData(substr($workload$x1));
    
sleep(1);
  }

  
$resultstrrev($workload);
  echo 
"Результат: $result\n";

  
# Возвращаем то, что мы хотим вернуть клиенту
  
return $result;
}

?>

Вывод рабочего для двух запущенных работников:

Полученная задача: H:foo.local:65
Рабочая нагрузка: !dlroW olleH (12)
1/12 выполнено
Полученная задача: H:foo.local:66
Рабочая нагрузка: Hello World! (12)
Отправка статуса: 1/12 выполнено
Отправка статуса: 2/12 выполнено
Отправка статуса: 2/12 выполнено
Отправка статуса: 3/12 выполнено
Отправка статуса: 3/12 выполнено
Отправка статуса: 4/12 выполнено
Отправка статуса: 4/12 выполнено
Отправка статуса: 5/12 выполнено
Отправка статуса: 5/12 выполнено
Отправка статуса: 6/12 выполнено
Отправка статуса: 6/12 выполнено
Отправка статуса: 7/12 выполнено
Отправка статуса: 7/12 выполнено
Отправка статуса: 8/12 выполнено
Отправка статуса: 8/12 выполнено
Отправка статуса: 9/12 выполнено
Отправка статуса: 9/12 выполнено
Отправка статуса: 10/12 выполнено
Отправка статуса: 10/12 выполнено
Отправка статуса: 11/12 выполнено
Отправка статуса: 11/12 выполнено
Отправка статуса: 12/12 выполнено
Отправка статуса: 12/12 выполнено
Результат: !dlroW olleH
Результат: Hello World!

Клиент выводит:

Статус: 1, H:foo.local:66 - 1/12
Статус: 1, H:foo.local:66 - 2/12
Статус: 1, H:foo.local:66 - 3/12
Статус: 1, H:foo.local:66 - 4/12
Статус: 1, H:foo.local:66 - 5/12
Статус: 1, H:foo.local:66 - 6/12
Статус: 1, H:foo.local:66 - 7/12
Статус: 1, H:foo.local:66 - 8/12
Статус: 1, H:foo.local:66 - 9/12
Статус: 1, H:foo.local:66 - 10/12
Статус: 1, H:foo.local:66 - 11/12
Статус: 1, H:foo.local:66 - 12/12
Завершено: 1, !dlroW olleH
Выполнено

Смотрите также

  • GearmanClient::addTask() - Добавить задачу, которая будет выполнена в параллельном режиме
  • GearmanClient::addTaskHigh() - Добавить высокоприоритетную задачу для работы в параллельном режиме
  • GearmanClient::addTaskLow() - Добавить низкоприоритетную задачу для работы в параллельном режиме
  • GearmanClient::addTaskHighBackground() - Добавить высокоприоритетную фоновую задачу для работы в параллельном режиме
  • GearmanClient::addTaskLowBackground() - Добавить низкоприоритетную фоновую задачу для работы в параллельном режиме
  • GearmanClient::runTasks() - Запустить список задач в параллельном режиме

add a note add a note

User Contributed Notes 3 notes

up
16
Anonymous
5 years ago
It is unlikely this example works quite as advertised.

The foreground job will block, however the background job should not.. (and if it does that's not documented Gearman behaviour far as I know and would not make sense for a Background job).

So, if the foreground job completes, then we would expect runTasks() to return at that moment, regardless of the status of the background job(s). With nothing else to do, the php script (the client) in this example would exit at that point.

To fully-utilize background jobs, it's reasonable to assume that we still wish to know their status. To do that, you need a polling loop that "checks up on them" and waits until their completion.

That eliminates the point of background jobs of course - because the client would still be Blocking (in a polling loop) more or less occupied, and unable to exit/finish.

So, in practice, background jobs mean you are decoupled from the client upon execution (because the main point is to free up the client, otherwise just use foreground execution), which means the client is likely going to exit, meaning the jobs themselves should be reporting their status and final result independently, not leaving it up to the client.

It's a significantly different setup compared to foreground jobs. In fact this example is kind of silly to even mix the two.

Nobody will ever see this post, because apparently nobody in the world has ever commented on the php gearman client, but it's a good post never the less.
up
2
iunknowvb at gmail dot com
3 years ago
function run_process($cmd,$outputFile = '/dev/null', $append = false){
                        $pid=0;
                    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'This is a server using Windows!';
                            $cmd = 'wmic process call create "'.$cmd.'" | find "ProcessId"';
                            $handle = popen("start /B ". $cmd, "r");
                            $read = fread($handle, 200); //Read the output
                            $pid=substr($read,strpos($read,'=')+1);
                            $pid=substr($pid,0,strpos($pid,';') );
                            $pid = (int)$pid;
                            pclose($handle); //Close
                    }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') {//'This is a server using 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') {//'This is a server using 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') {//'This is a server using 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);
up
-2
raitech at gmail dot com
5 years ago
This method seems only useful when you doesn't need to know something about the worker, when you can let runTasks() finishes its code and destroy the GearmanClient object without a problem.

If you need to get data from worker via callbacks, forget it after runTasks() finishes.

You have to block your execution in the GearmanClient "main loop", so it can call the callbacks. It seems that runTasks() is that "main loop".
To Top