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