這篇文章主要介紹“RoadRunner有哪些特性”的相關(guān)知識(shí),小編通過實(shí)際案例向大家展示操作過程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“RoadRunner有哪些特性”文章能幫助大家解決問題。
創(chuàng)新互聯(lián)建站專業(yè)為企業(yè)提供都勻網(wǎng)站建設(shè)、都勻做網(wǎng)站、都勻網(wǎng)站設(shè)計(jì)、都勻網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)與制作、都勻企業(yè)網(wǎng)站模板建站服務(wù),十年都勻做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。
在講述 Go 如何改善 PHP 死亡模型前,先了解一下常規(guī) PHP 開發(fā)環(huán)境。
通常,應(yīng)用運(yùn)行于 nginx 和 PHP-FPM 上。nginx 處理靜態(tài)請(qǐng)求,而動(dòng)態(tài)請(qǐng)求則被重定向給 PHP-FPM,并由其執(zhí)行 PHP 代碼。也許你用的是 Apache 和 mod_php,但是他們?cè)硐嗤\(yùn)行起來只有細(xì)微的差別。
看看 PHP-FPM 是如何執(zhí)行代碼的。當(dāng)收到請(qǐng)求,PHP-FPM 初始化 PHP 子進(jìn)程,并將請(qǐng)求的詳細(xì)信息轉(zhuǎn)發(fā)給它,作為其狀態(tài)的一部分(_GET, _POST, _SERVER 等)。
在 PHP 腳本執(zhí)行期間,狀態(tài)將無法更改,因此只能通過一種方式獲取一組新的輸入數(shù)據(jù):清除進(jìn)程內(nèi)存并再次初始化它。
這種性能模型有許多優(yōu)點(diǎn)。你不需要太擔(dān)心內(nèi)存消耗,所有進(jìn)程都是完全隔離的,如果其中一個(gè)進(jìn)程「死亡」,它將自動(dòng)重新創(chuàng)建,并且不會(huì)影響其他進(jìn)程。但是,當(dāng)你嘗試擴(kuò)展應(yīng)用程序時(shí),這種方式會(huì)有缺點(diǎn)產(chǎn)生。
如果你從事 PHP 的專業(yè)開發(fā),那么你就知道從哪兒開始創(chuàng)建一個(gè)新項(xiàng)目 —— 選擇框架。它是一個(gè)用于依賴注入、ORM、轉(zhuǎn)化和模板方法的庫。當(dāng)然,所有用戶輸入的數(shù)據(jù)都可以方便地放在一個(gè)對(duì)象中(Symfony / HttpFoundation 或者 PSR-7)。這些框架很棒!
但一切都有它的代價(jià)。在任何企業(yè)框架中,為了處理一個(gè)簡(jiǎn)單的用戶請(qǐng)求或訪問數(shù)據(jù)庫,您必須加載至少幾十個(gè)文件,創(chuàng)建許多類,并解析多個(gè)配置。但最糟糕的是,在每個(gè)任務(wù)完成后,您需要重置所有內(nèi)容并重新啟動(dòng):您剛剛啟動(dòng)的所有代碼都將變得無用,在它的幫助下,您將無法處理另一個(gè)請(qǐng)求。把這件事告訴任何用其他語言編寫的程序員 —— 你會(huì)看到他臉上的困惑。
多年來,PHP 工程師一直在尋找解決此問題的方法,他們使用了延遲加載技術(shù)、微幀、優(yōu)化庫、緩存等。但最終,您仍然必須放棄整個(gè)應(yīng)用程序,重新開始 *(譯者注:隨著 PHP7.4 中預(yù)加載的出現(xiàn),這個(gè)問題將得到部分解決)
您可以編寫持續(xù)時(shí)間超過幾分鐘的 PHP 腳本(最多幾小時(shí)或幾天):例如 Cron 任務(wù)、CSV 解析器、隊(duì)列處理程序。所有這些工作遵循一個(gè)模式:他們獲取一條任務(wù),處理完它,然后獲取下一個(gè)任務(wù)。代碼常駐在內(nèi)存中,因此避免了額外的操作來加載框架和應(yīng)用程序,節(jié)約了寶貴時(shí)間。
但是開發(fā)長(zhǎng)時(shí)間運(yùn)行的腳本并不是那么容易。任何錯(cuò)誤都會(huì)殺死進(jìn)程,內(nèi)存溢出會(huì)導(dǎo)致崩潰,而且不能用 F5 來調(diào)試程序了。
自 PHP 7 后情況有所改善:可靠的垃圾收集器出現(xiàn)了,它變得更容易處理錯(cuò)誤,內(nèi)核的擴(kuò)展可以避免內(nèi)存泄漏。是的,工程師仍然需要仔細(xì)處理內(nèi)存并記住代碼中的狀態(tài)的問題(有哪一種語言能讓你可以不關(guān)注這些事情呢?)當(dāng)然,在 PHP 7 中,驚喜并不多。
是否可以采用一種 常駐 PHP 腳本的模型,將其用于處理 HTTP 請(qǐng)求等更瑣碎的任務(wù),從而消除對(duì)每個(gè)請(qǐng)求都從頭開始下載所有內(nèi)容的需要?
要解決這個(gè)問題,首先需要實(shí)現(xiàn)一個(gè)服務(wù)器應(yīng)用程序,該應(yīng)用程序可以接收 HTTP 請(qǐng)求并將它們逐個(gè)重定向到 PHP worker,而不是每次都?xì)⑺浪?/p>
我們知道我們可以用純 PHP(PHP-PM)或 C 擴(kuò)展(Swoole)編寫 web 服務(wù)器。盡管每種方法都有其優(yōu)點(diǎn),但這兩種選擇都不適合我們 —— 我想要更多的東西。我們需要的不僅僅是一個(gè) web 服務(wù)器 —— 我們希望得到一個(gè)解決方案,可以使我們避免與 PHP 中的「重啟動(dòng)」相關(guān)的問題,同時(shí)可以輕松地為特定的應(yīng)用程序進(jìn)行調(diào)整和擴(kuò)展。也就是說,我們需要一個(gè)應(yīng)用服務(wù)器。
Go 可以幫助解決這個(gè)問題嗎?我們知道它可以,因?yàn)檫@種語言將應(yīng)用程序編譯成單個(gè)的二進(jìn)制文件; 它是跨平臺(tái)的; 使用自己的并行處理模型(并發(fā))和用于處理 HTTP 的庫; 最后,我們可以把更多的開源庫集成到我們的程序中。
首先,有必要確定兩個(gè)或多個(gè)應(yīng)用程序之間如何相互通信。
例如,使用 Alex Palaestras 的 go-php 庫,可以實(shí)現(xiàn) PHP 和 Go 進(jìn)程 (如 Apache 中的 mod_php) 之間的內(nèi)存共享。但是這個(gè)庫的功能限制了我們使用它解決問題。
我們決定使用另一種更常見的方法:通過使用 sockets /pipelines 來構(gòu)建進(jìn)程之間的交互。 這種方法在過去十年中已經(jīng)證明了其可靠性,并且在操作系統(tǒng)級(jí)別得到了很好的優(yōu)化。
首先,我們創(chuàng)建了一個(gè)簡(jiǎn)單的二進(jìn)制協(xié)議,用于在進(jìn)程之間交換數(shù)據(jù)和處理傳輸錯(cuò)誤。在其最簡(jiǎn)單的形式中, 這種類型的協(xié)議類似于 一個(gè)具有固定大小的 packet 頭 (在我們的示例中為 17 個(gè)字節(jié)) 的 netstring ,其中包含的信息有 packet 的類型,其大小和二進(jìn)制掩碼的信息,用來檢查數(shù)據(jù)的完整性。
在 PHP 端,我們使用了 pack 函數(shù) ,在 Go 端,使用了 編碼 / 二進(jìn)制 庫。
有一個(gè)協(xié)議對(duì)我們來說有點(diǎn)過時(shí),我們添加了直接 從 PHP 調(diào)用 net /rpc Go 服務(wù) 的功能。 這個(gè)功能在后面的開發(fā)中對(duì)我們有很大的幫助,因?yàn)槲覀兛梢暂p松地將 Go 庫集成到 PHP 應(yīng)用程序中。這項(xiàng)工作的結(jié)果可以在我們的另一個(gè)開源產(chǎn)品 Goridge 中看到。
在交互機(jī)制實(shí)現(xiàn)之后,我們開始思考如何更好地將任務(wù)轉(zhuǎn)移到 PHP 進(jìn)程中。當(dāng)任務(wù)到達(dá)時(shí),應(yīng)用服務(wù)器必須選擇一個(gè)空閑的 worker 來執(zhí)行它。 如果 worker 進(jìn)程因錯(cuò)誤而終止或「死亡」,我們將清除它并創(chuàng)建一個(gè)新的。 如果 worker 進(jìn)程成功執(zhí)行,我們會(huì)將它返回到可用于執(zhí)行任務(wù)的 worker 池中。
為了存儲(chǔ)活躍的 worker 進(jìn)程池,我們使用了一個(gè) 緩沖通道 , 為了從池中清除意外「死亡」的工作進(jìn)程,我們添加了一種跟蹤錯(cuò)誤和 worker 進(jìn)程狀態(tài)的機(jī)制。
最終,我們得到了一個(gè)可以運(yùn)行的 PHP 服務(wù)器,它能夠處理任何以二進(jìn)制形式呈現(xiàn)的請(qǐng)求。
為了讓我們的應(yīng)用程序作為 web 服務(wù)器開始工作,我們必須選擇一個(gè)可靠的 PHP 標(biāo)準(zhǔn)來處理任何傳入的 HTTP 請(qǐng)求。在我們的例子中,我們只需將簡(jiǎn)單的 net /http 請(qǐng)求從 Go 轉(zhuǎn)換 為 PSR-7 格式,這樣它就可以與目前大多數(shù)可用的 PHP 框架兼容。
由于 PSR-7 被認(rèn)為是不可變的(有人會(huì)說在技術(shù)上不是),開發(fā)人員必須編寫那些在原則上不將請(qǐng)求視為全局實(shí)體的應(yīng)用程序。這完全符合 PHP 常駐進(jìn)程的概念。我們的最終實(shí)現(xiàn)(尚未收到名稱)如下所示:
我們的第一個(gè)測(cè)試任務(wù)是一個(gè) API 后端,在該后端上,會(huì)周期性地出現(xiàn)不可預(yù)測(cè)的突發(fā)請(qǐng)求(比平時(shí)更頻繁)。雖然在大多數(shù)情況下 nginx capabilities 是足夠的,但是我們經(jīng)常因?yàn)闊o法在預(yù)期的負(fù)載增加下快速平衡系統(tǒng)而遇到 502 錯(cuò)誤。
為解決此問題,我們?cè)?2018 年初部署了第一臺(tái) PHP / Go 應(yīng)用服務(wù)器。并立即取得了驚人的效果!我們不僅完全消除了 502 錯(cuò)誤,并且還將服務(wù)器的數(shù)量減少了三分之二,節(jié)省了大量資金并解決了令工程師和產(chǎn)品經(jīng)理頭痛的問題。
在年中的時(shí)候,我們改進(jìn)了我們的方案,在 MIT 許可下將其發(fā)布在 GitHub 上,并命名為 RoadRunner, 從而強(qiáng)調(diào)了它驚人的速度和效率。
RoadRunner 的使用允許我們?cè)?Go 端使用中間件 net/http ,甚至在請(qǐng)求進(jìn)入 PHP 之前進(jìn)行 JWT 驗(yàn)證,以及在 Prometheus 中處理 WebSocket 和全局聚合狀態(tài)。
由于內(nèi)置的 RPC,你可以在不編寫擴(kuò)展包的情況下,在 PHP 中打開任何 Go 庫的 API。更重要的是,使用 RoadRunner,你可以部署不同于 HTTP 的新服務(wù)器。示例包括在 PHP 中運(yùn)行 AWS Lambda 處理器,創(chuàng)建強(qiáng)大的隊(duì)列 選擇器, 甚至將 gRPC 添加到我們的應(yīng)用程序中。
同時(shí)使用 PHP 和 Go ,對(duì)解決方案有了穩(wěn)定的提升,在一些測(cè)試中將應(yīng)用程序性能提高了 40 倍,改進(jìn)了調(diào)試工具,實(shí)現(xiàn)了與 Symfony 框架的集成,并添加了對(duì) HTTPS、HTTP/2、插件和 PSR-17 的支持。
關(guān)于“RoadRunner有哪些特性”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。