基本功能介绍
引用官方的介绍
某些场景对主流程没有依赖,可以直接使用任务来实现类似这些功能。框架为开发者提供了 协程 和 异步 两种任务。切记无论是 协程任务 还是 异步任务,任务里面操作都 只支持协程,且只能使用框架封装的所有 IO 操作(数据库、缓存…)
协程任务投递任务的时候不会阻塞主进程相当于一次协程调用,一般用于需要等待任务结果返回的场景。
有些场景主流程并不关心的任务执行的结果,此时就可以使用异步任务。
本人的理解
任务把一系列常用操作进行封装,使用协程可以大大的提高系统的效率,本人推荐使用任务来处理一些频繁调用的操作,例如数据库读取,可以分装到携程任务里.这里我们只介绍3种任务携程任务,异步任务,定时任务.关于同步任务我们不做介绍,因为同步任务和携程任务只能2选择1,我们推荐使用携程任务,一些非携程任务需要执行的操作可以分装成RPC服务,通过调用服务的方式来解决.
功能的相关配置选项
'httpServer' => [
'class' => HttpServer::class,
'port' => 18306,
'listener' => [
'rpc' => bean('rpcServer')
],
'process' => [
// 'monitor' => bean(MonitorProcess::class)
'crontab' => bean(CrontabProcess::class) //开启定时任务
//'log' => bean(\App\Process\LogProcess::class)
],
'on' => [
SwooleEvent::TASK => bean(SyncTaskListener::class), // 开启异步任务
SwooleEvent::TASK => bean(TaskListener::class), //开启携程任务监听
SwooleEvent::FINISH => bean(FinishListener::class) //开启任务监听的回调
],
/* @see HttpServer::$setting */
'setting' => [
'task_worker_num' => 12,
'task_enable_coroutine' => true, //这个一定要设置为true
'worker_num' => 6
]
],
task_enable_coroutine 必须为 true
task 事件和 finish 事件必须配置,且为 TaskListener::class 和 FinishListener::class
异步任务必须加入SyncTaskListener::class
任务配置与启用,在 Http Server / Rpc Server / Websocket Server 都完全一样.我们这里只列出了 Http Server 的配置
简单的示例
使用任务前,必须定义任务,定义任务很简单。如下定一个任务:
注意任务一般放到 app/task 目录下,官方已经帮我们创建了简单的实例,所以在官方原有的示例上修改即可
官方给出了示例
文件介绍
CronTask 为定时任务
SyncTask 为异步任务
TestTask 为携程任务
FinishListener 如果异步任务需要监听返回值,则在这里处理
namespace App\Task\Task;
use Swoft\Task\Annotation\Mapping\Task;
use Swoft\Task\Annotation\Mapping\TaskMapping;
/**
* Class TestTask
*
* @since 2.0
*
* @Task(name="testTask")
*/
class TestTask
{
/**
* @TaskMapping(name="list")
*
* @param int $id
* @param string $default
*
* @return array
*/
public function getList(int $id, string $default = 'def'): array
{
$rs = context()->getRequest();
echo $rs->getName() //输出任务名称
return [
'list' => [1, 3, 3],
'id' => $id,
'default' => $default
];
}
}
代码分析
需要使用的注解
@Task 注解
标记类是一个任务 这个注解是必须的,且必须定义成类注解,推荐传入name,给任务定义名称.
@TaskMapping 注解
标记方法是一个任务映射,定义任务映射的名称,必须定义,如果方法没有使用 @TaskMapping 注解,不会解析成任务。同样推荐传入name,给任务方法名称.
写代码很多时候不需要问为什么,对于学习一个框架而言,第一步是去照着例子使用它,所以尽量的跟着官方代码来修改,多尝试.
任务触发
携程任务触发
投递单个任务
Task::co(string $name, string $method, array $params = [], float $timeout = 3, array $ext = [])
//调用例子
$data = Task::co('testTask', 'list', [11,"wike_async"]);
参数详解
name 投递任务任务名称
我们上面定义的为 testTask (Task注解的值)
method 投递任务的方法名称
我们上面定义的为 list (TaskMapping注解的值)
params 任务传递的参数即是任务方法的参数,数组格式传递
例如 [10,”覆盖默认值”]
timeout 超时时间,默认 3s 超时
一般不需要修改,如果需要配置根据你的业务场景修改,如果需要任务没有超时限制,则使用异步任务
ext 任务扩展信息,会传递给任务进程里面
这个参数用的不多,一般场景不需要使用
一次性投递多个任务
Task::cos(array $tasks, float $timeout = 3, array $ext = [])
调用例子
输出
携程任务上下文
上下文获取方式
$rs= context()->getRequest()
常用方法
$rs->getServer();
getServer 获取任务 Server 信息
$rs->getTaskId();
获取任务 ID,对应 Swoole 任务 ID
$rs->getSrcWorkerId();
获取任务来自的 workerId
$rs->getData();
获取投递任务的原始是数据
$rs->getName();
获取任务名称
$rs->getMethod();
获取任务方法
$rs->getParams();
获取任务参数
$rs->getExt();
获取任务扩展信息
$rs->getType();
获取任务类型
$rs->getTaskUniqid();
获取任务全局唯一ID
异步任务投递
异步任务的定义和携程任务的定义流程是一样的,只是方法中,携程任务有上下文而异步任务没有.
Task::async(string $name, string $method, array $params = [], array $ext = [], int $dstWorkerId = -1, callable $fallback = null)
异步任务投递,返回一个全局唯一的任务ID
调用例子
$data = Task::async('sync', 'test', [12,"wike_async"]);
参数详解
name 投递任务任务名称
method 投递任务的方法名称
params 任务传递的参数即是任务方法的参数,数组格式传递
ext 任务扩展信息,会传递给任务进程里面
dstWorkerId 投递的进程 workerId,默认底层按需选择进程 workerId
fallback 失败的回调函数
异步任务我们一般是不关心返回结果的,但有些场景下我们需要监听返回结果.下面我们介绍下个人的处理方式
在异步任务的返回值中返回一个数组
代码如下
namespace App\Task\Task;
use Swoft\Task\Annotation\Mapping\Task;
use Swoft\Task\Annotation\Mapping\TaskMapping;
/**
* Class SyncTask
*
* @since 2.0
*
* @Task(name="sync")
*/
class SyncTask
{
/**
* @TaskMapping()
*
* @param string $name
*
* @return string
*/
public function test(string $name): array
{
return ['sync-test-' . $name,$taskname];
}
}
修改FinishListener文件
代码如下
namespace App\Task\Listener;
use function context;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
use Swoft\Log\Helper\CLog;
use Swoft\Task\TaskEvent;
/**
* Class FinishListener
*
* @since 2.0
*
* @Listener(event=TaskEvent::FINISH)
*/
class FinishListener implements EventHandlerInterface
{
/**
* @param EventInterface $event
*/
public function handle(EventInterface $event): void
{
// CLog::info(context()->getTaskUniqid());
$taskData = context()->getTaskData();
$data=json_decode($taskData,true);
$returndata=$data['result'][0];
$taskname=$data['result'][1];
echo $returndata."\n";
echo $taskname."\n";
if($taskname=="a"){
//执行异步a任务处逻辑
}
if($taskname=="b"){
//执行异步b任务处理逻辑
}
CLog::info($taskData);
}
}
你可以根据自己的需要修改代码,只需要逻辑对应上就可以了,返回值不一定必须2个参数,可以3个或多个,只要监听函数对应的修改即可
定时任务
在某些情况下需要定时的去执行某些任务,通常我们会使用 Linux 系统自带的 Crontab 去定时的执行我们编写好的脚本,但是这样及其不方便,首先 Linux 系统默认的 Crontab 最小单位只能支持到分钟,无法支持秒级任务,其次,如果我们重新编写脚本,则不能很方便友好的复用框架内的资源,如 Mysql 连接资源,框架中的各种类库。针对以上问题,框架为我们内置了一个 Crontab 组件,可以支持秒级任务。
简单示例
namespace App\Task\Crontab;
use Swoft\Crontab\Annotaion\Mapping\Cron;
use Swoft\Crontab\Annotaion\Mapping\Scheduled;
use Swoft\Log\Helper\CLog;
/**
* Class CronTask
*
* @since 2.0
*
* @Scheduled(name="wikecrontab")
*/
class CronTask
{
/**
* @Cron("* * * * * *")
*
*/
public function secondTask(): void
{
CLog::info('second task run: %s ', date('Y-m-d H:i:s'));
}
/**
* @Cron("0 * * * * *")
*/
public function minuteTask(): void
{
CLog::info('minute task run: %s ', date('Y-m-d H:i:s'));
}
}
代码分析
需要使用的注解
用于声明定时任务,如果是声明定时任务类,则必须使用此注解
使用示例:@Scheduled()、@Scheduled(“taskName”)、@Scheduled(name=”taskName”) 建议设置,在手动触发时有用.
@Cron()
声明需要运行的方法,如果没有使用此注解,则该方法不会被运行。
使用示例: @Cron(“ “)、@Cron(value=” “),表达式可简写,例如一个每秒都要执行的任务则可定义为 @Cron(“*”)
Cron格式说明
* * * * * *
- - - - - -
| | | | | |
| | | | | +----- day of week (0 - 6) (Sunday=0)
| | | | +----- month (1 - 12)
| | | +------- day of month (1 - 31)
| | +--------- hour (0 - 23)
| +----------- min (0 - 59)
+------------- sec (0-59)
示例:
* * * * * * 表示每秒执行一次。
0 * * * * * 表示每分钟的第0秒执行一次,即每分钟执行一次。
0 0 * * * * 表示每小时的0分0秒执行一次,即每小时执行一次。
0/10 * * * * * 表示每分钟的第0秒开始每10秒执行一次。
10-20 * * * * * 表示每分钟的第10-20秒执行一次。
10,20,30 * * * * * 表示每分钟的第10,20,30秒各执行一次。
手动执行
除了定时执行我们设置好的任务外,我们还可以在业务代码中直接手动执行我们的定时任务,方法如下。
$crontab = BeanFactory::getBean("crontab");
$crontab->execute("wikecrontab", "secondTask");