34、Hyperf 3 快速使用 - Hyperf 3 Task 的两种使用方法

作者: 温新

分类: 【Hyperf 3 基础系列】

阅读: 579

时间: 2023-04-25 16:14:12

hi,我是温新,一名 PHPer

Hypref 版本:Hyperf 3.0

学习目标:学习 Task 的使用

对于 Hyperf Task 的,始终有点疑惑。从测试情况来看,它是更像是在同步处理,而非 Swoole 中的异步任务。这是一个很疑惑的点。学习的时候可以拿下面的代码进行测试。

安装与配置

第一步:安装组件

composer require hyperf/task

第二步:配置任务

<?php
// config/autoload/server.php
declare(strict_types=1);

use Hyperf\Server\Event;
use Hyperf\Server\Server;
use Swoole\Constant;

return [
    'mode' => SWOOLE_PROCESS,
    'servers' => [
       	// 省略原有代码
    ],
    'settings' => [
       // 此处省略原有代码
        
        // 配置 Task
        // Task Worker 数量,根据您的服务器配置而配置适当的数量
        'task_worker_num' => 8,
        // 因为 `Task` 主要处理无法协程化的方法,所以这里推荐设为 `false`,避免协程下出现数据混淆的情况
        'task_enable_coroutine' => false,
    ],
    'callbacks' => [
        // 此处省略原有代码
        
        // 注册 Task 回调
        Event::ON_TASK => [Hyperf\Framework\Bootstrap\TaskCallback::class, 'onTask'],
        Event::ON_FINISH => [Hyperf\Framework\Bootstrap\FinishCallback::class, 'onFinish'],
    ],
];

两种方式使用 Task

准备工作:创建一个控制器

<?php

namespace App\Controller\demo;

use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\GetMapping;
use Hyperf\Utils\Coroutine;

#[Controller]
class TestController
{
    #[GetMapping('/test/index')]
    public function index()
    {
    }
}

准备工作:创建 Task 类

<?php
// 该类为 Task 处理类
    
namespace App\Task;

use Hyperf\Logger\LoggerFactory;
use Hyperf\Utils\Coroutine;
use Psr\Log\LoggerInterface;

class TestTaskService
{
    protected LoggerInterface $logger;

    public function __construct(LoggerFactory $loggerFactory)
    {
        $this->logger = $loggerFactory->get('log', 'default');
    }
    
    public function handle($cid)
    {
        echo $cid . '====' . Coroutine::id() . PHP_EOL;
        sleep(2);
        
        echo date('Y-m-d H:i:s') .  ' Task handle() 输出结果' . PHP_EOL;
    }
}

方式一:主动投递任务

第一步:修改控制器方法,投递任务

通过控制器中的方法,使用主动投递方式进行演示。

<?php
// App\Controller\demo\TestController.php
    
#[GetMapping('/test/index')]
public function index()
{
    $users = User::query()->paginate(3);

    $container = ApplicationContext::getContainer();
    $exec      = $container->get(TaskExecutor::class);
    $exec->execute(new Task([TestTaskService::class, 'handle'], [Coroutine::id()]));

    return $users;
}

第二步:测试

curl http://192.168.31.90:9501/test/index

# 服务端打印出的结果
2====-1
2023-03-19 14:39:33 Task handle() 输出结果
[DEBUG] Event Hyperf\Framework\Event\OnTask handled by Hyperf\Task\Listener\OnTaskListener listener.
2023-03-19 14:39:33 index() 输出结果
[DEBUG] Event Hyperf\Framework\Event\OnFinish handled by Hyperf\Task\Listener\OnFinishListener listener.

从输出结果来看,TestTaskServicesleep 阻塞了该类和 TestController 控制器方法,导致两个输出的时间是一样的。Hyperf 官方文档没有说明 Task 是异步任务还是同步任务,通过配置文件看,应该是异步但是,但是从实际结果来看,却又不是异步任务, 很让人迷惑。

里想中的输出结果是,先返回控制器方法中的结果,然后再输出任务类中的结果。如控制器中的 index 方法返回 2023-03-19 14:39:33 ,任务类应该返回 2023-03-19 14:39:35。让人迷惑。

如果你有什么想法,欢迎一起交流学习。

方式二:使用注解

Hyperf 官方的文档,真的是让人一言难尽了。后面我把官方使用注解的方式注释出来。

第一步:修改 TestTaskService

<?php

namespace App\Task;

use Hyperf\Task\Annotation\Task;
use Hyperf\Utils\Coroutine;

class TestTaskService
{
    #[Task]
    public function handle($cid)
    {
        echo $cid . '====' . Coroutine::id() . PHP_EOL;
        sleep(2);
        echo date('Y-m-d H:i:s') .  ' Task handle() 输出结果' . PHP_EOL;
    }
}

第二步:修改控制器

<?php

namespace App\Controller\demo;

use App\Model\User;
use App\Task\TestTaskService;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\GetMapping;
use Hyperf\Utils\Coroutine;

#[Controller]
class TestController
{
    // 注解使用 TestTaskService。该成这种方式使用
    // 是因为官方文档提供的案例报错
    #[Inject]
    protected TestTaskService $testTaskService;

    #[GetMapping('/test/index')]
    public function index()
    {
        $users = User::query()->paginate(3);

        $this->testTaskService->handle(Coroutine::id());
        echo date('Y-m-d H:i:s') .  ' index() 输出结果' . PHP_EOL;

        return $users;
    }
}

第三步:测试

curl http://192.168.31.90:9501/test/index

# 服务端打印出的结果
2====-1
2023-03-19 14:57:53 Task handle() 输出结果
[DEBUG] Event Hyperf\Framework\Event\OnTask handled by Hyperf\Task\Listener\OnTaskListener listener.
2023-03-19 14:57:53 index() 输出结果

官方使用注解案例

<?php

use Hyperf\Utils\Coroutine;
use Hyperf\Utils\ApplicationContext;
// 此处引入的注解是 Task
use Hyperf\Task\Annotation\Task;

class AnnotationTask
{
    #[Task]
    public function handle($cid)
    {
        return [
            'worker.cid' => $cid,
            // task_enable_coroutine=false 时返回 -1,反之 返回对应的协程 ID
            'task.cid' => Coroutine::id(),
        ];
    }
}

$container = ApplicationContext::getContainer();
// 使用时,使用的是 AnnotationTask,这里报错
$task = $container->get(AnnotationTask::class);
$result = $task->handle(Coroutine::id());

错误结果:

[ERROR] No entry or class found for 'App\Controller\demo\AnnotationTask'[68] in /hydev/www/LearnHyperf/2023/hyperf-study/vendor/hyperf/di/src/Container.php

还能说啥呢?一声长叹!

我是温新,本篇文章到此结束。

请登录后再评论