title: Asynchronous tasks meta:

  • name: description content: Mainly about how to use swoole extension of PHP asynchronous task delivery, and the common swoole asynchronous task error
  • name: keywords content: swoole|swoole extension|swoole framework|EasySwoole Asynchronous tasks|swoolea synchronous|swoole Asynchronous process

Installation

  1. composer require easyswoole/task

Standalone use example

  1. use EasySwoole\Task\Config;
  2. use EasySwoole\Task\Task;
  3. /*
  4. The number of worker processes, temporary directory, process name, maximum number of concurrent tasks, exception callbacks and so on can be modified in the configuration item
  5. */
  6. $config = new Config();
  7. $task = new Task($config);
  8. $http = new swoole_http_server("127.0.0.1", 9501);
  9. /*
  10. Add the service
  11. */
  12. $task->attachToServer($http);
  13. $http->on("request", function ($request, $response)use($task){
  14. if(isset($request->get['sync'])){
  15. $ret = $task->sync(function ($taskId,$workerIndex){
  16. return "{$taskId}.{$workerIndex}";
  17. });
  18. $response->end("sync result ".$ret);
  19. }else if(isset($request->get['status'])) {
  20. var_dump($task->status());
  21. }else{
  22. $id = $task->async(function ($taskId,$workerIndex){
  23. \co::sleep(1);
  24. var_dump("async id {$taskId} task run");
  25. });
  26. $response->end("async id {$id} ");
  27. }
  28. });
  29. $http->start();

Used in the framework

The 3.3.0 version of EasySwoole asynchronous tasks is implemented as a separate component to implement and resolve asynchronous tasks:

  • Undeliverable closure task
  • Unable to continue delivering tasks in other custom processes such as TaskWorker
  • Realize task flow limitation and state monitoring

For the old version, please do the following :

  • Configuration item deletion : MAIN_SERVER.SETTING.task_worker_num 与 MAIN_SERVER.SETTING.task_enable_coroutine
  • Configuration item added: MAIN_SERVER.TASK ,The default value is ['workerNum'=>4,'maxRunningNum'=>128,'timeout'=>15]
  • Note that EasySwoole’s Temp directory cannot be in a Shared directory between the virtual machine and the host, which will result in a UnixSocket link being created without permission

Task manager

EasySwoole defines a task manager with a full namespace of: EasySwoole\EasySwoole\Task\TaskManager It is a singleton that inherits an ‘EasySwoole\Task\Task’ object and is instantiated in the main service creation event of ‘core.php’. Can be invoked anywhere after the service is started.

Post closure tasks

  1. TaskManager::getInstance()->async(function (){
  2. var_dump('r');
  3. });

::: warning Since PHP itself cannot serialize closures, the closure post is done by reflecting the closure function, getting PHP code to serialize PHP code directly, and then eval code directly, so the post closure cannot use external object references and resource handles. For complex tasks, use task template methods.
:::

Delivery callable

  1. TaskManager::getInstance()->async(callable);

Delivery template task

  1. use EasySwoole\Task\AbstractInterface\TaskInterface;
  2. class Task implements TaskInterface
  3. {
  4. function run(int $taskId, int $workerIndex)
  5. {
  6. var_dump('c');
  7. TaskManager::getInstance()->async(function (){
  8. var_dump('r');
  9. });
  10. }
  11. function onException(\Throwable $throwable, int $taskId, int $workerIndex)
  12. {
  13. echo $throwable->getMessage();
  14. }
  15. }
  16. TaskManager::getInstance()->async(Task::class);
  17. //or
  18. TaskManager::getInstance()->async(new Task());

Asynchronous tasks - below version 3.3.0

::: warning Refer to the Demo: Asynchronous task handling demo :::

::: warning Asynchronous task manager class:EasySwoole\EasySwoole\Swoole\Task\TaskManager :::

Asynchronous task delivery can occur anywhere after the service is started. To simplify asynchronous task delivery, the framework encapsulates the task manager for synchronous/asynchronous task delivery,There are two ways to post a Task: directly post a closure and directly post a job template class

Drop the closure directly

When the task is relatively simple, the closure can be directly posted anywhere including in various callbacks after the controller/timer/service starts

  1. // Post in the controller example
  2. function index()
  3. {
  4. \EasySwoole\EasySwoole\Swoole\Task\TaskManager::async(function () {
  5. echo "Perform asynchronous tasks...\n";
  6. return true;
  7. }, function () {
  8. echo "Asynchronous task finished...\n";
  9. });
  10. }
  11. // An example of delivery in a timer
  12. \EasySwoole\Component\Timer::getInstance()->loop(1000, function () {
  13. \EasySwoole\EasySwoole\Swoole\Task\TaskManager::async(function () {
  14. echo "Perform asynchronous tasks...\n";
  15. });
  16. });

::: warning

Since PHP itself cannot serialize closures, the closure post is done by reflecting the closure function, getting PHP code to serialize PHP code directly, and then eval code directly, so the post closure cannot use external object references and resource handles. For complex tasks, use task template methods.

:::

The following usage is incorrect:

  1. $image = fopen('test.php', 'a');//Serializing data using an external resource handle will not exist
  2. $a=1;//Using external variables will not exist
  3. TaskManager::async(function ($image,$a) {
  4. var_dump($image);
  5. var_dump($a);
  6. $this->testFunction();//Using a reference to an external object will cause an error
  7. return true;
  8. },function () {});

Post the task template class

