文章详情

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

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2023-10-09 13:16

关注

hyperf Hyperf go Coroutine defer

场景

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


应用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

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