日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > php >内容正文

php

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

發(fā)布時(shí)間:2024/9/27 php 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 php ioc容器,PHP 在Swoole中使用双IoC容器实现无污染的依赖注入 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

容器(container)技術(shù)(可以理解為全局的工廠方法), 已經(jīng)是現(xiàn)代項(xiàng)目的標(biāo)配. 基于容器, 可以進(jìn)一步實(shí)現(xiàn)控制反轉(zhuǎn), 依賴注入. Laravel 的巨大成功就是構(gòu)建在它非常強(qiáng)大的IoC容器 illuminate/container 基礎(chǔ)上的. 而 PSR-11 定義了標(biāo)準(zhǔn)的 container , 讓更多的 PHP 項(xiàng)目依賴容器實(shí)現(xiàn)依賴解耦, 面向接口編程.

另一方面, PHP 天生一個(gè)進(jìn)程響應(yīng)一次請求的模型, 已經(jīng)不能完全適應(yīng)開發(fā)的需要. 于是 Swoole, reactPHP, roadrunner 也越來越流行. 它們共同的特點(diǎn)是一個(gè) php worker 進(jìn)程在生命周期內(nèi)要響應(yīng)多個(gè)請求, 甚至同一時(shí)間同時(shí)運(yùn)行多個(gè)請求 (協(xié)程).

在這些引擎上使用傳統(tǒng)只考慮單請求的容器技術(shù), 就容易發(fā)生單例相互污染, 內(nèi)存泄露等問題 (姑且稱之為”IoC容器的請求隔離問題” ). 于是出現(xiàn)了各種策略以解決之.

多輪對話機(jī)器人框架 CommuneChatbot 使用 swoole 做通信引擎, 同時(shí)非常廣泛地使用了容器和依賴注入. 在本項(xiàng)目中使用了 “雙容器策略” 來解決 “請求隔離問題” .

所謂”雙容器策略”, 總結(jié)如下:

同時(shí)運(yùn)行 “進(jìn)程級容器” 與 “請求級容器”

“進(jìn)程級容器” :

傳統(tǒng)的IoC 容器, 例如 Illuminate/container

“請求級容器” :

所有工廠方法注冊到容器的靜態(tài)屬性上

在 worker 進(jìn)程初始化階段 注冊服務(wù)

每個(gè)請求到來后, 實(shí)例化一個(gè)請求容器.

請求中生成的單例, 掛載到容器的動(dòng)態(tài)屬性上.

持有”進(jìn)程級容器”, 當(dāng)綁定不存在時(shí), 到”進(jìn)程級容器” 上查找之.

請求結(jié)束時(shí)進(jìn)行必要清理, 防止內(nèi)存泄露

解決方案的代碼在 https://github.com/thirdgerb/container 創(chuàng)建了一個(gè) composer 包 commune/container

容器的”請求隔離”問題

關(guān)于容器, 控制反轉(zhuǎn)與依賴注入

為防止部分讀者不了這些概念, 簡單說明一下.

所謂容器, 相當(dāng)于一個(gè)全局的工廠. 可以在這里 “注冊” 各種服務(wù)的工廠方法, 再使用容器統(tǒng)一地獲取. 例如

1 $container = newContainer();2

3 //綁定一個(gè)單例

4 $container->singleton(5

//綁定對象的ID, 通常是 interface, 以實(shí)現(xiàn)面向接口編程.

6

UserInterface::class,

7

//生成實(shí)例的工廠方法.

8

function() {9

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

13 //從容器中獲取實(shí)例

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

16 $user instanceof UserInterface; //true

當(dāng)一個(gè)類的實(shí)例在容器中生成, 或者一個(gè)方法被容器調(diào)用時(shí), 就可以方便地實(shí)現(xiàn)依賴注入.

簡單來說, 容器通過反射機(jī)制可獲取目標(biāo)方法的依賴 ( laravel 用反射來獲取 typehint 類型約束, 而 Swoft項(xiàng)目似乎與spring 相似, 是從注釋上獲取的).

然后容器查找是否已注冊了 依賴 (dependency) 的實(shí)現(xiàn) (resolver), 如果已注冊, 就從容器中生成該依賴, 再注入給目標(biāo)方法.

具有依賴注入能力的容器, 我們稱之為 IoC (控制反轉(zhuǎn)) 容器. 關(guān)于IoC 容器的好處不是本文重點(diǎn), 先跳過去了.

IoC 容器的請求隔離問題

容器最典型的應(yīng)用場景之一, 就是持有單例. 但在 swoole 等引擎上, 一個(gè) worker 進(jìn)程要響應(yīng)多個(gè)請求, 單例的數(shù)據(jù)就容易相互污染.

例如我們把 session 的數(shù)據(jù)放在 一個(gè) SessionInterface 中, 每個(gè)邏輯調(diào)用時(shí)都用容器來取:

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

由于單例在容器內(nèi)只生成一次, 那第二次請求時(shí), 容器會(huì)給出第一次請求的session單例, 從而邏輯就亂套了.

所以容器要運(yùn)行在 swoole 等引擎上, 必須做到請求與請求相隔離.

常見的解決策略

由于 Laravel 等使用了IoC 容器的項(xiàng)目能帶來極好的工程體驗(yàn), 而Swoole 能帶來極大的性能提升, 于是有許多試圖結(jié)合兩者的項(xiàng)目, 都面臨了 “請求隔離問題”.

我個(gè)人看到過的解決策略有以下三種, 都能一定程度解決問題, 但也有美中不足之處.

克隆策略:

方案: 每次請求, 克隆一個(gè)新的 container

問題:

要遞歸地 clone 屬性, 才能避免淺拷貝導(dǎo)致的污染

無法區(qū)分進(jìn)程共享的單例, 和請求隔離的單例.

清洗策略:

方案: 每次請求結(jié)束時(shí), 主動(dòng)清洗掉已注冊的單例

問題:

定義類時(shí)就要考慮清洗邏輯, 可能要實(shí)現(xiàn)interface, 耦合較重

swoole 發(fā)展到協(xié)程后, 同時(shí)可能相應(yīng)多個(gè)請求, 清晰策略失效了.

重新注冊:

方案: 每個(gè)請求到來時(shí), 實(shí)例化一個(gè)新容器, 重新注冊所有服務(wù)

問題:

注冊服務(wù)其實(shí)開銷很大, 尤其是需要大量讀文件的初始化(比如翻譯組件)

無法區(qū)分進(jìn)程共享的單例, 和請求隔離的單例.

利用不了 swoole 的優(yōu)勢, 比起多進(jìn)程模型只少了 composer autoloader 的加載.

CommuneChatbot 遇到的請求隔離問題

總結(jié)

以上是生活随笔為你收集整理的php ioc容器,PHP 在Swoole中使用双IoC容器实现无污染的依赖注入的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。