PHP的异步、并行、高性能网络通信引擎,使用纯C语言编写,提供了PHP语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,异步Redis,数据库连接池,AsyncTask,消息队列,毫秒定时器,异步文件读写,异步DNS查询。 Swoole内置了Http/WebSocket服务器端/客户端、Http2.0服务器端。
前提:
保证你的环境下已经安装好swoole拓展!如不知道怎么安装请上网百度 参考下面:
博主的是在ubuntu下安装的:
安装的时候尽量使用php自带的pecl安装,一键安装,没那么多事儿。耐操的同学可以试试自行编译安装,虽然也比较简单,但容易出现一些版本的问题。
一键安装:
/usr/local/php/bin/pecl install swoole
复制代码
确保产生的swoole.so文件在/usr/local/php/lib/php/extensions/no-debug-non-zts-20131226下面(一般都在),不在的话考到这里
然后在php.ini文件里添加extension="/usr/local/php/lib/php/extensions/no-debug-non-zts-20131226/swoole.so",然后重启php-fpm加载swoole扩展模块即可。 使用php -m 命令查看加载的模块,看看有没有swoole
正文
swoole主要分为client和server两个部分,client向资源池发送任务,server从资源池中去任务处理。两者监听同一个端口号。
Server: server 定义了一条命令 swoole-server需要以守卫进程的方式运行,一直监听9800端口号,只要有client法该客户端发送任务,server就可以捕获并且处理。
server 生命周期分为 Receive(接收)、Task(执行)、Finish(执行完成),详细见下面代码和注释。
<?php
namespace app\console\swoole;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Log;
class ServerCommand extends Command {
private $serv;
protected $isLog = true;//是否写log,生产环境请设置为false
protected function configure() {
$this->setName('swoole-server')->setDescription('swoole服务端');
}
protected function execute(Input $input, Output $output) {
$this->serv = new \swoole_server('0.0.0.0', 9800); // 允许所有IP访问
$this->logFile = RUNTIME_PATH . 'swoole-log' . DS . date('Ymd') . '.log';
$this->serv->set([
'worker_num' => 12,
// 一般设置为服务器CPU数的1-4倍
'task_worker_num' => 20,
// task进程的数量(一般任务都是同步阻塞的,可以设置为单进程单线程)
'daemonize' => true,
'open_eof_split' => true,//打开eof_split检测
'package_eof' => PHP_EOL,//设置EOF
// 以守护进程执行
// 'task_ipc_mode' => 1, // 使用unix socket通信,默认模式
'log_file' => $this->logFile,
// swoole日志
// 数据包分发策略(dispatch_mode=1/3时,底层会屏蔽onConnect/onClose事件,
// 原因是这2种模式下无法保证onConnect/onClose/onReceive的顺序,非请求响应式的服务器程序,请不要使用模式1或3)
// 'dispatch_mode' => 2, // 固定模式,根据连接的文件描述符分配worker。这样可以保证同一个连接发来的数据只会被同一个worker处理
]);
$this->serv->on('Receive', [
$this,
'onReceive'
]);
$this->serv->on('Task', [
$this,
'onTask'
]);
$this->serv->on('Finish', [
$this,
'onFinish'
]);
$this->serv->start();
}
/**
* 测试使用,生成环境不要用,效率极低
* @param $strLogContext
*/
public function error($strLogContext) {
$fp = @fopen($this->logFile, "a+");
@fputs($fp, $strLogContext . PHP_EOL);
@fclose($fp);
}
/**
* 接收到数据时回调此函数,发生在worker进程中
* $server,swoole_server对象
* $fd,TCP客户端连接的文件描述符
* $from_id,TCP连接所在的Reactor线程ID
* $data,收到的数据内容,可能是文本或者二进制内容
*/
public function onReceive($serv, $fd, $from_id, $data) {
$str = PHP_EOL . "=========== onReceive ============" . PHP_EOL;
$str .= "Get Message From Client {$fd}:{$data}" . '--fromid--' . $from_id . PHP_EOL;
Log::record($str, 'swoole');
$serv->task($data);
}
/**
* 在task_worker进程内被调用。worker进程可以使用swoole_server_task函数向task_worker进程投递新的任务。当前的Task进程在调用onTask回调函数时会将进程状态切换为忙碌,
* 这时将不再接收新的Task,当onTask函数返回时会将进程状态切换为空闲然后继续接收新的Task。
* $task_id是任务ID,由swoole扩展内自动生成,用于区分不同的任务。$task_id和$src_worker_id组合起来才是全局唯一的,不同的worker进程投递的任务ID可能会有相同
* $src_worker_id来自于哪个worker进程
* $data 是任务的内容
*/
public function onTask($serv, $task_id, $src_worker_id, $data) {
$array = json_decode($data, true);
$str = "=========== onTask ============" . PHP_EOL;
$str .= var_export($array, 1) . PHP_EOL;
Log::record($str, 'swoole');
return $array;
}
/**
* 当worker进程投递的任务在task_worker中完成时,task进程会通过swoole_server->finish()方法将任务处理的结果发送给worker进程
* $task_id是任务的ID
* $data是任务处理的结果内容(也就是onTask()函数,中return的值)
*/
public function onFinish($serv, $task_id, $data) {
if($data['errno'] > 0) {
$str = "onFinish ERROR:{$task_id} " . json_encode($data) . '|' . date('Y-m-d H:i:s') . PHP_EOL;
Log::record(['more_info' => [$str . '|' . date('Y-m-d H:i:s') . PHP_EOL]], 'elk');
Log::save();
}
}
}
复制代码
client是想进程池发送任务的,同样的是监听9800端口。
下面的demo代码是把client也定义了一条命令,实际业务中也可以直接在 业务逻辑中调用_sendData($data)方法,前提是client类不要在继承Command类,否则会出错。
<?php
/**
* Created by PhpStorm.
* Date: 2017/2/15
* Time: 14:26
*/
namespace app\console\swoole;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Log;
class ClientCommand extends Command{
protected function configure() {
$this->setName('swoole-client')->setDescription('swoole客户端');
}
protected function execute(Input $input, Output $output) {
Log::record('swoole-client', 'swoole');
$data = [1,2,3];
$this->_sendData(json_encode($data));
}
private function _sendData($data) {
$client = new \swoole_client(SWOOLE_SOCK_TCP);
$client->connect('127.0.0.1', 9800, 1);
$client->send($data.PHP_EOL);
}
}
复制代码
使用方法
定义了上面两个命令之后,可以执行了。
在项目中先运行server,让它开始监听
php think swoole-server
复制代码
然后执行client开始投递任务。
php think swoole-client
复制代码
然后在日志中就可以看到执行时的日志: