php ioc容器,PHP 在Swoole中使用双IoC容器实现无污染的依赖注入

容器(container)技术(可以理解为全局的工厂方法), 已经是现代项目的标配. 基于容器, 可以进一步实现控制反转, 依赖注入. Laravel 的巨大成功就是构建在它非常强大的IoC容器 illuminate/container 基础上的. 而 PSR-11 定义了标准的 container , 让更多的 PHP 项目依赖容器实现依赖解耦, 面向接口编程.

另一方面, PHP 天生一个进程响应一次请求的模型, 已经不能完全适应开发的需要. 于是 Swoole, reactPHP, roadrunner 也越来越流行. 它们共同的特点是一个 php worker 进程在生命周期内要响应多个请求, 甚至同一时间同时运行多个请求 (协程).

在这些引擎上使用传统只考虑单请求的容器技术, 就容易发生单例相互污染, 内存泄露等问题 (姑且称之为”IoC容器的请求隔离问题” ). 于是出现了各种策略以解决之.

多轮对话机器人框架 CommuneChatbot 使用 swoole 做通信引擎, 同时非常广泛地使用了容器和依赖注入. 在本项目中使用了 “双容器策略” 来解决 “请求隔离问题” .

所谓”双容器策略”, 总结如下:

同时运行 “进程级容器” 与 “请求级容器”

“进程级容器” :

传统的IoC 容器, 例如 Illuminate/container

“请求级容器” :

所有工厂方法注册到容器的静态属性上

在 worker 进程初始化阶段 注册服务

每个请求到来后, 实例化一个请求容器.

请求中生成的单例, 挂载到容器的动态属性上.

持有”进程级容器”, 当绑定不存在时, 到”进程级容器” 上查找之.

请求结束时进行必要清理, 防止内存泄露

解决方案的代码在 https://github.com/thirdgerb/container 创建了一个 composer 包 commune/container

容器的”请求隔离”问题

关于容器, 控制反转与依赖注入

为防止部分读者不了这些概念, 简单说明一下.

所谓容器, 相当于一个全局的工厂. 可以在这里 “注册” 各种服务的工厂方法, 再使用容器统一地获取. 例如

1 $container = newContainer();2

3 //绑定一个单例

4 $container->singleton(5

//绑定对象的ID, 通常是 interface, 以实现面向接口编程.

6

UserInterface::class,

7

//生成实例的工厂方法.

8

function() {9

return new class implementsUserInterface{};10 }11 );12

13 //从容器中获取实例

14 $user = $container->get(UserInterfacle::class);15

16 $user instanceof UserInterface; //true

当一个类的实例在容器中生成, 或者一个方法被容器调用时, 就可以方便地实现依赖注入.

简单来说, 容器通过反射机制可获取目标方法的依赖 ( laravel 用反射来获取 typehint 类型约束, 而 Swoft项目似乎与spring 相似, 是从注释上获取的).

然后容器查找是否已注册了 依赖 (dependency) 的实现 (resolver), 如果已注册, 就从容器中生成该依赖, 再注入给目标方法.

具有依赖注入能力的容器, 我们称之为 IoC (控制反转) 容器. 关于IoC 容器的好处不是本文重点, 先跳过去了.

IoC 容器的请求隔离问题

容器最典型的应用场景之一, 就是持有单例. 但在 swoole 等引擎上, 一个 worker 进程要响应多个请求, 单例的数据就容易相互污染.

例如我们把 session 的数据放在 一个 SessionInterface 中, 每个逻辑调用时都用容器来取:

$sessionInstance = container()->make(SessionInterface::class);

由于单例在容器内只生成一次, 那第二次请求时, 容器会给出第一次请求的session单例, 从而逻辑就乱套了.

所以容器要运行在 swoole 等引擎上, 必须做到请求与请求相隔离.

常见的解决策略

由于 Laravel 等使用了IoC 容器的项目能带来极好的工程体验, 而Swoole 能带来极大的性能提升, 于是有许多试图结合两者的项目, 都面临了 “请求隔离问题”.

我个人看到过的解决策略有以下三种, 都能一定程度解决问题, 但也有美中不足之处.

克隆策略:

方案: 每次请求, 克隆一个新的 container

问题:

要递归地 clone 属性, 才能避免浅拷贝导致的污染

无法区分进程共享的单例, 和请求隔离的单例.

