Swoft2 框架 Task 组件完全指南:从入门到高级应用,1小时必会经典教程
- 作者: 刘杰
- 来源: 技术那些事
- 阅读:259
- 发布: 2025-07-11 15:44
- 最后更新: 2025-08-23 06:54
Swoft Task 组件原理分析
Swoft Task 是 Swoft 框架中用于处理异步任务和定时任务的核心组件,其设计目标是解决 PHP 应用中耗时操作(如数据同步、邮件发送、报表生成等)的阻塞问题,基于 Swoole 扩展的多进程模型实现高性能的任务处理。以下从核心原理、架构组件、流程机制等方面进行详细分析。
概念和目标
Swoft Task 本质是对 Swoole Task 机制的高层封装,同时结合 Swoft 框架的特性(如注解、依赖注入、协程等)提供更便捷的任务开发体验。
核心目标
将耗时操作从主业务流程中剥离,通过独立的任务进程异步处理,避免阻塞 Worker 进程(处理 HTTP/WS 等请求的进程),提升应用的并发能力和响应速度。
适用场景
异步任务(如短信 / 邮件发送、日志异步写入)、定时任务(如数据备份、统计报表生成)、延迟任务(如订单超时取消)等。
底层依赖 Swoole 的 Task 机制
Swoft Task 依赖 Swoole 的TaskWorker 进程模型,这是理解其原理的基础。Swoole 的 Task 机制核心是 “进程间任务投递与处理”。下面我们先来看下 Swoole 的官方给出的进程模型。
| 进程类型 | 作用 |
|---|---|
| Master 进程 | 负责接收客户端的通信,运行 Reactor 线程,收发客户端的数据,分发数据到 Worker 进程 |
| Manager 进程 | 管理进程,管理 Worker/TaskWorker 进程 |
| Worker 进程 | 处理客户端请求(如 HTTP/WS),可投递任务 |
| TaskWorker 进程 | 独立进程,专门处理 Worker 投递的异步任务 |

