Swoole 中使用 Context 類管理上下文,防止發生數據錯亂


前面的文章中,我們說過:不能使用類靜態變量 Class::$array / 全局變量 global $_array / 全局對象屬性 $object->array / 其他超全局變量 $GLOBALS 等保存協程上下文內容,以免發生數據錯亂。

那是因為Swoole是常駐內存的,這些全局變量是共享的,在遇到並發請求時,協程A寫入的內容可能會因為協程掛起或協程調度被協程B並發修改了,會導致上下文內容不一致。

解決辦法是加入一個基於協程 ID 來保存上下文的 Context 類,來隔離不同協程之間的上下文/全局變量,然后在協程退出時清理上下文內容。

use Swoole\Coroutine;

class Context
{
    protected static $pool = [];

    // 基於協程 `ID` 獲取數據
    static function get($key)
    {
        $cid = Coroutine::getCid();
        if ($cid < 0)
        {
            return null;
        }
        if(isset(self::$pool[$cid][$key])){
            return self::$pool[$cid][$key];
        }
        return null;
    }

    // 基於協程 `ID` 寫入數據
    static function put($key, $item)
    {
        $cid = Coroutine::getCid();
        if ($cid > 0)
        {
            self::$pool[$cid][$key] = $item;
        }

    }

    // 基於協程 `ID` 刪除數據
    static function delete($key = null)
    {
        $cid = Coroutine::getCid();
        if ($cid > 0)
        {
            if($key){
                unset(self::$pool[$cid][$key]);
            }else{
                unset(self::$pool[$cid]);
            }
        }
    }
}

使用示例:

$server = new Swoole\Http\Server('127.0.0.1', 9501);

$server->on('request', function ($request, $response) {
    if ($request->server['request_uri'] == '/a') {
        Context::put('name', 'a');
        co::sleep(1.0);
        echo Context::get('name');
        $response->end(Context::get('name'));
        //退出協程時清理
        Context::delete('name');
    } else {
        Context::put('name', 'b');
        $response->end();
        //退出協程時清理
        Context::delete();
    }
});
$server->start();

模擬並發測試:

curl http://127.0.0.1:9501/a
curl http://127.0.0.1:9501/b


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM