享受PHP與Go的強大合體【RoadRunner】的樂趣!

2021-11-12 16:00:37

享受它的樂趣吧!


在過去的十年中,我們一直在為 財富 500 強公司 以及使用者人數不超過 500 人的企業開發應用程式。 一直以來,我們的工程師主要使用 PHP 來開發後端。 但是兩年前,出現了一些問題不僅嚴重影響了我們的產品效能,還影響了它們的可延伸性——因此我們將 Golang (Go) 引入了我們的技術棧。

幾乎同時,我們發現 Go 不僅允許我們建立更大的應用程式,並且能夠將效能提高多達 40 倍。 有了它,我們能夠擴充套件使用 PHP 編寫的現有產品,並通過結合兩種語言的優勢來改進它們。

我們將通過大量的 Go 和 PHP 經驗告訴你,如何用它解決實際的開發問題,以及我們如何把它變成一個工具,來消除與 PHP 死亡模型 相關的一些問題。

推薦學習:《》

常規 PHP 開發環境

在講述 Go 如何改善 PHP 死亡模型前,先了解一下常規 PHP 開發環境。

通常,應用執行於 nginx 和 PHP-FPM 上。nginx 處理靜態請求,而動態請求則被重定向給 PHP-FPM,並由其執行 PHP 程式碼。也許你用的是 Apache 和 mod_php,但是他們原理相同,執行起來只有細微的差別。

看看 PHP-FPM 是如何執行程式碼的。當收到請求,PHP-FPM 初始化 PHP 子程序,並將請求的詳細資訊轉發給它,作為其狀態的一部分(_GET, _POST, _SERVER 等)。

在 PHP 指令碼執行期間,狀態將無法更改,因此只能通過一種方式獲取一組新的輸入資料:清除程序記憶體並再次初始化它。

這種效能模型有許多優點。你不需要太擔心記憶體消耗,所有程序都是完全隔離的,如果其中一個程序「死亡」,它將自動重新建立,並且不會影響其他程序。但是,當你嘗試擴充套件應用程式時,這種方式會有缺點產生。

典型 PHP 環境的缺點和低效性

如果你從事 PHP 的專業開發,那麼你就知道從哪兒開始建立一個新專案——選擇框架。它是一個用於依賴注入、ORM、轉化和模板方法的庫。當然,所有使用者輸入的資料都可以方便地放在一個物件中(Symfony / HttpFoundation 或者 PSR-7)。這些框架很棒!

但一切都有它的代價。在任何企業框架中,為了處理一個簡單的使用者請求或存取資料庫,您必須載入至少幾十個檔案,建立許多類,並解析多個設定。但最糟糕的是,在每個任務完成後,您需要重置所有內容並重新啟動:您剛剛啟動的所有程式碼都將變得無用,在它的幫助下,您將無法處理另一個請求。把這件事告訴任何用其他語言編寫的程式設計師——你會看到他臉上的困惑。

多年來,PHP工程師一直在尋找解決此問題的方法,他們使用了延遲載入技術、微幀、優化庫、快取等。但最終,您仍然必須放棄整個應用程式,重新開始*(譯者注:隨著PHP7.4中預載入的出現,這個問題將得到部分解決)

一個PHP程序能處理多個請求嗎?

您可以編寫持續時間超過幾分鐘的PHP指令碼(最多幾小時或幾天):例如Cron任務、CSV解析器、佇列處理程式。所有這些工作遵循一個模式:他們獲取一條任務,處理完它,然後獲取下一個任務。程式碼常駐在記憶體中,因此避免了額外的操作來載入框架和應用程式,節約了寶貴時間。

但是開發長時間執行的指令碼並不是那麼容易。任何錯誤都會殺死程序,記憶體溢位會導致崩潰,而且不能用F5來偵錯程式了。

自PHP 7後情況有所改善:可靠的垃圾收集器出現了,它變得更容易處理錯誤,核心的擴充套件可以避免記憶體漏失。是的,工程師仍然需要仔細處理記憶體並記住程式碼中的狀態的問題(有哪一種語言能讓你可以不關注這些事情呢?)當然,在PHP 7中,驚喜並不多。

是否可以採用一種 常駐 PHP 指令碼的模型,將其用於處理 HTTP 請求等更瑣碎的任務,從而消除對每個請求都從頭開始下載所有內容的需要?

要解決這個問題,首先需要實現一個伺服器應用程式,該應用程式可以接收 HTTP 請求並將它們逐個重定向到 PHP worker,而不是每次都殺死它。

我們知道我們可以用純 PHP(PHP-PM)或 C 擴充套件(Swoole)編寫 web 伺服器。儘管每種方法都有其優點,但這兩種選擇都不適合我們 —— 我想要更多的東西。我們需要的不僅僅是一個 web 伺服器 —— 我們希望得到一個解決方案,可以使我們避免與 PHP 中的「重新啟動動」相關的問題,同時可以輕鬆地為特定的應用程式進行調整和擴充套件。也就是說,我們需要一個應用伺服器。

Go 可以幫助解決這個問題嗎?我們知道它可以,因為這種語言將應用程式編譯成單個的二進位制檔案; 它是跨平臺的; 使用自己的並行處理模型(並行)和用於處理 HTTP 的庫; 最後,我們可以把更多的開源庫整合到我們的程式中。

合併兩種程式語言遇到的困難

首先,有必要確定兩個或多個應用程式之間如何相互通訊。

例如,使用 Alex Palaestras 的 go-php 庫, 可以實現 PHP 和 Go 程序 (如 Apache 中的 mod_php) 之間的記憶體共用。但是這個庫的功能限制了我們使用它解決問題。

