內容來源:2018 年 5 月 20 日,騰訊企點開放平臺技術負責人熊月在“PHPCon China 2018 技術峰會”進行《嵌入式PHP的探索實踐》演講分享。IT 大咖說(微信id:itdakashuo)作為獨家視訊合作方,經主辦方和講者審閱授權釋出。
閱讀字數:2410 | 7分鐘閱讀
摘要
對於擁有很多複雜業務場景的tob領域,“開發效率”和“效能”常常是我們考慮的兩個主要問題,PHP作為指令碼語言,本身適用於快速開發業務邏輯,同時為了解決PHP特定的效能瓶頸,一般將C++/PHP結合,在PHP程式碼裡呼叫C/C++擴充套件。 這次我們帶來了不同思路的探索:將php嵌入到高效能C/C++框架執行,將C/C++框架作為容器,完美結合php快速開發優勢和C/C++高效能特點。Zend Engine提供了一種嵌入式開發模式,我們利用這一特性使它可以在C/C++的環境中單獨執行PHP指令碼,並且支援多例項執行,可以在C/C++協程框架中執行。嵌入式PHP也為在任意C/C++協程框架結合帶來無限可能,包括在C++的客戶端上執行PHP。
嵌入式PHP
作為一門後臺開發語言PHP有著不同的發展階段。最開始是大家都比較熟悉的LAMP,接著是PHP-fpm和fastcgi,再往後是swoole,之後在swoole的基礎上又新增了協程。
為了便於理解,在介紹嵌入式PHP之前要先講下SAPI的概念。SAPI即後臺應用程式程式設計介面,是PHP與其他應用程式互動的介面。常見的SAPI有cgi、fpm、cli、Apace2 hander,而嵌入式PHP(embed)也是其中一類。
業務場景
我們最初的業務框架是基於TSF2.0,底層為Zend Engine和擴充套件,擴充套件最核心的部分是基於swoole。在此之上是TSF PHP層,包含協程排程器、微服務框架、監控管理程式、MVC模式。最上層才是真正的執行邏輯的PHP指令碼。
這樣一套框架存在幾個問題。PHP原生的Generator協程需要配合yield使用,對開發來說不怎麼友好,因為yield的使用時機不太好確定,尤其是對於新手。由於協程排程器是用原生PHP實現的,因此相對其他語言在效能上會差些,特別是在高併發場景下。還有就是低版本swoole不夠穩定,問題最多的就是在記憶體洩露這塊。大家都知道騰訊內部有很多公用元件,這些元件介面大多是用C++實現的,為此我們需要做的是用擴充套件對接PHP與其他元件,而問題就在於擴充套件無法使用上層的PHP協程。
方案:SPP+PHP
為什麼選擇嵌入式PHP
SNG中有個非常有名的C++後臺框架SPP,它是一個高效能的網路框架,起始於2008年,被廣泛的應用於SNG的各個業務線。眾所周知開發效率一直是PHP的長處,效能方面則是短板。所以我們就在想能不能將SPP和PHP結合起來兼顧高效能和開發效率,嵌入式PHP無疑是很好的結合方案。
SPP主要有5個模組。proxy用來處理新請求,記憶體佇列是proxy和workgroup互動的記憶體區域,workgroup是和後臺邏輯指令碼互動的模組,controller作為控制核心來控制proxy和workgroup的執行狀態,最後是最重要的協程模組。
如何將SPP和Zend結合
SPP其實是基於協程的框架,協程是一個使用者態的多執行緒概念。在協程切換的時候會涉及記憶體管理的機制,而Zend沒有這種切換記憶體資源的機制,只有全域性變數和多執行緒資源隔離的方式。這樣的話要想將SPP和Zend結合起來,就要對Zend進行改造。
Zend的原始碼大概有60萬行,如果直接改動核心原始碼,不光實施起來很麻煩,對之後的升級也會造成問題。最好的辦法是藉助Zend本身的機制對入口進行改造,而不侵入核心。
Zend改造
Zend有多程式和多執行緒兩種方式,在多執行緒模式下有一個執行緒安全的機制ZTS。ZTS本質其實是對每個執行緒的全域性資源進行了隔離,與SPP協程的結合就需要用到ZTS,下面是具體步驟。
第一步當然是開啟Zend核心ZTS開關,第二步為了滿足協程上下文切換,需要將ZTS中的執行緒私有變數轉化為全域性資料元素,第三步增加資源入口切換API。做完這三步就完成了Zend和SPP的結合,雖然步驟不多但實際上在做的過程中還是會有很多挑戰。
PHP執行流排程器
解決了結合問題之後,接下來為了將整個流程串起來需要有一個執行流程排程器。上圖是整個執行流程,首先SPP通過SAPI進入到Zend中,然後Zend執行PHP指令碼,先編譯成OpCode,之後如果有網路IO就會用到協程。協程也可以基於SPP提供的API來運作,通過Tsrm的全域性資源table可以進行協程切換。
在有這樣一套執行流的情況下,擴充套件也可以依賴SPP的API實現協程排程。
上圖是SPP結合PHP之後的整體架構。最底層是SPP,往上是PHP執行流排程器,中間是PHP核心和擴充套件,最上層是PHP指令碼。這一整套架構以嵌入式的方式結合了SPP和PHP,讓開發者既可以利用SPP提供的高效能網路框架,同時也能應用到上層PHP快速開發的特點。
這裡展示的是壓測時候的環境資料,包括機型、壓測工具、壓測方法以及框架的版本等。
這是壓測後獲得的實際資料對比,可以看到SPP-PHP框架相對舊的框架效能上大概有3倍的提升。