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

前言

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

沙盒

本文主要介紹在ThinkPHP-swoole 3.0當中所用到的沙盒技術。
沙盒--顧名思義,全部程序都運行在一個封閉容器當中,得益於更完善的容器技術,在3.0擴展當中沙盒得以大展身手。php

首先,查看沙盒是如何使用的,查看擴展當中Swoole.php,其中的OnRequest函數thinkphp

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();
        }
    }

代碼中,從容器中取出沙盒,而後將請求注入到沙盒,並在沙盒中計算並返回結果。最終對沙盒進行清除,那麼Sandbox是如何起到沙盒的做用的呢?swoole

//$sandbox->setRequest($request);

    public function setRequest(Request $request)
    {
        Context::setData('_request', $request);
        
        return $this;
    }

上述代碼將請求注入到了沙盒內,這裏又多出一個Context,那麼這個類又是作什麼的呢?爲什麼不在沙盒內整個屬性來存儲呢?這個咱們文末在作介紹,我介紹沙盒。app

//$sandbox->init();
     public function init()
    {
        if (!$this->config instanceof Config) {
            throw new RuntimeException('Please initialize after setting base app.');
        }
        
        $this->setInstance($app = $this->getApplication());
        $this->resetApp($app);
    }

最主要的環節也就是這裏了,看到這裏就明白沙盒爲什麼稱之爲沙盒了。因爲tp6是基於容器建立和銷燬資源的,那麼各個容器之間是相對隔離的。下面接着看代碼函數

//$this->setInstance($app = $this->getApplication());
 public function getApplication()
    {
        $snapshot = $this->getSnapshot();
        if ($snapshot instanceof Container) {
            return $snapshot;
        }
        
        $snapshot = clone $this->getBaseApp();
        $this->setSnapshot($snapshot);
        
        return $snapshot;
    }

看到什麼了嗎?clone,複製。這裏將容器對象進行了複製,也就是原容器有的對象,這個新容器也有。也就是說每次請求都會建立一個新的環境用於執行和解析,因爲容器的隔離性,每一個請求都不會和其餘請求進行干擾。
至於下面這段代碼,看到這裏,我以爲您也已經明白了。ui

$this->resetApp($app);

最後,$sandbox->clear(),清空Context類中保存的當前協程的數據,並將當前沙盒的容器初始化this

public function clear()
    {
        Context::clear();
        $this->setInstance($this->getBaseApp());
    }

番外篇

沙盒當中爲什麼會須要Context類呢?看到這個類之後就會明白了,static::getCoroutineId()是獲取當前的協程ID,每個協程都會有一個獨一無二的ID,這樣經過Context來存一些特殊數據或者對象就不會形成數據混亂。由於只有當前協程才能夠讀取到該數據。spa

<?php

namespace think\swoole\coroutine;

use Swoole\Coroutine;
use think\Container;

class Context
{
    /**
     * The app containers in different coroutine environment.
     *
     * @var array
     */
    protected static $apps = [];

    /**
     * The data in different coroutine environment.
     *
     * @var array
     */
    protected static $data = [];

    /**
     * Get app container by current coroutine id.
     */
    public static function getApp()
    {
        return static::$apps[static::getCoroutineId()] ?? null;
    }

    /**
     * Set app container by current coroutine id.
     *
     * @param Container $app
     */
    public static function setApp(Container $app)
    {
        static::$apps[static::getCoroutineId()] = $app;
    }

    /**
     * Get data by current coroutine id.
     *
     * @param string $key
     *
     * @return mixed|null
     */
    public static function getData(string $key)
    {
        return static::$data[static::getCoroutineId()][$key] ?? null;
    }

    /**
     * Set data by current coroutine id.
     *
     * @param string $key
     * @param        $value
     */
    public static function setData(string $key, $value)
    {
        static::$data[static::getCoroutineId()][$key] = $value;
    }

    /**
     * Remove data by current coroutine id.
     *
     * @param string $key
     */
    public static function removeData(string $key)
    {
        unset(static::$data[static::getCoroutineId()][$key]);
    }

    /**
     * Get data keys by current coroutine id.
     */
    public static function getDataKeys()
    {
        return array_keys(static::$data[static::getCoroutineId()] ?? []);
    }

    /**
     * Clear data by current coroutine id.
     */
    public static function clear()
    {
        unset(static::$apps[static::getCoroutineId()]);
        unset(static::$data[static::getCoroutineId()]);
    }

    /**
     * Get current coroutine id.
     */
    public static function getCoroutineId()
    {
        return Coroutine::getuid();
    }
}
相關文章
相關標籤/搜索