我們決定使用另一種更常見的方法:通過使用 sockets / pipelines 來構建程序之間的互動。 這種方法在過去十年中已經證明了其可靠性,並且在作業系統級別得到了很好的優化。

首先,我們建立了一個簡單的二進位制協定,用於在程序之間交換資料和處理傳輸錯誤。在其最簡單的形式中, 這種型別的協定類似於 一個具有固定大小的 packet 頭 (在我們的範例中為 17 個位元組) 的 netstring ,其中包含的資訊有 packet 的型別,其大小和二進位制掩碼的資訊,用來檢查資料的完整性。

在 PHP 端,我們使用了 pack 函數 ,在 Go 端,使用了 編碼 / 二進位制 庫。

有一個協定對我們來說有點過時,我們新增了直接 從 PHP 呼叫 net / rpc Go 服務 的功能。 這個功能在後面的開發中對我們有很大的幫助,因為我們可以輕鬆地將 Go 庫整合到 PHP 應用程式中。這項工作的結果可以在我們的另一個開源產品 Goridge 中看到。

在多個 PHP Worker 之間分配任務

在互動機制實現之後,我們開始思考如何更好地將任務轉移到 PHP 程序中。當任務到達時,應用伺服器必須選擇一個空閒的 worker 來執行它。 如果 worker 程序因錯誤而終止或「死亡」,我們將清除它並建立一個新的。 如果 worker 程序成功執行,我們會將它返回到可用於執行任務的 worker 池中。

7a43d737b275a71f280bbb8d8c0a0b1.png

為了儲存活躍的 worker 程序池,我們使用了一個 緩衝通道 , 為了從池中清除意外「死亡」的工作程序,我們新增了一種跟蹤錯誤和 worker 程序狀態的機制。

最終,我們得到了一個可以執行的 PHP 伺服器,它能夠處理任何以二進位制形式呈現的請求。

為了讓我們的應用程式作為 web 伺服器開始工作,我們必須選擇一個可靠的 PHP 標準來處理任何傳入的 HTTP 請求。在我們的例子中,我們只需將簡單的 net / http 請求從 Go 轉換 為 PSR-7 格式,這樣它就可以與目前大多數可用的 PHP 框架相容。

由於 PSR-7 被認為是不可變的(有人會說在技術上不是),開發人員必須編寫那些在原則上不將請求視為全域性實體的應用程式。這完全符合 PHP 常駐程序的概念。我們的最終實現(尚未收到名稱)如下所示:

679f70da54830441997242bdd8d586c.png

RoadRunner - 高 - 效能 PHP 應用伺服器

我們的第一個測試任務是一個 API 後端,在該後端上,會週期性地出現不可預測的突發請求(比平時更頻繁)。雖然在大多數情況下 nginx capabilities 是足夠的,但是我們經常因為無法在預期的負載增加下快速平衡系統而遇到 502 錯誤。

為解決此問題,我們在 2018 年初部署了第一臺 PHP / Go 應用伺服器。並立即取得了驚人的效果!我們不僅完全消除了 502 錯誤,並且還將伺服器的數量減少了三分之二,節省了大量資金並解決了令工程師和產品經理頭痛的問題。

在年中的時候,我們改進了我們的方案,在 MIT 許可下將其釋出在 GitHub 上,並命名為 RoadRunner, 從而強調了它驚人的速度和效率。

RoadRunner 是如何改進你的開發堆疊的

RoadRunner 的使用允許我們在 Go 端使用中介軟體 net/http ,甚至在請求進入 PHP 之前進行 JWT 驗證,以及在 Prometheus 中處理 WebSocket 和全域性聚合狀態。

由於內建的 RPC,你可以在不編寫擴充套件包的情況下,在 PHP 中開啟任何 Go 庫的 API。更重要的是,使用 RoadRunner,你可以部署不同於 HTTP 的新伺服器。範例包括在 PHP 中執行 AWS Lambda 處理器 , 建立強大的佇列 選擇器, 甚至將 gRPC 新增到我們的應用程式中。

同時使用 PHP 和 Go ,對解決方案有了穩定的提升,在一些測試中將應用程式效能提高了 40 倍,改進了偵錯工具,實現了與 Symfony 框架的整合,並新增了對 HTTPS、HTTP/2、外掛和 PSR-17 的支援。

結論

有些人仍然被過時的 PHP 概念所束縛,認為 PHP 是一種緩慢、繁瑣的語言,只適合在 WordPress 下編寫外掛。這些人甚至還說 PHP 有這樣一個限制:當應用程式變得足夠大時,你必須選擇一種更「成熟」的語言,並重寫多年積累的程式碼庫。

對於這些問題,我的回答是:再想一想。我們相信只是你自己為 PHP 設定了一些限制。你可以用一生的時間從一種語言遷移到另一種語言,試圖找到與你的需求完美結合的語言,或者你可以將語言視為工具。像 PHP 這樣的語言,它的假想缺陷可能是其成功的真正原因。如果你將它與另一種語言(如 Go)相結合,那麼你將創造出比只使用一種語言更強大的產品。

在交替使用過Go和PHP之後,我們可以說我們很喜歡它們。我們不打算犧牲其中一個來換取另一個,相反,我們會想辦法從這個雙重架構中獲得更多收益。

原文地址:https://sudonull.com/post/6470-RoadRunne...

譯文地址:https://learnku.com/php/t/61733

以上就是享受PHP與Go的強大合體【RoadRunner】的樂趣!的詳細內容,更多請關注TW511.COM其它相關文章!