34、Hyperf 3 快速使用 - Hyperf 3 Task 的两种使用方法
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.
从输出结果来看,TestTaskService
中 sleep
阻塞了该类和 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
还能说啥呢?一声长叹!
我是温新,本篇文章到此结束。
请登录后再评论