Task 异步任务的交互流程(异步非阻塞)
Task 异步任务处理的是 Worker 进程投递到 TaskWorker 进程的任务,从上图的进程模型可以看出,Worker 进程和 TaskWorker 进程是通过 Unix Socket(本地套接字/域套接字)进行通信的。具体过程如下:
- Worker 通过
Swoole\Server->task()投递一个任务到task_work进程池中,此函数是非阻塞的,执行完成后会立即返回。Worker 可以继续处理新的请求。使用 Task 必须设置task_worker_num(也就是任务进程的数量),并且必须设置 Server 的 onTask 和 onFinish 事件的回调函数。 -
onTask其实是在 TaskWorker 进程中被调用的。Worker 进程通过task()函数向 TaskWorker 进程投递新的任务。接收到任务的 TaskWorker 进程会通过onTask回调函数,设置自己的进程状态为忙碌。这时将不在接收新的 Task,当onTask回调函数执行完毕后,会将当前进程状态设置为空闲,然后就可以继续接收新的Task。 -
onFinish函数,是在 worker 进程被调用的,当 Worker 进程投递到 TaskWorker 的的 Task 任务完毕时候,TaskWorker 进程会调用Swoole\Server->finish()方法,将onTask的处理结构,发送会 Worker 进程。
Task 同步任务的交互流程(异步非阻塞)
Task 的同步任务,本质上还是异步执行的。
- 由于 Worker 进程是运行在协程状态,当发起
Swoole\Server->taskCo()调用后,任务仍然被分发到处于空闲状态的 TaskWorker进程,与此同时,Worker 进程中发起taskCo调用的协程被挂起(可以理解成为此协程开始等待),进而此 Worker 进程会切换到另一个协程,继续处理任务。 -
onTask仍然是在 TaskWorker 进程中运行。接收到刚才的 Worker 进程发来的任务的 TaskWorker 进程,通过onTask设置自己进程为忙碌,开始处理任务。处理完成后,会发起Swoole\Server->finish()调用,将执行完的接口发送回传递任务的 Worker 进程,同时携带着发布此任务的协程ID。 -
onFinish是在 Worker 进程执行,Worker 进程通过协程ID 会恢复之前投递任务后挂起的协程。然后执行onFinish的回调函数。 - 由于协程在通过
taskCo处理任务时,会暂停当前协程,就导致依赖此协程的任务进入阻塞状态。对Worker 的这次请求,也就成了同步阻塞的状态。
Task 使用详解
@Task 注解
类注解,只能放到类的注解中使用,表示此类是一个 Task 任务。
框架使用时候,会收集所有的 Task 标注的类,均为 Task 任务。这些任务在框架启动时候,最终会加载在 TaskWorker 进程中,等待着投递而来的任务。
-
name
用来表示任务的名字,非必填,如果不填,最终任务名就是类名
@TaskMapping 注解
此任务相当于控制器的 @RequestMapping 注解,是用来绑定到 Task 的路由上的。根据不同的任务名和Mapping 映射,来决定投递的任务分发给哪个任务代码。
-
name
表示任务的 action,非必填,如果不填,会自动使用方法名。
Task 任务的定义示例
虽然 Task 支持同步和异步两种任务方式,但是对于 TaskWorker 进程的Task 任务而言,其实都是一样的。所以在任务的定义上都是一致的。如下示例:
php
namespace App\Task\Task;
use Swoft\Task\Annotation\Mapping\Task;
use Swoft\Task\Annotation\Mapping\TaskMapping;
/**
* Class SitemapTask
*
* @Task(name="sitemap")
*/
class SitemapTask
{
/**
* 执行生成网站地图任务
*
* @TaskMapping(name="run")
* @return void
*/
public function run()
{
echo 'sitemap create success.'. PHP_EOL;
}
}
任务投递
如何区分任务的执行方式,取决于任务的投递方式,如果是按照异步任务来投递,任务投递完后,不会阻塞,会继续向下执行;如果按照同步任务方式投递,任务投递完后,Worker 进程中处理当前请求的协程会阻塞,等待任务在 TaskWorker 执行完毕后,协程会恢复继续执行。
异步任务投递方式代码示例
php
// 投递单个同步任务 Task::async([任务名], [方法名], [参数数组])
$name = 'attach';
$method = 'upload/image';
$params = ['attach_id', 'filepath', 'file'];
Task::async('attach', 'upload/image', $params);
// 后边代码会继续执行,不会等待异步任务执行完
同步任务投递方式代码示例
php
// 投递单个同步任务 Task::co([任务名], [方法名], [参数数组])
$name = 'attach';
$method = 'upload/image';
$params = ['attach_id', 'filepath', 'file'];
Task::co('attach', 'upload/image', $params); // 后续代码会等待异步任务返回后才执行
// 投递多个同步任务 Task::cos([多个任务数组], [超时时间], [附加数据]);
$tasks = [
[$name, $method, $params];
];
Task::cos($tasks, 3, []); // 单个同步任务投递,由此函数封装而成,也是同步执行
注意点
-
投递到任务的参数数组,只能是普通数组,不能是关联数组(通过分析源码得出的结论)
-
投递到任务的参数值,只能是普通的基本数据类型(整数、字符串之类),由于数据传送打包的协议比较简单,不支持对象的传递。
-
多任务投递,会异步投递到多个 TaskWorker 执行,不是依次顺序执行。超时时间参数,用来规定单个Task任务的最大的超时时间。比如设置为3秒时,所有任务在3秒内执行完的可以正常返回数据,超过3s 的会被自动停止,返回 false。通过设置超时时间,可以让所有任务在指定时间内都返回(不一定所有任务都正常执行完毕)。所有任务全都返回后,才会触发投递任务的协程恢复执行。