ThinkPHP即將迎來最新版本6.0,針對目前愈來愈流行Swoole,thinkphp也推出了最新的擴展think-swoole 3.0
tp-swoole3.0不一樣於2.0版本,採用了全新的架構。(以下圖目錄結構)php
tp主要針對的是很是駐內存方式運行,爲了兼容swoole,雖然作了不少優化,可是仍然沒法像swoft,sd等一些針對swoole開發的框架同樣。這裏所說的不一樣,不是指tp很差,而是由於兩種模式都要兼容,不得不作出一些取捨。web
分析該框架的運行機制,其實主要分析swoole的OnRequest函數便可,路由分發,數據處理等都是在函數處進行處理的。thinkphp
Swoole.php安全
public function onRequest($req, $res) { $this->app->event->trigger('swoole.request'); $this->resetOnRequest(); /** @var Sandbox $sandbox */ $sandbox = $this->app->make(Sandbox::class); $request = $this->prepareRequest($req); try { $sandbox->setRequest($request); $sandbox->init(); $response = $sandbox->run($request); $this->sendResponse($sandbox, $response, $res); } catch (Throwable $e) { try { $exceptionResponse = $this->app ->make(Handle::class) ->render($request, $e); $this->sendResponse($sandbox, $exceptionResponse, $res); } catch (Throwable $e) { $this->logServerError($e); } } finally { $sandbox->clear(); } }
函數初始處,觸發了一個request事件,這裏方便用戶自定義處理請求,進行一些定製化處理websocket
$this->app->event->trigger('swoole.request');
重置請求,當是Websocket的時候,重置該類,具體爲何,下次咱們分析Websocket的時候在進行解釋swoole
$this->resetOnRequest(); protected function resetOnRequest() { // Reset websocket data if ($this->isServerWebsocket) { $this->app->make(Websocket::class)->reset(true); } }
接下來經過容器獲取沙盒,這裏也是關鍵之處。在很是住內存框架中,爲了方便會有一些寫法致使在常駐內存方式下不容易被釋放內存,小則內存泄漏,大則數據錯亂。而沙盒能夠很好的解決這個問題。(文章最後會介紹一個形成內存泄漏和數據錯亂的案例)cookie
$sandbox = $this->app->make(Sandbox::class);
請求進行預處理,這裏進行的是request的轉換,從swoole的request轉換到tp的request架構
$request = $this->prepareRequest($req); $header = $req->header ?: []; $server = $req->server ?: []; if (isset($header['x-requested-with'])) { $server['HTTP_X_REQUESTED_WITH'] = $header['x-requested-with']; } if (isset($header['referer'])) { $server['http_referer'] = $header['referer']; } if (isset($header['host'])) { $server['http_host'] = $header['host']; } // 從新實例化請求對象 處理swoole請求數據 /** @var \think\Request $request */ $request = $this->app->make('request', [], true); return $request->withHeader($header) ->withServer($server) ->withGet($req->get ?: []) ->withPost($req->post ?: []) ->withCookie($req->cookie ?: []) ->withInput($req->rawContent()) ->withFiles($req->files ?: []) ->setBaseUrl($req->server['request_uri']) ->setUrl($req->server['request_uri'] . (!empty($req->server['query_string']) ? '&' . $req->server['query_string'] : '')) ->setPathinfo(ltrim($req->server['path_info'], '/'));
對沙盒進行設置,並初始化沙盒併發
$sandbox->setRequest($request); $sandbox->init();
啓動沙盒app
$response = $sandbox->run($request);
若是發生異常,則將異常信息處理併發送
try { $exceptionResponse = $this->app ->make(Handle::class) ->render($request, $e); $this->sendResponse($sandbox, $exceptionResponse, $res); } catch (Throwable $e) { $this->logServerError($e); }
最終須要將沙盒信息清除
$sandbox->clear();
以上是tp-swoole對HTTP的處理流程,下文會詳細介紹沙盒的運行機制
class A{ private static $intance=null; public static function getInstance(){ if (!empty(self::$intance)){ return self::$intance; } self::$intance = new static(); return self::$intance; } public static function clear(){ self::$intance=null; } public function echo(){ echo "echo"; } } $b = A::getInstance(); A::clear(); print_r($b->echo());
以上代碼會報錯嗎?
不會。仍然會輸出echo
下面在作另一個實驗
class A { private static $intance = null; private $echo = 'echo'; public static function getInstance() { if (!empty(self::$intance)) { return self::$intance; } self::$intance = new static(); return self::$intance; } public static function clear() { self::$intance = null; } public function echo() { echo $this->echo; } public function setEcho($echo) { $this->echo = $echo; } } $b = A::getInstance(); $a = A::getInstance(); $a->setEcho("b"); print_r($b->echo()); A::clear(); print_r($b->echo()); $a->setEcho("a"); print_r($b->echo());
以上代碼會輸出什麼?
答案是:bba。那麼爲何不只沒有報錯,還輸出這樣的答案
PHP的變量對象引入是地址引用,當$a和$b被賦值時,他們所存的內容都是同樣的,且只有一份都是self::$intance位置所存放的內容。修改$a或$b都會修改self::$intance,那麼爲什麼當self::$intance爲清除後,$a和$b仍然正常?基於PHP寫時複製,當self::$intance被清空,就會複製出來一份給$a和$b來使用。
當咱們在很是住內存方式開發時,這些都不須要注意,由於每次請求都至關於一個單獨的線程,初始化全部數據,最後在將全部數據銷燬,且全部數據都是按照順序執行的。長住內存方式,就須要注意這些問題,否則會出現相似線程安全的問題。至於爲什麼會出現這樣的問題,下文再敘。