爲何說有訪問安全問題呢?傳統地,在php的的環境中,不多有Phper遇到所謂變量安全訪問問題。舉個例子,代碼大約以下:php
class db { protected static $instance; protected $dbCon; function __construct() { /* * 咱們這裏用stdclass來模擬一個數據庫鏈接 */ $this->dbCon = new \stdClass(); } public static function getInstance() { if(!isset(self::$instance)){ self::$instance = new db(); } return self::$instance; } function dbCon() { return $this->dbCon; } } $con = db::getInstance()->dbCon(); $con->key = 'new'; var_dump($con->key);
這個是在fpm模式下,很常見的數據庫鏈接單例模式的使用。乍一看沒有問題,但實際上,在協程環境下,會出現鏈接跨協程使用問題,舉例以下git
go(function (){ go(function (){ db::getInstance()->dbCon()->key = 'one'; //假設這sql執行了1s \co::sleep(1); var_dump(db::getInstance()->dbCon()->key); }); go(function (){ db::getInstance()->dbCon()->key = 'two'; //假設這sql執行了0.1s \co::sleep(0.1); var_dump(db::getInstance()->dbCon()->key); }); });
咱們會發現,以上代碼當中,協程2的數據污染到了協程1的數據,那麼所以這樣確定是不行的。github
爲了解決這個問題,咱們引入協程上下文管理這樣的概念,由此來實現每一個協程環境內的數據隔離。sql
class dbContext { private $container = []; private static $instance; public static function getInstance() { if(!isset(self::$instance)){ self::$instance = new dbContext(); } return self::$instance; } function dbCon() { $cid = \co::getCid(); if(!isset($this->container[$cid])){ $this->container[$cid] = new stdClass(); defer(function (){ $this->destroy(); }); } return $this->container[$cid]; } function destroy() { $cid = \co::getCid(); if(!isset($this->container[$cid])){ unset($this->container[$cid]); } } } go(function (){ go(function (){ dbContext::getInstance()->dbCon()->key = 'one'; //假設這sql執行了1s \co::sleep(1); var_dump(dbContext::getInstance()->dbCon()->key); }); go(function (){ dbContext::getInstance()->dbCon()->key = 'two'; //假設這sql執行了0.1s \co::sleep(0.1); var_dump(dbContext::getInstance()->dbCon()->key); }); });
以上代碼中,咱們用每一個協程的id,來做爲每一個協程棧的數據token,用了defer方法,實現了每一個協程退出的時候的數據自動清理,從而避免了內存泄露。數據庫
咱們不難發現,以上代碼中,實際上依舊是短鏈接的管理方式,沒辦法對連接進行復用,因爲本文章僅作基礎原理講解之用,具體有興趣的同窗,能夠查看下Easyswoole這個框架的鏈接池和協程上下文管理器,項目主頁在 www.easyswoole.com ,若以爲喜歡,有幫助,能夠給easyswoole的github倉庫點個贊。安全