Task特性的用途
task模块用来做一些异步的慢速任务,比如webim中发广播,发送邮件等。
- task进程必须是同步阻塞的
- task进程支持定时器
node.js 假如有10万个连接,要发广播时,那会循环10万次,这时候程序不能做任何事情,不能接受新的连接,也不能收包发包。
而swoole不同,丢给task进程之后,worker进程可以继续处理新的数据请求。任务完成后会异步地通知worker进程告诉它此任务已经完成。
当然task模块的作用还不仅如此,实现PHP的数据库连接池,异步队列等,还需要进一步挖掘。
task底层使用Unix Socket管道通信,是全内存的,没有IO消耗。单进程读写性能可达100万/s,不同的进程使用不同的管道通信,可以最大化利用多核。
代码示例
这里使用swoole创建websocket服务来完成task异步任务的实例,并不局限于websocket服务使用,TCP、UDP、HTTP服务都可使用,举一反三。
服务器端
WsTask.php
<?php
/**
* User: websockte 基础类
* FileName: 文件名称
* Date: 2019/3/23
* Time: 21:42
*/
class WsTask
{
CONST HOST = '0.0.0.0'; //IP地址
CONST POST = 8812; //端口号
protected $ws; //服务器对象
public function __construct()
{
$this->ws = new Swoole\WebSocket\Server(self::HOST,self::POST);
$this->ws->set([
'worker_num'=>2,
'task_worker_num'=>2 //配置此参数后将会启用task功能
]);
$this->ws->on('open',[$this,'on_open']);
$this->ws->on('message',[$this,'on_message']);
//使用task任务必须要注册task、finish回调函数
$this->ws->on('task',[$this,'on_task']);
$this->ws->on('finish',[$this,'on_finish']);
$this->ws->on('close',[$this,'on_close']);
$this->ws->start();
}
/**
* 客户端与服务器成功建立连接 完成握手
* @param object $server 服务器对象
* @param object $request Http对象
*/
public function on_open($server,$request)
{
echo "server: handshake success with fd-{$request->fd}";
}
/**
* 监听客户端发送数据
* @param object $server 服务器对象
* @param object $frame 包含客户端信息
*/
public function on_message($server,$frame)
{
echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
//投放task任务到task_worker任务池
$data = [
'task'=>1,
'fd'=>$frame->fd
];
$server->task($data);
//返回数据给客户端
$server->push($frame->fd,'hello my name is houguang',1,true);
}
/**
* 执行task任务 返回执行结果到worker进程
* @param object $sev 服务器对象
* @param int $taskId task任务ID
* @param int $workId task任务进程ID
* @param array $data task任务数据
*/
public function on_task($sev,$taskId,$workId,$data)
{
print_r($data);
echo "Tasker进程接收到数据,task_id={$taskId}\n";
echo "正在处理task异步任务....\n";
//模拟执行10s场景
sleep(10);
//返回执行结果到worker进程 调用onFinish回调函数
return '执行结果return_data';
}
/**
* 将任务处理的结果发送给worker进程
* @param object $sev 服务器对象
* @param int $taskId 任务的ID
* @param $data task任务处理返回的结果内容
*/
public function on_finish($sev,$taskId,$data)
{
echo "接收到Tasker进程处理任务结果,finish_task_id={$taskId}\n";
echo "返回处理结果给woker进程:{$data}\n";
}
/**
* 服务器发送数据到客户端
* @param array $message
* @method push 客户端连接的ID,发送的数据内容,发送数据内容的格式,默认为文本,发送结果bool
*/
public function push($message)
{
$this->ws->push($this->fd,$message);
}
/**
* 断开连接
* @param object $ser 服务器对象
* @param int $fd 客户端唯一标识
*/
public function on_close($ser,$fd)
{
echo "client {$fd} closed\n";
}
}
$server = new WsTask();
函数原型:
Server->task
投递一个异步任务到task_worker池中。此函数是非阻塞的,执行完毕会立即返回。Worker进程可以继续处理新的请求。使用Task功能,必须先设置 task_worker_num,并且必须设置Server的onTask和onFinish事件回调函数(详细说明请参照官网)。
Server->task(mixed $data, int $dst_worker_id = -1);
- $data要投递的任务数据,必须是可序列化的PHP变量
- $dst_worker_id可以制定要给投递给哪个Task进程,传入ID即可,范围是0 - (serv->task_worker_num -1)
上面示例通过创建websocket服务来实现task异步任务的使用,当客户端和服务器建立连接后,客户端发送数据到服务端,服务端监听到客户端数据,投递一个异步任务到task_worker池中,模拟耗时任务sleep10秒,然后返回push数据给客户端。
客户端
ws_client.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
Hello My Name is Houguang!!!
</body>
<script>
//websocket 地址
var wsUrl = 'ws://X.X.X.X:8812';
//创建websocket对象实例
var websocket = new WebSocket(wsUrl);
//成功和websockte服务建立连接 执行回调函数
websocket.onopen = function (evt) {
//连接成功 发送数据给服务器
websocket.send('How are you?');
console.log('wb_server-connect-success');
}
//接收服务器返回数据
websocket.onmessage = function (evt) {
console.log('ws_server-return-data:'+evt.data);
}
//连接关闭
websocket.onclose = function (evt) {
console.log('ws_server-close');
}
//发送错误
websocket.onerror = function (evt,err) {
console.log('ws_server-error-data:'+err);
}
</script>
</html>
测试
开启websocket服务
php WsTask.php
这里通过使用HTTP服务模拟客户端访问
php HttpServer.php #开启Http服务
浏览器访问 IP地址+端口号(http端口号8811)
客户端访问直接返回数据并没有延迟10秒,task任务在服务端自动执行
服务端状态 执行task任务
task任务执行完毕 通知task_worker进程执行结果