清洗策略:

方案: 每次请求结束时, 主动清洗掉已注册的单例

问题:

定义类时就要考虑清洗逻辑, 可能要实现interface, 耦合较重

swoole 发展到协程后, 同时可能相应多个请求, 清晰策略失效了.

重新注册:

方案: 每个请求到来时, 实例化一个新容器, 重新注册所有服务

问题:

注册服务其实开销很大, 尤其是需要大量读文件的初始化(比如翻译组件)

无法区分进程共享的单例, 和请求隔离的单例.

利用不了 swoole 的优势, 比起多进程模型只少了 composer autoloader 的加载.

CommuneChatbot 遇到的请求隔离问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/528211.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

oracle19c监听服务启动失败,Oracle19c安装(有失败成功记录)

Oracle19c安装(有失败成功记录)失败过程失败安装设置 (图为安装成功后获取)成功过程安装设置配置监听桌面类与服务器类的区别总结说明:失败过程失败问题一模一样,无论安装成功或者失败都出现“无法添加用户XXXX到%2%组中”的弹窗,都是点6下继…

oracle立即关闭数据库,Oracle数据库的起步和关闭

Oracle数据库的启动和关闭要启动和关闭数据库,必须要以具有Oracle 管理员权限的用户登陆,通常也就是以具有SYSDBA权限的用户登陆,启动一个数据库需要三个步骤:1、 创建一个Oracle实例(非安装阶段)2、 由实例安装数据库(安装阶段)3、 打开数据…

oracle空例程,2018.5.29 Oracle连接到空闲例程

解决方法如下:1、通过cmd命令窗启动Oracle:(最好是以管理员身份启动)C:\Users\Administrator>sqlplus /nologSQL>conn /as sysdba 或者这种 conncet [email protected] as sysdba //sys是用户名 123456是密码 。 后面的是数据库名字SQL>startup…

linux ospf 命令,OSPF单区域配置 - linuxprobe2020的个人空间 - OSCHINA - 中文开源技术交流社区...

为了弥补距离矢量路由协议的不足,IEFI组织开发了一种基于链路状态的内部网关协议OSPF实验环境:红,绿,蓝三个颜色区域代表三个不同网络的办公场所,要求使用OSPF协议实现网络互通。pc1:172.16.1.1pc2:172.16.2.1pc3:172.…

Linux内核权限维持,Linux权限维持笔记

一、修改文件/终端的属性1、修改文件创建时间如果蓝队是按照文件修改时间来判断后门的话,比如现在我们上传一个shell,可以看到shell文件与原文件的时间是不同的解决方法:touch -r teamserver shell.php现在再来看原文件与shell文件的修改时间…

ubuntu下的linux怎样备份文件,Ubuntu系统如何备份还原?Ubuntu系统备份还原教程

Ubuntu系统如何备份还原?Ubuntu系统是一个以桌面应用为主的开源操作系统,专为开发团队而打造!无论你使用的是什么操作系统,都有可能出现电脑无法修复的故障,这时候备份还原功能就显得非常重要了。今天小编要给大家分享…

jenkins linux编译c,【Linux】【Jenkins】代码编译和执行过程中的问题汇总

1.问题1:java.io.FileNotFoundException: /root/.jenkins/workspace/Videoyi_AutoTest_Maven/config-log4j\log4j.xml (No such file or directory)该问题是由于代码是在windows下编写和编译的,使用的都是\\来实现目录结构的,结果到linux下的…

linux cd -目录,linux cd

cd -返回之前的目录cd !$ 把上个命令的参数作为这个命令的参数阅读目录(Content)1.命令格式:cd [目录名]2.命令功能切换当前目录至 [目录名]3. 常用范例1.进入系统根目录命令:cd /说明:进入系统根目录,上面命令执行完后拿ls命令看一下&#x…

linux清理整个磁盘空间,一次Linux磁盘空间清理的经历

最近,在Linux上运行BPM应用总是报没有空间的错误。经过一番调查和周折,终于找出了问题的症结,并顺利解决。今天,我把过程总结一下,方便自己或其他有类似问题的朋友参考。这里不对具体命令的具体各种参数做深入介绍&…

linux6.5 查看分辨率命令,centos6.5跟centos7的top命令中移动查看顺序的指令怎么不一样...

