---恢复内容开始---
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主要分为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
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
然后在日志中就可以看到执行时的日志:
源码地址:https://git.oschina.net/chenjunliang2017/thinkphp5.git