前提安装好swoole后,php添加好swoole扩展
deepin 安装请参考 博客其他文章
swoole server task 类直接上代码
服务端代码(server.php)
/* swoole Task运行实例 Task简介 Swoole的业务逻辑部分是同步阻塞运行的,如果遇到一些耗时较大的操作,例如访问数据库、广播消息等,就会影响服务器的响应速度。因此Swoole提供了Task功能,将这些耗时操作放到另外的进程去处理,当前进程继续执行后面的逻辑. 运行Task,需要在swoole服务中配置参数 task_worker_num,即可开启task功能。此外,必须给swoole_server绑定两个回调函数:onTask和onFinish。这两个回调函数分别用于执行Task任务和处理Task任务的返回结果。 */ class server { private $serv; /** * [__construct description] * 构造方法中,初始化 $serv 服务 */ public function __construct() { //服务地址,以后最好做成配置 $this->serv = new swoole_server('0.0.0.0', 9501); //初始化swoole服务 $this->serv->set(array( 'worker_num' => 8, 'daemonize' => false, //是否作为守护进程,此配置一般配合log_file使用 'max_request' => 1000, 'log_file' => './swoole.log', 'task_worker_num' => 8 )); //设置监听 $this->serv->on('Start', array($this, 'onStart')); $this->serv->on('Connect', array($this, 'onConnect')); $this->serv->on("Receive", array($this, 'onReceive')); $this->serv->on("Close", array($this, 'onClose')); $this->serv->on("Task", array($this, 'onTask')); $this->serv->on("Finish", array($this, 'onFinish')); //开启 $this->serv->start(); } public function onStart($serv) { echo SWOOLE_VERSION . " onStart\n"; } public function onConnect($serv, $fd) { echo $fd."Client Connect.\n"; } public function onReceive($serv, $fd, $from_id, $data) { echo "Get Message From Client {$fd}:{$data}\n"; // send a task to task worker. $param = array( 'fd' => $fd ); // start a task $serv->task(json_encode($param)); //如果不调用task //直接返回 //调用处理代码,的到结果$result,然后返回 // $serv->send($fd['fd'] ,$result); echo "Continue Handle Worker\n"; } public function onClose($serv, $fd) { echo "Client Close.\n"; } public function onTask($serv, $task_id, $from_id, $data) { echo "This Task {$task_id} from Worker {$from_id}\n"; echo "Data: {$data}\n"; for($i = 0 ; $i < 2 ; $i ++ ) { sleep(1); echo "Task {$task_id} Handle {$i} times...\n"; } $fd = json_decode($data, true); $serv->send($fd['fd'] , "Data in Task {$task_id}"); return "Task {$task_id}'s result"; } public function onFinish($serv,$task_id, $data) { echo "Task {$task_id} finish\n"; echo "Result: {$data}\n"; } }
调用php 命令运行某个start_server.php.里面代码
start_server.php
$server
=
new
server();
客户端代码(client.php)
class Client { private $client; public function __construct() { $this->client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); $this->client->on('Connect', array($this, 'onConnect')); $this->client->on('Receive', array($this, 'onReceive')); $this->client->on('Close', array($this, 'onClose')); $this->client->on('Error', array($this, 'onError')); } public function connect() { if(!$fp = $this->client->connect("127.0.0.1", 9501 , 1)) { echo "Error: {$fp->errMsg}[{$fp->errCode}]\n"; return; } } //connect之后,会调用onConnect方法 public function onConnect($cli) { fwrite(STDOUT, "Enter Msg:"); swoole_event_add(STDIN,function(){ fwrite(STDOUT, "Enter Msg:"); $msg = trim(fgets(STDIN)); $this->send($msg); }); } public function onClose($cli) { echo "Client close connection\n"; } public function onError() { } public function onReceive($cli, $data) { echo "Received: ".$data."\n"; } public function send($data) { $this->client->send($data); } public function isConnected($cli) { return $this->client->isConnected(); } }
同步调用:
$client = new Client(); $client->connect(); if ($result = $client->send($data)) { //默认请求,请求任务是,直接发送请求,无交互数据(从server获取到返回值) // print_r($client->receive()); echo 'success'; } else { echo 'fail'; } //获取又返回的swoole,请求 // if($data = $client->recv()){ // echo "获取到的返回值".$data; // } $client->close();
异步调用
$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); //设置事件回调函数 $client->on("connect", function($cli) { global $params; //$params = array(); //$params['service'] = 'Default.Index'; //$params['username'] = 'swoole'; $data = json_encode($params); echo "Send: " . $data . "\n";; $cli->send($data); }); $client->on("receive", function($cli, $data){ echo "Received: " . $data . "\n"; }); $client->on("error", function($cli){ echo "Connect failed\n"; }); $client->on("close", function($cli){ echo "Connection close\n"; }); //发起网络连接 $client->connect($ip, $port, 3);
- 其他写法
配置文件信息
/** * Swoole扩展类库 */ 'Swoole' => array( //服务 'server' => array( 'ip' => '127.0.0.1', 'port' => 9501, 'worker_num' => 1, ), //计划任务 'task' => array( 'ip' => '127.0.0.1', 'port' => 9502, 'worker_num' => 1, ), ),
分开swolle普通请求和,task模式
类源码:
class SwooleLite { /** * 任务列表 * @var */ protected $task_list; /** * 配置信息 * @var mixed */ protected $config; /** * * 开启调试模式 * @var bool */ protected $debug; /** * debug 暂时没用debug 后期可扩展为,开启记录日志 * Lite constructor. * @param bool $debug */ public function __construct($debug = FALSE) { $this->debug = $debug; $this->config = \PhalApi\DI()->config->get('app.Swoole.server'); } /** * 启动接口服务,支持长链接 */ public function runServer() { $oldErrorHandler = set_error_handler(array(__CLASS__, 'myErrorHandler')); $this->config = \PhalApi\DI()->config->get('app.Swoole.server'); $ip = isset($this->config['ip']) ? $this->config['ip'] : '127.0.0.1'; $port = isset($this->config['port']) ? $this->config['port'] : 9501; $workerNum = isset($this->config['worker_num']) ? $this->config['worker_num'] : 8; $serv = new \swoole_server($ip, $port); $serv->set(array( 'worker_num' => $workerNum, //工作进程数量 'daemonize' => true, //是否作为守护进程 )); $serv->on('connect', function ($serv, $fd) { // \PhalApi\DI()->logger->debug('client connect in swoole'); \PhalApi\DI()->logger->debug('client connect in swoole'); }); $serv->on('receive', function ($serv, $fd, $fromId, $data) { try { $params = json_decode($data, TRUE); if (!is_array($params)) { $params = array(); } \PhalApi\DI()->logger->debug('params in swoole', $params); $url = 's=App.Site.Index&username=dogstar'; $result = \PhalApi\DI()->request->requestApi($url); \PhalApi\DI()->logger->debug('result in swoole', $result); // \PhalApi\DI()->response->setData($result['data']); // \PhalApi\DI()->response->getResult(); // $res = \PhalApi\DI()->response->output(); // $result = \PhalApi\DI()->response->output(); // \PhalApi\DI()->logger->debug('output in swoole', $res); $serv->send($fd, json_encode($result)); } catch (Exception $ex) { echo $ex->getTraceAsString(); \PhalApi\DI()->logger->error('exception in swoole', $ex->getMessage()); //TODO 通知管理员 } if (\PhalApi\DI()->notorm) { \PhalApi\DI()->notorm->disconnect(); } $serv->close($fd); }); $serv->on('close', function ($serv, $fd) { \PhalApi\DI()->logger->debug('client close in swoole'); }); $serv->start(); } /** * 启动计划任务,支持异步处理 */ public function runTask() { $oldErrorHandler = set_error_handler(array(__CLASS__, 'myErrorHandler')); $this->config = \PhalApi\DI()->config->get('app.Swoole.task'); $ip = isset($this->config['ip']) ? $this->config['ip'] : '127.0.0.1'; $port = isset($this->config['port']) ? $this->config['port'] : 9502; $workerNum = isset($this->config['worker_num']) ? $this->config['worker_num'] : 4; $serv = new \swoole_server($ip, $port); $serv->set(array('task_worker_num' => $workerNum)); $serv->on('Receive', function ($serv, $fd, $from_Id, $data) { echo "Get Message From Client {$fd}:{$data}\n"; // send a task to task worker. $param = array( 'fd' => $fd ); // start a task $serv->task( json_encode( $param ) ); echo "Continue Handle Worker\n"; // $taskId = $serv->task($data); // $this->task_list[$taskId] = $fd; // \PhalApi\DI()->logger->debug("asynctask($taskId) dispath in swoole", print_r($data)); // echo "Continue Handle Worker\n"; }); $serv->on('Task', function ($serv, $task_id, $from_id, $data) { echo "This Task {$task_id} from Worker {$from_id}\n"; echo "Data: {$data}\n"; for($i = 0 ; $i < 10 ; $i ++ ) { sleep(1); echo "Taks {$task_id} Handle {$i} times...\n"; } $fd = json_decode( $data , true )['fd']; $serv->send( $fd , "Data in Task {$task_id}"); return "Task {$task_id}'s result"; // // \PhalApi\DI()->logger->debug("asynctask($taskId) start in swoole", print_r($data)); // // $params = json_decode($data, TRUE); // if (!is_array($params)) { // $params = array(); // } // // try { // // $params = json_decode($data, TRUE); // if (!is_array($params)) { // $params = array(); // } // \PhalApi\DI()->logger->debug('params in swoole', $params); // // $url = 's=App.Site.Index&username=dogstar'; // $result = \PhalApi\DI()->request->requestApi($url); // \PhalApi\DI()->logger->debug('result in swoole', $result); // $serv->finish(json_encode($result)); // // } catch (Exception $ex) { // echo $ex->getTraceAsString(); // \PhalApi\DI()->logger->error("asynctask($taskId) exception in swoole", $ex->getMessage()); // $serv->finish("Exception: " . $ex->getMessage()); // //TODO 通知管理员 // } // // if (\PhalApi\DI()->notorm) { // \PhalApi\DI()->notorm->disconnect(); // } }); $serv->on('Finish', function ($serv, $task_id, $data) { echo "Task {$task_id} finish\n"; echo "Result: {$data}\n"; \PhalApi\DI()->logger->debug("asynctask($task_id) finish in swoole", $data); $serv->close($this->task_list[$task_id]); }); $serv->start(); } /** * error handler function */ public static function myErrorHandler($errno, $errstr, $errfile, $errline) { if (!(error_reporting() & $errno)) { // This error code is not included in error_reporting return; } switch ($errno) { case E_USER_ERROR: case E_USER_WARNING: case E_USER_NOTICE: default: \PhalApi\DI()->logger->error('error in swoole', array('errno' => $errno, 'errstr' => $errstr, 'errline' => $errline, 'errfile' => $errfile)); break; } /* Don't execute PHP internal error handler */ return true; } }
(1)长链接入口
通过书写php 命令,调用run_server.php,文件内容为下面的
//$ vim ./Server/run_server.php $swooleLite = new Swoole_Lite(); $swooleLite->runServer();
启动、重启和关闭服务
启动可以用:
php ./Server/run_server.php
关闭可以用:
ps -ef | grep run_server | grep -v grep | awk '{print $2}'|xargs kill -9
(2)异步计划任务
在使用长链接入口对外提供接口服务后,由于不再是HTTP协议,所以入口建议放置在新的目录./Server,而不再是./Public。
入口文件的编写,如同我们以往一样,很简单:
//$ vim ./Server/run_server.php $swooleLite = new Swoole_Lite(); $swooleLite->runServer();
启动、重启和关闭服务
启动可以用:
nohup php ./Server/run_task.php > ./Server/run_task.log 2>&1 &
(3)客户端调用
测试脚本源码:check.php
if ($argc < 3) { echo "Usage: $argv[0] <ip> <port> [service] [params ...]\n\n"; echo "Example: $argv[0] 127.0.0.1 9501 Default.Index username=swoole\n\n"; exit(1); } $ip = trim($argv[1]); $port = intval($argv[2]); $service = isset($argv[3]) ? trim($argv[3]) : 'Default.Index'; $params = array('service' => $service); if (isset($argv[4])) { $moreParams = array_slice($argv, 4); foreach ($moreParams as $param) { list($key, $value) = explode('=', $param); $params[$key] = $value; } } $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); //设置事件回调函数 $client->on("connect", function($cli) { global $params; //$params = array(); //$params['service'] = 'Default.Index'; //$params['username'] = 'swoole'; $data = json_encode($params); echo "Send: " . $data . "\n";; $cli->send($data); }); $client->on("receive", function($cli, $data){ echo "Received: " . $data . "\n"; }); $client->on("error", function($cli){ echo "Connect failed\n"; }); $client->on("close", function($cli){ echo "Connection close\n"; }); //发起网络连接 $client->connect($ip, $port, 3);
在扩展类库里有一个测试的脚本,可以用来进行PHP客户端的请求。
(1)默认接口调用
$ php ./check.php 127.0.0.1 9501 Default.Index username=swoole
Send: {"service":"Default.Index","username":"swoole"}
Received: {"ret":200,"data":{"title":"Hello World!","content":"Hi swoole, welcome to use PhalApi!","version":"1.1.3","time":1430620911},"msg":""}
Connection close
(2)带数据库的调用
$ php ./check.php 127.0.0.1 9501 User.getBaseInfo userId=1
Send: {"service":"User.getBaseInfo","userId":"1"}
Received: {"ret":200,"data":{"code":0,"msg":"","info":{"id":"1","username":"aevit","nickname":"test","password":"DE4CA99150F44B26F0D320DCA6E4B7629C43B6","salt":"wefewfew","reg_time":"0","avatar":"http:\/\/image.famillelab.com\/no_avatar.png","UUID":""}},"msg":""}
Connection close
(3)异步计划任务的调度
$ php ./check.php 127.0.0.1 9502 Default.Index username=swoole
Send: {"service":"Default.Index","username":"swoole"}
Connection close
假设Default.Index为一个计划任务的接口,但目前发现一个问题是,首次请求异步计划任务不会主动结束,而需要工作强制ctrl + c结束,再请求,则正常。
对应的log,可以看到:
2015-05-03 12:24:57|DEBUG|asynctask(0) dispath in swoole|{"service":"Default.Index","username":"swoole"}
2015-05-03 12:24:57|DEBUG|asynctask(0) start in swoole|{"service":"Default.Index","username":"swoole"}
2015-05-03 12:24:57|DEBUG|asynctask(0) finish in swoole|{"ret":200,"data":{"title":"Hello World!","content":"Hi swoole, welcome to use PhalApi!","version":"1.1.3","time":1430627097},"msg":""}
2015-05-03 12:24:57|DEBUG|asynctask(1) dispath in swoole|{"service":"Default.Index","username":"swoole"}
2015-05-03 12:24:57|DEBUG|asynctask(1) start in swoole|{"service":"Default.Index","username":"swoole"}
2015-05-03 12:24:57|DEBUG|asynctask(1) finish in swoole|{"ret":200,"data":{"title":"Hello World!","content":"Hi swoole, welcome to use PhalApi!","version":"1.1.3","time":1430627097},"msg":""}
...
附赠:swoole 重启脚本
linux shell 写swoole重启脚本 代码如下
#!/bin/sh kill `lsof -t -i:9501` sleep 2 php /data/web/mircoweb/wwwroot/Public/swoole.php sleep 1 netstat -ntlp
把以上代码,写入shell脚本,比如 swoole_reload.sh,
然后执行 sh swoole.reload.sh