创作人 Leo
编辑时间 Wed Jan 1,2020 at 10:13
swoole 是一款高性能的php网络框架
异步任务
高性能tcp/udp服务器
websocket服务器
swoole屏蔽了网络通信io处理、进程/线程/io复用的管理等复杂问题,使开发者可以专注业务逻辑
swoole的服务端程序运行在命令行,异步任务需要配置几个worker,worker根据具体业务需要配置
配置的过小会导致单个worker子进程压力过大,配置太多会导致系统资源不够用,一般建议配置设置为cpu的1-4倍
安装
swoole是以php扩展的形式安装
phpize
./configure
make
make install
为什么配置与CPU有关?这里涉及到计算机多任务处理的一个知识:
一个cpu的一个线程在一个时钟频率下只能处理一条指令
我们眼睛看到的系统同时处理多个程序,实际上是cpu在不停的切换进程上下文做到的
在io密集型程序中(比如web应用程序)
由于cpu计算的很快,但是io特别慢(不是一个数量级),所以cpu大部分时间都是闲置的
在很早以前,人们使用多线程/多进程的方式处理异步io,也就是当一个io事件(比如accept/recv)来到,系统不得不单独开一个进程/线程来处理
由于进程/线程不能开的太多(超级占资源),所以cpu会有大量闲置时间
io复用完美解决了这个问题,以最差的select为例(早期的Apache使用这个东西,而nginx使用epoll,瞬间秒杀apache好几条街,不知道它们之间区别的自行百度)
单个select可以同时监听最多1024个文件描述符,我们给每一个进程/线程配置一个select,如果是10个进程/线程,就可以同时监听10240个文件描述符
比如实时聊天系统,mmorpg游戏,每一个文件描述符,实际上就是一个持续在线的用户,那我单台服务器就可以支持同时万人在线
swoole内部使用reactor模型,实现方式是io复用,也就是一个worker进程维护多个event loop
当一个io事件到达,对应的端口去接收,这时候cpu可以继续处理其他指令,而不需要等待io操作完成
著名的高性能web服务器 nginx 也是同理
例:SWOOLE实现异步任务处理
AsyncServer.php
<?php
/**
* 异步任务测试
*/
date_default_timezone_set('Asia/Chongqing');
class TaskManager{
public static $inst = null ;
private $db = null;
private function __construct(){
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test', 'root', 'lixinxin');
$this->db = $pdo ;
}
private function __clone(){
}
public static function getInst(){
if (self::$inst==null && !(self::$inst instanceof self) ){
self::$inst = new self() ;
}
return self::$inst ;
}
public function processTask($jsonData){
$taskData = json_decode($jsonData, true) ;
$ret = 'err' ;
switch ($taskData['taskid']){
case 1:
//sendmsg
//
//echo 'send msg task is invoked ' , PHP_EOL ;
//file_put_contents('/tmp/swoole_test.txt', date('Y-m-d H:i:s').'send msg task is invoked ' . PHP_EOL, FILE_APPEND) ;
$ret = date('Y-m-d H:i:s').'send msg task is invoked ' ;
break;
case 2:
$save = $taskData['data'];
$sql = "insert into user(account, pwd, nickname, school) values('{$save['account']}','{$save['pwd']}','{$save['nickname']}','{$save['school']}')" ;
$this->db->query($sql) ;
$ret = date('Y-m-d H:i:s').'save data task is invoked ' ;
break;
default:
//echo 'unknow task id ' , PHP_EOL ;
break;
}
return $ret . PHP_EOL;
}
}
$serv = new swoole_server("127.0.0.1", 9501);
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test', 'root', 'lixinxin');
//设置异步任务的工作进程数量
$serv->set(array(
'task_worker_num' => 10 ,
'user' => 'lixin',
));
$serv->on('receive', function($serv, $fd, $from_id, $data) {
//投递异步任务
$task_id = $serv->task($data);
echo "Dispath AsyncTask: id=$task_id
";
});
//处理异步任务
$serv->on('task', function ($serv, $task_id, $from_id, $data) {
echo "New AsyncTask[id=$task_id]".PHP_EOL;
//返回任务执行的结果
$ret = TaskManager::getInst()->processTask($data) ;
$serv->finish($ret);
});
//处理异步任务的结果
$serv->on('finish', function ($serv, $task_id, $data) {
//echo "AsyncTask[$task_id] Finish: $data".PHP_EOL;
file_put_contents('/tmp/swoole_test.txt', $data, FILE_APPEND) ;
});
$serv->start();
AsyncClient.php
<?php
/**
* 异步任务测试,客户端程序
*/
$taskData = [
'taskid' => '2' ,
'data' => [
'account' => 'lixinxi',
'pwd' => rand() ,
'nickname' => 'lalal',
'school' => rand().' schoole'
]
] ;
$fd = fsockopen('127.0.0.1', 9501);
fwrite($fd, json_encode($taskData)) ;
fclose($fd);