摘要:訪問(wèn)安全問(wèn)題為什么說(shuō)有訪問(wèn)安全問(wèn)題呢傳統(tǒng)地,在的的環(huán)境中,很少有遇到所謂變量安全訪問(wèn)問(wèn)題。上下文管理器為了解決這個(gè)問(wèn)題,我們引入?yún)f(xié)程上下文管理這樣的概念,由此來(lái)實(shí)現(xiàn)每個(gè)協(xié)程環(huán)境內(nèi)的數(shù)據(jù)隔離。
訪問(wèn)安全問(wèn)題
為什么說(shuō)有訪問(wèn)安全問(wèn)題呢?傳統(tǒng)地,在php的的環(huán)境中,很少有Phper遇到所謂變量安全訪問(wèn)問(wèn)題。舉個(gè)例子,代碼大約如下:
class db { protected static $instance; protected $dbCon; function __construct() { /* * 我們這里用stdclass來(lái)模擬一個(gè)數(shù)據(jù)庫(kù)連接 */ $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);
這個(gè)是在fpm模式下,很常見(jiàn)的數(shù)據(jù)庫(kù)連接單例模式的使用。乍一看沒(méi)有問(wèn)題,但實(shí)際上,在協(xié)程環(huán)境下,會(huì)出現(xiàn)連接跨協(xié)程使用問(wèn)題,舉例如下
go(function (){ go(function (){ db::getInstance()->dbCon()->key = "one"; //假設(shè)這sql執(zhí)行了1s co::sleep(1); var_dump(db::getInstance()->dbCon()->key); }); go(function (){ db::getInstance()->dbCon()->key = "two"; //假設(shè)這sql執(zhí)行了0.1s co::sleep(0.1); var_dump(db::getInstance()->dbCon()->key); }); });
我們會(huì)發(fā)現(xiàn),以上代碼當(dāng)中,協(xié)程2的數(shù)據(jù)污染到了協(xié)程1的數(shù)據(jù),那么因此這樣肯定是不行的。
上下文管理器為了解決這個(gè)問(wèn)題,我們引入?yún)f(xié)程上下文管理這樣的概念,由此來(lái)實(shí)現(xiàn)每個(gè)協(xié)程環(huán)境內(nèi)的數(shù)據(jù)隔離。
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"; //假設(shè)這sql執(zhí)行了1s co::sleep(1); var_dump(dbContext::getInstance()->dbCon()->key); }); go(function (){ dbContext::getInstance()->dbCon()->key = "two"; //假設(shè)這sql執(zhí)行了0.1s co::sleep(0.1); var_dump(dbContext::getInstance()->dbCon()->key); }); });
以上代碼中,我們用每個(gè)協(xié)程的id,來(lái)作為每個(gè)協(xié)程棧的數(shù)據(jù)token,用了defer方法,實(shí)現(xiàn)了每個(gè)協(xié)程退出的時(shí)候的數(shù)據(jù)自動(dòng)清理,從而避免了內(nèi)存泄露。
通用版本的連接池與協(xié)程上下文管理我們不難發(fā)現(xiàn),以上代碼中,實(shí)際上依舊是短連接的管理方式,沒(méi)辦法對(duì)鏈接進(jìn)行復(fù)用,由于本文章僅做基礎(chǔ)原理講解之用,具體有興趣的同學(xué),可以查看下Easyswoole這個(gè)框架的連接池和協(xié)程上下文管理器,項(xiàng)目主頁(yè)在 www.easyswoole.com ,若覺(jué)得喜歡,有幫助,可以給easyswoole的github倉(cāng)庫(kù)點(diǎn)個(gè)贊。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/31790.html