我的编程空间,编程开发者的网络收藏夹
学习永远不晚

基于 Hyperf ,进行便捷的上下文和协程调度管理,实现 伪事务 般的defer应用和请求级上下文管理

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

基于 Hyperf ,进行便捷的上下文和协程调度管理,实现 伪事务 般的defer应用和请求级上下文管理

hyperf Hyperf go Coroutine defer

场景

一个API项目,日常写代码过程中,在需要进行上下文设置时,当然是字面意思,但有时候,一个API动作的完成,可能需要有一些主动发起的协程调度,如 CSP 之类,或者 第三方耗时API 需要在事务最后进行,这些实践起来,虽然原生方法能够解决,但并不优雅,我对其进行了一定的改造,以实现:

  • Db事务中,将第三方耗时API请求、MQ投递 放在 EasyCo::defer 里,在Db事务结束时,commit就正常commit,但 rollBack 时,可以通过 EasyCo::deferRollBack 取消本次动作中注册的defer
  • 在一次 API动作 实现的过程中,通过 EasyCtx::set 设定的上下文变量,可以在本次请求里,通过任何 EasyCo::create 创建的协程里,EasyCtx::get 拿到,不需要进行主动的协程use和赋值

应用Demo

defer 的伪事务

//伪代码Db::beginTransaction();try {//TODO 业务操作make(UserService::class)->bsAction();//耗时操作// - 第三方API 联动业务// - 投递MQEasyCoroutine::easyDeferTrans(function (){//你的耗时操作});Db::commit();}catch (\Throwable $exception){Db::rollBack();EasyCoroutine::easyDeferRollBack();}        

通过这样的姿势,在一些你本来就封装了事务操作A,被嵌套在别人的复合事务操作B时,你的A就不是最终执行事务了,PHP本身没有嵌套事务,这样的嵌套只会合并为一个大事务进行执行,只需要在任意的 Db::rollBack() 时,同步执行 EasyCoroutine::easyDeferRollBack() ,即可取消掉本次注册的defer

请求级上下文

EasyContext::easySet('testData',1123123);        EasyCoroutine::easyCreate(function (){EasyContext::easyGet('testData');});

通过这样的姿势,只要是 EasyCtx::set 的变量,必定可在 EasyCo::create 创建的协程中,通过 EasyCtx::get 进行获取,即可完成请求级的上下文管理,并且只是赋值,无地址干扰操作


源码

EasyCtx

declare (strict_types=1);namespace App\Utils;use Hyperf\Context\Context;use Hyperf\Utils\Str;use Hyperf\WebSocketServer\Context as WsContext;class EasyContext{        public static function __callStatic($name, $arguments)    {        if (Str::startsWith(strtolower($name), 'ws')) {            //非ws环境中,使用ws上下文会导致内存泄漏,请谨慎使用            $wsMethodMap = [                'wsSet'          => 'set',                'wsGet'          => 'get',                'wsHas'          => 'has',                'wsDestroy'      => 'destroy',                'wsRelease'      => 'release',                'wsCopy'         => 'copy',                'wsOverride'     => 'override',                'wsGetOrSet'     => 'getOrSet',                'wsGetContainer' => 'getContainer',            ];            $method      = $wsMethodMap[$name] ?? false;            return WsContext::{$method}(...$arguments);        }        return Context::{$name}(...$arguments);    }        public static function easySet(mixed $id, mixed $value): mixed    {        $key          = self::ctxKey();        $current      = Context::getOrSet($key, []);        $current[$id] = $value;        return Context::set($key, $current);    }        public static function easyGet(string $id, $default = null): mixed    {        $current = Context::get(self::ctxKey(), []);        if (!$current) {            return $default;        } else {            return $current[$id] ?? $default;        }    }    static private function ctxKey(): string    {        return 'EasyContextCreate';    }}

defer 伪事务

declare (strict_types=1);namespace App\Utils;use Hyperf\Context\Context;use Hyperf\Utils\Coroutine;class EasyCoroutine{        public static function __callStatic($name, $arguments)    {        return Coroutine::{$name}(...$arguments);    }        static public function easyCreate(callable $callable): int    {        //创建时,获取当前特定需传输的上下文        $current = Context::get(self::ctxKey(), []);        return Coroutine::create(function () use ($callable, $current) {            try {                //也许copy进来,但感觉没必要,copy会直接覆盖且清空原上下文                //此处进行强行覆盖操作 其实等同于copy                Context::set(self::ctxKey(), $current);                call($callable);            } catch (\Throwable $exception) {                //日志记录                //通过 opis/closure 可将回调函数作为字符串保存日志 \Opis\Closure\serialize($callable)            }        });    }        static public function easyDeferTrans(callable $callable): string    {        if (Coroutine::inCoroutine()) {            $id = Coroutine::id() . '-' . session_create_id();        } else {            $id = uniqid() . '-' . session_create_id();        }        $deferCtx      = Context::getOrSet(self::deferCtxKey(), []);        $deferCtx[$id] = 1;        Context::set(self::deferCtxKey(), $deferCtx);        $fn = function () use ($callable, $id) {            try {                $deferCtx = Context::get(self::deferCtxKey(), []);                $defer = intval($deferCtx[$id] ?? 0);                if ($defer) {                    call($callable);                }            } catch (\Throwable $exception) {                //日志记录                //通过 opis/closure 可将回调函数作为字符串保存日志 \Opis\Closure\serialize($callable)            }        };        Coroutine::defer($fn);        return $id;    }        static public function easyDeferRollBack(): void    {        try {            if (Coroutine::inCoroutine()) {                Context::set(self::deferCtxKey(), []);            } else {                Context::destroy(self::deferCtxKey());            }        } catch (\Throwable $exception) {            //日志记录            //通过 opis/closure 可将回调函数作为字符串保存日志 \Opis\Closure\serialize($callable)        }    }        static public function easyDeferRollBackById(mixed $id): void    {        try {            $deferCtx = Context::get(self::deferCtxKey(), []);            if ($deferCtx) {                $deferCtx[$id] = 0;                Context::set(self::deferCtxKey(), $deferCtx);            }        } catch (\Throwable $exception) {            //日志记录            //通过 opis/closure 可将回调函数作为字符串保存日志 \Opis\Closure\serialize($callable)        }    }        static private function deferCtxKey(): string    {        return 'EasyCoroutineDefer';    }        static private function ctxKey(): string    {        return 'EasyContextCreate';    }}

来源地址:https://blog.csdn.net/liyunfan2016/article/details/128077937

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

基于 Hyperf ,进行便捷的上下文和协程调度管理,实现 伪事务 般的defer应用和请求级上下文管理

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录