進程就是
正在運行的程序
的
一個實例
$process = new swoole_process(function(swoole_process $pro) { // todo // php redis.php $pro->exec("/usr/local/php/bin/php", [__DIR__.'/../server/http_server.php']); }, false); $pid = $process->start(); echo $pid . PHP_EOL; //回收結束運行的子進程 swoole_process::wait();
以樹狀圖顯示進程間的關係:pstree -p 進程id
啓動成功後會建立worker_num+2
個進程。Master
進程+Manager
進程+serv
->worker_num
個Worker
進程php
管道:進程和進程間的一個橋樑
echo "process-start-time:".date("Ymd H:i:s"); $workers = []; $urls = [ 'http://baidu.com', 'http://sina.com.cn', 'http://qq.com', 'http://baidu.com?search=singwa', 'http://baidu.com?search=singwa2', 'http://baidu.com?search=imooc', ]; //建立多個子進程分別模擬請求URL的內容 for($i = 0; $i < 6; $i++) { $process = new swoole_process(function(swoole_process $worker) use($i, $urls) { // curl $content = curlData($urls[$i]); //將內容寫入管道 // echo $content.PHP_EOL; $worker->write($content.PHP_EOL); }, true); $pid = $process->start(); $workers[$pid] = $process; } //獲取管道內容 foreach($workers as $process) { echo $process->read(); } /** * 模擬請求URL的內容 1s * @param $url * @return string */ function curlData($url) { // curl file_get_contents sleep(1); return $url . "success".PHP_EOL; } echo "process-end-time:".date("Ymd H:i:s");
內存操做模塊之:
Table
swoole_table
一個基於共享內存和鎖實現的超高性能,併發數據結構
使用場景:用於解決多進程/多線程
數據共享和同步加鎖問題
進程結束後內存表會自動釋放html
// 建立內存表 $table = new swoole_table(1024); // 內存表增長一列 $table->column('id', $table::TYPE_INT, 4); $table->column('name', $table::TYPE_STRING, 64); $table->column('age', $table::TYPE_INT, 3); $table->create(); $table->set('singwa_imooc', ['id' => 1, 'name'=> 'singwa', 'age' => 30]); // 另一種方案 $table['singwa_imooc_2'] = [ 'id' => 2, 'name' => 'singwa2', 'age' => 31, ]; $table->decr('singwa_imooc_2', 'age', 2); print_r($table['singwa_imooc_2']); echo "delete start:".PHP_EOL; $table->del('singwa_imooc_2'); print_r($table['singwa_imooc_2']);
線程、進程、協程的區別
進程,線程,協程與並行,併發
併發與並行的區別?mysql
$http = new swoole_http_server('0.0.0.0', 9001); $http->on('request', function($request, $response) { // 獲取redis 裏面 的key的內容, 而後輸出瀏覽器 $redis = new Swoole\Coroutine\Redis(); $redis->connect('127.0.0.1', 6379); $value = $redis->get($request->get['a']); // mysql..... //執行時間取它們中最大的:time = max(redis,mysql) $response->header("Content-Type", "text/plain"); $response->end($value); }); $http->start();
重難點
)$http = new swoole_http_server("0.0.0.0", 9911); $http->set( [ 'enable_static_handler' => true, 'document_root' => "/home/wwwroot/swoole/thinkphp/public/static", 'worker_num' => 5, ] ); //此事件在Worker進程/Task進程啓動時發生,這裏建立的對象能夠在進程生命週期內使用 $http->on('WorkerStart', function(swoole_server $server, $worker_id) { // 定義應用目錄 define('APP_PATH', __DIR__ . '/../../../../application/'); // 加載框架裏面的文件 require __DIR__ . '/../../../../thinkphp/base.php'; }); $http->on('request', function($request, $response) use($http){ //若是在每次請求時加載框架文件,則不用修改thinkphp5源碼 // // 定義應用目錄 // define('APP_PATH', __DIR__ . '/../../../../application/'); // // 加載框架裏面的文件 // require_once __DIR__ . '/../../../../thinkphp/base.php'; /** * 解決上一次輸入的變量還存在的問題 * 方案一:if(!empty($_GET)) {unset($_GET);} * 方案二:$http-close();把以前的進程kill,swoole會從新啓一個進程,重啓會釋放內存,把上一次的資源包括變量等所有清空 * 方案三:$_SERVER = [] */ $_SERVER = []; if(isset($request->server)) { foreach($request->server as $k => $v) { $_SERVER[strtoupper($k)] = $v; } } if(isset($request->header)) { foreach($request->header as $k => $v) { $_SERVER[strtoupper($k)] = $v; } } $_GET = []; if(isset($request->get)) { foreach($request->get as $k => $v) { $_GET[$k] = $v; } } $_POST = []; if(isset($request->post)) { foreach($request->post as $k => $v) { $_POST[$k] = $v; } } //開啓緩衝區 ob_start(); // 執行應用並響應 try { think\Container::get('app', [APP_PATH]) ->run() ->send(); }catch (\Exception $e) { // todo } //輸出TP當前請求的控制方法 //echo "-action-".request()->action().PHP_EOL; //獲取緩衝區內容 $res = ob_get_contents(); ob_end_clean(); $response->end($res); //把以前的進程kill,swoole會從新啓一個進程,重啓會釋放內存,把上一次的資源包括變量等所有清空 //$http->close(); }); $http->start();
測試:redis
//此事件在Worker進程/Task進程啓動時發生,這裏建立的對象能夠在進程生命週期內使用 $http->on('WorkerStart', function(swoole_server $server, $worker_id) { // 定義應用目錄 define('APP_PATH', __DIR__ . '/../../../../application/'); // 加載框架裏面的文件 require __DIR__ . '/../../../../thinkphp/base.php'; });
Tips:若是修改了加載框架文件,須要重啓:
php php_server.php
onWorkerStart:sql
此事件在Worker
進程/Task
進程啓動時發生,這裏建立的對象能夠在進程生命週期內使用
在onWorkerStart
中加載框架的核心文件後:thinkphp
當前worker
進程沒有結束,因此會保存上一次的資源等。解決上一次輸入的變量還存在的問題:segmentfault
if(!empty($_SERVER)) { unset($_SERVER); }
$http-close();
把以前的進程kill
,swoole
會從新啓一個進程,重啓會釋放內存,把上一次的資源包括變量等所有清空(php-cli
控制檯會提示錯誤)$_SERVER = []
(推薦方案)當第一次請求後下一次再請求不一樣的模塊或者方法不生效,都是‘第一次’請求 模塊/控制器/方法。以下圖:
修改ThinkPHP5
框架Request.php
源碼位置:/thinkphp/library/think/Request.php
修改以下:瀏覽器
function path() { }
$this->path
function pathinfo() { }
$this->pathinfo
pathinfo
路由,添加以下代碼在function pathinfo() { }
中if (isset($_SERVER['PATH_INFO']) && $_SERVER['PATH_INFO'] != '/') { return ltrim($_SERVER['PATH_INFO'], '/'); }
修改後完整Request.php
文件:緩存
/** * 獲取當前請求URL的pathinfo信息(含URL後綴) * @access public * @return string */ public function pathinfo() { if (isset($_SERVER['PATH_INFO']) && $_SERVER['PATH_INFO'] != '/') { return ltrim($_SERVER['PATH_INFO'], '/'); } // if (is_null($this->pathinfo)) { if (isset($_GET[$this->config->get('var_pathinfo')])) { // 判斷URL裏面是否有兼容模式參數 $_SERVER['PATH_INFO'] = $_GET[$this->config->get('var_pathinfo')]; unset($_GET[$this->config->get('var_pathinfo')]); } elseif ($this->isCli()) { // CLI模式下 index.php module/controller/action/params/... $_SERVER['PATH_INFO'] = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : ''; } // 分析PATHINFO信息 if (!isset($_SERVER['PATH_INFO'])) { foreach ($this->config->get('pathinfo_fetch') as $type) { if (!empty($_SERVER[$type])) { $_SERVER['PATH_INFO'] = (0 === strpos($_SERVER[$type], $_SERVER['SCRIPT_NAME'])) ? substr($_SERVER[$type], strlen($_SERVER['SCRIPT_NAME'])) : $_SERVER[$type]; break; } } } $this->pathinfo = empty($_SERVER['PATH_INFO']) ? '/' : ltrim($_SERVER['PATH_INFO'], '/'); // } return $this->pathinfo; } /** * 獲取當前請求URL的pathinfo信息(不含URL後綴) * @access public * @return string */ public function path() { //註銷判斷,再也不復用類成員變量$this->path // if (is_null($this->path)) { $suffix = $this->config->get('url_html_suffix'); $pathinfo = $this->pathinfo(); if (false === $suffix) { // 禁止僞靜態訪問 $this->path = $pathinfo; } elseif ($suffix) { // 去除正常的URL後綴 $this->path = preg_replace('/\.(' . ltrim($suffix, '.') . ')$/i', '', $pathinfo); } else { // 容許任何後綴訪問 $this->path = preg_replace('/\.' . $this->ext() . '$/i', '', $pathinfo); } // } return $this->path; }
class Http { CONST HOST = "0.0.0.0"; CONST PORT = 9911; public $http = null; public function __construct() { $this->http = new swoole_http_server(self::HOST, self::PORT); $this->http->set( [ 'enable_static_handler' => true, 'document_root' => "/home/wwwroot/swoole/thinkphp/public/static", 'worker_num' => 4, ] ); $this->http->on("workerstart", [$this, 'onWorkerStart']); $this->http->on("request", [$this, 'onRequest']); $this->http->on("close", [$this, 'onClose']); $this->http->start(); } /** * 此事件在Worker進程/Task進程啓動時發生,這裏建立的對象能夠在進程生命週期內使用 * 在onWorkerStart中加載框架的核心文件後 * 1.不用每次請求都加載框架核心文件,提升性能 * 2.能夠在後續的回調中繼續使用框架的核心文件或者類庫 * * @param $server * @param $worker_id */ public function onWorkerStart($server, $worker_id) { // 定義應用目錄 define('APP_PATH', __DIR__ . '/../../../../application/'); // 加載框架裏面的文件 require __DIR__ . '/../../../../thinkphp/base.php'; } /** * request回調 * 解決上一次輸入的變量還存在的問題例:$_SERVER = [] * @param $request * @param $response */ public function onRequest($request, $response) { $_SERVER = []; if(isset($request->server)) { foreach($request->server as $k => $v) { $_SERVER[strtoupper($k)] = $v; } } if(isset($request->header)) { foreach($request->header as $k => $v) { $_SERVER[strtoupper($k)] = $v; } } $_GET = []; if(isset($request->get)) { foreach($request->get as $k => $v) { $_GET[$k] = $v; } } $_POST = []; if(isset($request->post)) { foreach($request->post as $k => $v) { $_POST[$k] = $v; } } $_POST['http_server'] = $this->http; ob_start(); // 執行應用並響應 try { think\Container::get('app', [APP_PATH]) ->run() ->send(); }catch (\Exception $e) { // todo } $res = ob_get_contents(); ob_end_clean(); $response->end($res); } /** * close * @param $ws * @param $fd */ public function onClose($ws, $fd) { echo "clientid:{$fd}\n"; } } new Http();
示例演示:發送驗證碼性能優化
一、優化,將對接第三方的接口放入異步任務中
$_POST['http_server']->task($taskData);
/** * 發送驗證碼 */ public function index() { $phoneNum = intval($_GET['phone_num']);// tp input if(empty($phoneNum)) { return Util::show(config('code.error'), 'error'); } // 生成一個隨機數 $code = rand(1000, 9999); $taskData = [ 'method' => 'sendSms', 'data' => [ 'phone' => $phoneNum, 'code' => $code, ] ]; //優化,將對接第三方的接口放入異步任務中 $_POST['http_server']->task($taskData); return Util::show(config('code.success'), 'ok'); } }
二、將http
對象放入預約義$_POST
中,傳給調用者
$_POST['http_server'] = $this->http;
/** * request回調 */ public function onRequest($request, $response) { ...... //將http對象放入預約義$_POST中,傳給調用者 $_POST['http_server'] = $this->http; ob_start(); // 執行應用並響應 try { think\Container::get('app', [APP_PATH]) ->run() ->send(); }catch (\Exception $e) { // todo } ...... }
三、Task任務分發
/** * Task任務分發 */ public function onTask($serv, $taskId, $workerId, $data) { // 分發 task 任務機制,讓不一樣的任務 走不一樣的邏輯 $obj = new app\common\lib\task\Task; $method = $data['method']; $flag = $obj->$method($data['data']); return $flag; // 告訴worker }
四、表明的是swoole
裏面後續全部task
異步任務都放這裏來
class Task { /** * 異步發送 驗證碼 */ public function sendSms($data, $serv) { try { $response = Sms::sendSms($data['phone'], $data['code']); }catch (\Exception $e) { return false; } // 若是發送成功 把驗證碼記錄到redis裏面 if($response->Code === "OK") { Predis::getInstance()->set(Redis::smsKey($data['phone']), $data['code'], config('redis.out_time')); }else { return false; } return true; } }