hyperf 协程数据混淆的思考和分析

"博客探讨了在 Hyperf 框架中,如何处理协程上下文对不同请求的影响,尤其是当使用注入类时导致的数据共享问题。文章介绍了两种解决方案:一是利用 `HyperfUtilsContext` 进行协程上下文管理,二是结合魔术方法 `__get` 和 `__set` 实现数据隔离。这两种方法确保了并发场景下数据的正确性和独立性。"
摘要由CSDN通过智能技术生成
<?php
namespace App\Controller;

use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\HttpServer\Contract\RequestInterface;

/**
 * @AutoController()
 */

 class CoController{

    private $foo = 1;
    
    public function get(){
        return $this->foo;
    }
    public function update(RequestInterface $request){
        $foo = $request -> input('foo');
        $this->foo = $foo;
        return $this->foo;
    }
 }

get 返回的是1 这一步是对的
在这里插入图片描述
第二步 更新值返回更新值是2 也是对的
在这里插入图片描述
第三步 设想是返回1 但是返回的是2
单例对不同请求的影响 怎么处理这种情况往下看
在这里插入图片描述

处理过程
处理方法一:协程上下文
由于同一个进程内协程间是内存共享的,但协程的执行/切换是非顺序的,也就意味着我们很难掌控当前的协程是哪一个*(事实上可以,但通常没人这么干)*,所以我们需要在发生协程切换时能够同时切换对应的上下文。
在 Hyperf 里实现协程的上下文管理将非常简单,基于 Hyperf\Utils\Context 类的 set(string $id, $value)、get(string $id, $default = null)、has(string $id)、override(string $id, \Closure $closure) 静态方法即可完成上下文数据的管理,通过这些方法设置和获取的值,都仅限于当前的协程,在协程结束时,对应的上下文也会自动跟随释放掉,无需手动管理,无需担忧内存泄漏的风险。

<?php
namespace App\Controller;

use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Utils\Context;

/**
 * @AutoController()
 */

 class CoController{

    public function get(){
        //从当前协程上下文中取出 key 为 foo 的值,如不存在则返回 null 字符串
        return Context::get('foo', 'null');
    }
    public function update(RequestInterface $request){
        $foo = $request -> input('foo');
        Context::set('foo', $foo);
        return Context::get('foo');
    }
 }
第一步请求get方法  返回为空

在这里插入图片描述
第二步更新值 更新返回值为100
在这里插入图片描述
第三步 在获取看看是不是有影响–没有影响
在这里插入图片描述

处理方法二:协程上下文 + 魔术方法

<?php
namespace App\Controller;

use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Utils\Context;

/**
 * @AutoController()
 */

 class CoController{

    public function get(){
        return $this->foo;
    }
    public function update(RequestInterface $request){
        $foo = $request -> input('foo');
        $this->foo = $foo;
        return $this->foo;
    }

    public function __get($name){
    	//这个加class怕别的类有一样的, 这样确保一个类里面就一个
        return Context::get(__CLASS__ . ":" . $name, 'null');
    }

    public function __set($name, $value){
        return Context::set(__CLASS__. ":" . $name, $value);
    }
 }

//返回都正常,没有影响
在这里插入图片描述

通过Inject引入的类 – 出现的问题是会影响数据

<?php
namespace App\Controller;

use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Utils\Context;
use Hyperf\Di\Annotation\Inject;

/**
 * @AutoController()
 */
 class CoController{
    /**
     * @Inject()
     * @var \App\Foo
     */
    private $foo;
    public function get(){
        return $this->foo->bar;
    }
    public function update(RequestInterface $request){
        $foo = $request -> input('foo');
        $this->foo->bar = $foo;
        return $this->foo->bar;
    }
 }
<?php
namespace App;

Class Foo{

    public $bar = 1;
}

//影响了数据—无论是不是静态属性,都会在全局共享,所以要把所有的值都存储到协程上下文中,协程上下文会在协程结束的时候释放掉,无需担忧内存泄漏的问题
在这里插入图片描述
那么问题, 如果是这种引入类的方法应该怎么解决呢

//引入这的代码不需要改变

<?php
namespace App\Controller;

use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Utils\Context;
use Hyperf\Di\Annotation\Inject;

/**
 * @AutoController()
 */
 class CoController{
    /**
     * @Inject()
     * @var \App\Foo
     */
    private $foo;
    public function get(){
        return $this->foo->bar;
    }
    public function update(RequestInterface $request){
        $foo = $request -> input('foo');
        $this->foo->bar = $foo;
        return $this->foo->bar;
    }


 }

//foo类改变就可以

<?php
namespace App;

use Hyperf\Utils\Context;
Class Foo{

    public function __set($name, $value){
        return Context::set(__CLASS__ . ":" .$name, $value);
    }

    public function __get($name){
        return Context::get(__CLASS__ . ":" . $name);
    }
}

效果
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值