ThinkPHP即将迎来最新版本6.0,针对目前越来越流行Swoole,thinkphp也推出了最新的扩展think-swoole 3.0
本文主要介绍在ThinkPHP-swoole 3.0当中所用到的沙盒技术。
沙盒--顾名思义,所有程序都运行在一个封闭容器当中,得益于更完善的容器技术,在3.0扩展当中沙盒得以大展身手。
首先,查看沙盒是如何使用的,查看扩展当中Swoole.php,其中的OnRequest函数
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是如何起到沙盒的作用的呢?
//$sandbox->setRequest($request); public function setRequest(Request $request) { Context::setData('_request', $request); return $this; }
上述代码将请求注入到了沙盒内,这里又多出一个Context,那么这个类又是做什么的呢?为何不在沙盒内整个属性来存储呢?这个我们文末在做介绍,我介绍沙盒。
//$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,复制。这里将容器对象进行了复制,也就是原容器有的对象,这个新容器也有。也就是说每次请求都会创建一个新的环境用于执行和解析,由于容器的隔离性,每个请求都不会和其他请求进行干扰。
至于下面这段代码,看到这里,我觉得您也已经明白了。
$this->resetApp($app);
沙盒当中为何会需要Context类呢?看到这个类以后就会明白了,static::getCoroutineId()是获取当前的协程ID,每一个协程都会有一个独一无二的ID,这样通过Context来存一些特殊数据或者对象就不会造成数据混乱。因为只有当前协程才可以读取到该数据。
<?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(); } }