首頁  >  文章  >  後端開發  >  享受PHP與Go的強大合體【RoadRunner】的樂趣!

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

藏色散人
藏色散人轉載
2021-11-12 14:22:085464瀏覽

#享有它的樂趣!


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

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

我們將透過大量的 Go 和 PHP 經驗告訴你,如何用它來解決實際的開發問題,以及我們如何把它變成一個工具,來消除與  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 池中。

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

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

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

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

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

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

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】的樂趣!的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:learnku.com。如有侵權,請聯絡admin@php.cn刪除