top 命令 移动查看顺序centos6.5 以下 top -ab -n 1按o 按大写字母可以调顺序 大写字母往左 小写往右f,o . Fields/Columns: ‘f’ add or remove; ‘o’ change display orderF or O . Select sort field查看cpu和内存占用排序,可以直接快捷方式但是在centos7中快捷键用大小写…

linux nfs spec,创建 NFS Ubuntu Linux 服务器卷 - Azure Kubernetes Service | Microsoft Docs

您现在访问的是微软AZURE全球版技术文档网站,若需要访问由世纪互联运营的MICROSOFT AZURE中国区技术文档网站,请访问 https://docs.azure.cn.在 Azure Kubernetes 服务 (AKS) 中手动创建和使用 NFS(网络文件系统)Linux 服务器卷4/25/2019本文内容基于容器…

c语言中字符数字加'0',C语言中的NULL与转义字符'\0'以及数值0的关系

以下两种只是一个巧合,只不过地址值的数值为0而已。0本身只不过是一个可以显示的字符,与内存并没有直接关系。在0与ASCII表中关联NULL做了关联,这样使得输入转义字符\0,也可以将一个变量赋值为NULL。而\0对应的ASCII码又是第0号&a…

c语言程序设计杨辉三角过程,C语言编程 打印杨辉三角

在做这道题时首先要观察杨辉三角的规律11 11 2 11 3 3 11 4 3 4 1……可以看出1.每行的数字个数与所处行数相等2.除去每行第一个和最后一个数字唯一,其他数字都等于头顶元素加头顶前一个元素源代码:#include#includevoid PrintY(int length, int array[][100]){int …

c语言中的所有关键字,C语言中的32个关键字

C语言中的32个关键字数据类型关键字(12个)(1) char:声明字符型变量或函数(2) double:声明双精度变量或函数(3) enum:声明美剧类型(4) float:声明浮点型变量或函数(5) int:声明整型变量或函数(6) …

c语言手游常用代码,c语言源代码【操作流程】

很多小伙伴都遇到过c语言源代码的困惑吧,一些朋友看过网上零散的c语言源代码的处理方法,并没有完完全全明白c语言源代码是如何解决的,今天小编准备了简单的解决办法,只需要按照1:编写C源代码for_learning_compile.c2&a…

c语言中文件是如何存储的,急求如何将下列C语言程序数据存储到文件中?

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼求如何改动才能将下列程序的存储输入或输出数据(或两者一起)到指定的文件(或运行时直接创立一个文件)如Arrangement中。#include int n0;int rest[7][7]; //全局声明,以供全局调用int main(){void perm(int list[],int ,int );int …

pdxp协议 C语言,集成CC控制逻辑,PD协议及MCU的Type-C应用方案

Type-C系列亮点介绍内包MCU、集成CC逻辑和DP协议支持Typec DP Alt mode 和单独的DP输入支持速率最高可达4-lane 5.4Gbps(HBR2)支持macbook2016版本,拔掉adapter不掉电● DP1.2/Type C转HDMI1.4,支持HDCP1.3,可扩展PD,USB3.0接口● 支持1/2/4 lane DP输…

Android打开谷歌应用,谷歌确认 Android 12 新增剪贴板访问提醒,将在 Beta 2 上线

IT之家 5 月 19 日消息 据外媒 xda-developers 报道,近年来,谷歌一直在打击 Android 系统中的剪贴板访问,并在发布 Android 10 时禁止后台应用读取剪贴板数据。在最新的 Android 12 中,谷歌引入了一项设置,每当应用访问…

Android nfc编译,【Android编译】各个模块编译方法

一、如何编译出vendor.img1. 首先找到产品对应BoardConfig.mk路径:LINUX/android/device/项目/产品/BoardConfig.mk2. 修改BoardConfig.mk设置下面代码中的ENABLE_VENDOR_IMAGE为true。#Enable split vendor imageENABLE_VENDOR_IMAGE : true#ENABLE_VENDOR_IMAGE :…

ubuntu下android源码编译环境,ubuntu12.04 64位上搭建android源码编译环境

1.首先替换源为163的源,默认源下载速度太慢sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup (备份下当前的源列表)sudo gedit /etc/apt/sources.list (打开Ubuntu 12.04源列表文件)deb http://mirrors.163.com/ubuntu/ precise main restricteddeb-src…