think-swoole 3.0入門教程(thinkphp 6.0)架構分析 1

前言

ThinkPHP即將迎來最新版本6.0,針對目前愈來愈流行Swoole,thinkphp也推出了最新的擴展think-swoole 3.0

架構分析

tp-swoole3.0不一樣於2.0版本,採用了全新的架構。(以下圖目錄結構)php

clipboard.png

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來使用。

當咱們在很是住內存方式開發時,這些都不須要注意,由於每次請求都至關於一個單獨的線程,初始化全部數據,最後在將全部數據銷燬,且全部數據都是按照順序執行的。長住內存方式,就須要注意這些問題,否則會出現相似線程安全的問題。至於爲什麼會出現這樣的問題,下文再敘。

相關文章
相關標籤/搜索