When the task is complex, logical and fixed, you can create a task template in advance and post the task template directly to simplify the operation and facilitate the delivery of the same task in multiple different places. First, you need to create a task template

::: warning Asynchronous task template class:EasySwoole\EasySwoole\Swoole\Task\AbstractAsyncTask :::

  1. class Task extends \EasySwoole\EasySwoole\Swoole\Task\AbstractAsyncTask
  2. {
  3. /**
  4. * Execute the content of the task
  5. * @param mixed $taskData taskData
  6. * @param int $taskId The task number of the execution task
  7. * @param int $fromWorkerId Assign the worker process number of the task
  8. * @author : evalor <master@evalor.cn>
  9. */
  10. function run($taskData, $taskId, $fromWorkerId,$flags = null)
  11. {
  12. // Note that the task number is not absolutely unique
  13. // Each worker process is numbered from 0
  14. // So $fromWorkerId + $taskId is the absolutely unique number
  15. // !!! The return result is needed to complete the task
  16. }
  17. /**
  18. * A callback to the completion of a task
  19. * @param mixed $result The result returned when the task is completed
  20. * @param int $task_id The task number of the execution task
  21. * @author : evalor <master@evalor.cn>
  22. */
  23. function finish($result, $task_id)
  24. {
  25. // The processing of the end of task execution
  26. }
  27. }

Then, as in the previous example, you can post anywhere after the service is started, just replace the closure with an instance of the task template class

  1. // Post in the controller example
  2. function index()
  3. {
  4. // Instantiate the task template class and bring in the data to get the data in the task class $taskData parameter
  5. $taskClass = new Task('taskData');
  6. \EasySwoole\EasySwoole\Swoole\Task\TaskManager::async($taskClass);
  7. }
  8. // An example of delivery in a timer
  9. \EasySwoole\Component\Timer::getInstance()->loop(1000, function () {
  10. \EasySwoole\EasySwoole\Swoole\Task\TaskManager::async($taskClass);
  11. });

Use a quick task template

By inheriting ‘EasySwoole, EasySwoole, Swoole, Task, QuickTaskInterface’, add the run method, you can achieve a Task template, through the direct post class name to run the Task:

  1. <?php
  2. namespace App\Task;
  3. use EasySwoole\EasySwoole\Swoole\Task\QuickTaskInterface;
  4. class QuickTaskTest implements QuickTaskInterface
  5. {
  6. static function run(\swoole_server $server, int $taskId, int $fromWorkerId,$flags = null)
  7. {
  8. echo "Quick task template";
  9. // TODO: Implement run() method.
  10. }
  11. }

Controller call:

  1. $result = TaskManager::async(\App\Task\QuickTaskTest::class);

Deliver asynchronous tasks in a custom process

Due to the particularity of custom process, it is not possible to directly call Swoole’s asynchronous task-related method for asynchronous task delivery. The framework has encapsulated the relevant method to facilitate asynchronous task delivery, please see the following example

::: warning Custom process post asynchronous task without finish callback
:::

  1. public function run(Process $process)
  2. {
  3. // Delivery the closure directly
  4. TaskManager::processAsync(function () {
  5. echo "process async task run on closure!\n";
  6. });
  7. // Delivery task class
  8. $taskClass = new TaskClass('task data');
  9. TaskManager::processAsync($taskClass);
  10. }

Concurrent execution of tasks

Sometimes it is necessary to execute multiple asynchronous tasks at the same time. The most typical example is data collection. After collecting and processing multiple data, concurrent task delivery can be carried out,A result set is returned after all tasks are executed

  1. // multitasking
  2. $tasks[] = function () { sleep(50000);return 'this is 1'; }; // task1
  3. $tasks[] = function () { sleep(2);return 'this is 2'; }; // task2
  4. $tasks[] = function () { sleep(50000);return 'this is 3'; }; // task3
  5. $results = \EasySwoole\EasySwoole\Swoole\Task\TaskManager::barrier($tasks, 3);
  6. var_dump($results);

::: warning Note: the Barrier is a block waiting for execution, and all tasks will be distributed to different Task processes (there should be enough Task processes, or they will also be blocked) to execute synchronously, and all results will not be returned until all tasks finish execution or timeout. The default Task timeout is 0.5 seconds,Only task 2 in the example above can execute properly and return results。 :::

Class function reference

  1. /**
  2. * Deliver an asynchronous task
  3. * @param mixed $task Asynchronous tasks that need to be delivered
  4. * @param mixed $finishCallback The callback function after the task executes
  5. * @param int $taskWorkerId Specify the Task process number to post (randomly post to idle process by default)
  6. * @return bool Successful delivery returns the integer $task_id, Return false on delivery failure
  7. */
  8. static function async($task,$finishCallback = null,$taskWorkerId = -1)
  1. /**
  2. * Deliver an asynchronous task
  3. * @param mixed $task Asynchronous tasks that need to be delivered
  4. * @param float $timeout Task timeout
  5. * @param int $taskWorkerId Specify the Task process number to post (randomly post to idle process by default)
  6. * @return bool|string Successful delivery returns the integer $task_id, Return false on delivery failure
  7. */
  8. static function sync($task, $timeout = 0.5, $taskWorkerId = -1)
  1. /**
  2. * Deliver tasks in asynchronous processes
  3. * @param array $taskList List of tasks to perform
  4. * @param float $timeout Task timeout
  5. * @return array|bool The execution result of each task
  6. */
  7. static function barrier(array $taskList, $timeout = 0.5)