《高性能PHP》学习笔记
本文主要是閱讀《高性能PHP》之后做的學習筆記,以便查閱。
簡介
《高性能PHP 7》從PHP所需環境講起,涉及環境搭建與配置設置等內容,能夠幫助有一定計算機基礎的讀者實現PHP運行環境的搭建及PHP周邊軟件的安裝配置?!陡咝阅躊HP 7》亮點內容是介紹PHP 7特性的部分,同時也告訴讀者哪些語法將會被廢棄,對讀者上手PHP 7有極大的幫助。除介紹PHP 7的新特性外,《高性能PHP 7》用大量章節介紹如何使用PHP 7及周邊軟件來構造高性能的Web應用程序,同時介紹了一些PHP開發的最佳實踐,幫助讀者更好地理解Web項目與PHP應用程序開發。附錄部分為讀者介紹了一些不錯的工具,講解了MVC與開發框架等周邊知識,能夠幫助讀者更好地開發PHP項目。
環境搭建
PHP出現的背景
由于PHP語言自身存在性能問題的瓶頸,面對大規模應用場景存在性能低下問題,即使是小型應用程序在面對大流量場景時,也存在這個問題。
在此期間出現了很多緩存技術方案,例如APC緩存等,但它們只是治標不治本。所以,PHP社區迫切需要一款能夠顯著提升PHP應用性能的新版本,此時,PHPNG項目隨之而來。
PHPNG全稱PHP Next Generation,是PHP的全新分支,主要目標是提升應用性能。一些人認為PHPNG屬于即時編譯(Just In Time,JIT),但實際上,它是基于Zend Engine的依次重構,以針對性能問題的進行專項優化。PHPNG是PHP 7項目的基礎,通過PHP官網的wiki可以看到PHPNG項目已被并入PHP 7的開發主干。
搭建windows環境
在Windows系統中,已經有很多PHP集成環境工具,這些工具打包了Apache、PHP、MySQL等PHP常用的軟件,是的這些集成安裝和使用非常的簡單。這些工具大部分采用Apache搭配PHP 7作為WebServer,這樣的集成環境有XAMPP、WAMPP、EasyPHP。其中,只有EasyPHP同樣也可以采用Nginx搭配PHP 7使用的方式作為WebServer,并支持WebServer在Nginx、Apache之間的輕松轉換。
XAMPP軟件還可以運行在Linux與MacOS上,而WAMPP與EasyPHP只能運行在Windows環境中,相比之下,更推薦支持Nginx的EasyPHP。關于WebServer軟件,本文推薦使用Nginx。
Nginx的Windows版本可以從http://nginx,org/en/download.html 下載,雖然用其他主線版本沒有什么問題,但是更推薦使用穩定版本。PHP的Windows版本可以從http://windows.php.net/download/下載,下載與系統匹配的32位或者64位的非線程安全版本。
我們可以根據自己的實際情況選擇集成環境,但是我們應該更深入掌握WebServer的每一個細節,所以我們獨立安裝Nginx(或者Apache)、PHP 7、MySQL,并設置好他們之間的連接,手動將它們配置到一起。
此處以Nginx為例:
- 根據上面提供的地址下載Nginx、PHP的二進制程序,復制Nginx到一個自定義的目錄中,再將PHP復制到Nginx目錄下,或是找一個固定的目錄存放PHP。
- 在PHP目錄下,有兩個.ini文件,分別是php.ini-development與php.ini-production,選擇其中之一并改名為php.ini,之后PHP會以這個文件作為配置文件。
按住shift鍵并用鼠標右擊PHP目錄,打開命令行窗口,輸入以下命令:php-cgi -b 127.0.0.1:9000
此處參數-b用于告知PHP我們希望啟動FastCGI服務,執行上述命令會將綁定PHP到127.0.0.1這個IP地址的9000端口上,至此PHP便運行起來了。
配置Nginx,打開nginx_folder/conf/nginx.conf文件,首先在配置文件的服務器配置信息中添加根目錄和默認首頁文件,具體如下:server { root html; index index.php index.html index.htm;
現在配置Nginx,讓它在啟動時通過FastCGI模式與PHP通信。在nginx.conf中配置,去掉下面這塊配置的注釋即可啟用:location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_param SCRIPT_FILENAME complete_path_webroot_folder$fastcgi_script_name;include fastcgi_params;
注意:complete_path_webroot_folder$fastcgi_script_name路徑的默認值是nginx目錄下的一個HTML的目錄。另外fastcgi_param配置中的符號“\”需要用符號“/”加以替換。
在Nginx目錄下,通過以下命令重啟Nginx:nginx -s restart,重啟之后,打開瀏覽器,輸入Windows服務器,輸入Windows服務器、設備的IP地址或主機名,即可看到Nginx歡迎頁面。
- 到這一步,就只需驗證PHP的安裝情況以及它與Nginx的工作情況,在Webroot目錄下創建一個info.php文件,并且編輯代碼,在瀏覽器其中訪問對應的服務器,驗證是否能夠正確解析這個文件。
PHP 7新特性
PHP 7具有很多編寫高性能、高效代碼的新特性,同時也移除了一些歷史版本中過時的特性,這些過時特性如果在PHP 7中使用會觸發一個Error錯誤。目前大多數的Fatal錯誤都可以異常捕獲,所以PHP不再顯示一些不標準的Fatal錯誤,取而代之的是拋出一個攜帶著很多可用信息的異常。PHP 7提供了一些OOP新特性。
類型聲明
老版的PHP在函數和類之間傳遞參數時不必聲明變量類型,同樣返回數據時也不必聲明變量類型,任何數據都可以被傳遞。這樣會造成PHP不清楚傳遞的是什么類型的變量,函數和方法接收的變量數據類型也是未知的。因為PHP 7引入形參和返回值的類型聲明。
自己的理解:其實最開始PHP不需要類型聲明就是為了簡化編程,那么處理過程就會更復雜,這可能就是一個性能與編程簡便的一個權衡。類型聲明是針對大規模應用場景的一個升級,這就是一個語言成長的必經之路吧。
形參聲明:PHP 7支持的形參類型聲明的類型有整型、浮點型、字符串型、布爾類型,可用于函數形參及對象的方法形參上。例如:public function age(int $age) { // 函數體}
返回值聲明:類似于形參聲明,注意類型也可以是某個類的對象,例如:public function age(int $age) : string { // 函數體}
命名空間與use關鍵字批量聲明
當代碼量規模變大時,很多類會放在命名空間下,這樣方便維護和管理。然而當出現一個命名空間下有很多類且我們要一個性使用多個類的情況下,我們不得不直接將它們聲明在代碼的頂部。在命名空間下聲明類如下所示:
| 1 2 3 4 | $book = new Publichers\Packt\Book(); $ebook = new Publichers\Packt\Ebook(); $video = new Publichers\Packt\Video(); $Presentation = new Publichers\Packt\Presentation(); |
加載類的方式有兩種:
- 通過include聲明顯示要加載的類。
- 通過_autoload函數來加載所有的類文件。
PHP 7引入批量的use聲明,用以增加代碼的可讀性,其中包括三種聲明模式:
非混合模式的use聲明
1
2
3use Publichers\Packt\{ Book, Ebook, Video, Presentation };
use function Publichers\Packt\{ getBook ,savebook };
use const Publichers\Packt\{ COUNT,KEY };混合模式的use聲明
1
2
3
4
5
6
7
8
9
10use Publichers\Packt\{
Book,
Ebook,
Video,
Presentation,
function getBook;
function saveBook;
const COUNT,
const KEY
};復合模式的use聲明
1
2
3
4
5
6use Publichers\Packt\{
Paper\Book,
Electronic\Ebook,
Media\Video,
Media\Presentation
};
匿名類的聲明與使用同時進行,它具備其他類所具備的所有功能,差別在于匿名類沒有類名。匿名類的一次性小任務代碼流程對性能提升幫助很大,你不必將整個類寫完后再使用它。匿名類的語法如下:
| 1 2 3 4 5 6 7 | new class(argument) { definition }; //示例如下 $name = new class() { public function _construct() { // 此處為構造函數 echo 'Altaf Huassain'; } }; |
雖然匿名類是沒有命名的,但是PHP內部,會在內存的引用地址表中為其分配一個全局唯一的名稱。匿名類在繼承方面與命名類相同,一樣可以繼承父類及父類的方法和接口,方式與繼承命名類相同。
老式構造函數的摒棄
從PHP 4開始,構造函數便可以通過命名保持與類名一致的方式來聲明自己是構造函數,這種方式一種被沿用至PHP 5.6.大師在PHP 7中,這種構造函數的聲明方式不推薦使用。
老式構造方法:
| 1 2 3 4 5 6 | class Packt { protected $number; public function packt() { // 此處為構造函數 echo 'Altaf Huassain'; } }; |
新的構造方法:
| 1 2 3 4 5 6 | class Packt { protected $number; public function _construct() { // 此處為構造函數 echo 'Altaf Huassain'; } }; |
注意:老式的構造函數聲明方式在PHP 7中被使用,只是會產生不推薦的信息,但是一般這類不推薦的方式在接下來的版本中會被移除,所以不建議使用老版構造方法。在PHP 7中同時出現老版構造函數和新方法,新方法的構造函數會被調用,而老版構造函數不會被調用。
Throwable接口
PHP 7提供一種全局的接口,使得所有的類都可以基于此使用throw關鍵字。在PHP 7之前異常可以被截獲,但是錯誤不能被截獲。從PHP 7之后,任何完整程序或者一部分中的Fatal錯誤可以被截獲。為了更好地截獲諸多的錯誤(大多數Fatal錯誤),PHP 7提供了throwable接口,異常與錯誤都繼承這個接口。
自定義的PHP類是不能繼承throwable接口的,如果希望繼承throwable接口,需要繼承某個異常類。
太空飛船操作符(<=>)
太空飛船操作符在比較變量(包括字符串型、整型、浮點型、數組、對象等)時比較有用,太空飛船操作符將(<、==、>)
具體的使用規則如下:
- 當符號兩邊相等時返回0
- 當符號右邊大于符號左邊時返回-1
- 當符號右邊小于符號左邊時返回1
null合并運算符(??)
在PHP 7中推薦使用合并運算符,在第一操作數存在時可被直接返回第二操作數,具體使用方法如下:
| 1 2 3 | $post = $_POST['title'] ?? NULL; // 等效于老版的PHP的如下語句: $post = ($_POST['title']) ? $_POST['title'] : NULL; |
合并運算符可以連續使用:
| 1 | $post = $_POST['title'] ?? $_GET['title'] ?? 'No POST or GET'; |
統一變量語法
通過花括號來規定變量解析的優先級,針對以下情況:
| 1 2 3 4 5 | $first = ['name' => 'second']; $second = 'Howdy'; echo $$first['name']; //PHP 5.X版本,這種方式與從左到右的變量解析的原則($$first會優先被解析)產生不一致。這樣的方法在PHP版本中會產生錯誤。 // 統一變量語法: echo ${$first['name']}; |
其他特性與變更
PHP 7還更新了很多其他特性,例如常量數組、switch循環中的多個默認值、session_start中的選項數組等。
常量數組:
PHP 5.6開始,常量數組可以使用const關鍵字來聲明,方法如下:
1 const STORES = ['en', 'fr', 'ar' ];
define('STORES', ['en', 'fr', 'ar' ];
switch中的多個default默認值:PHP 7開始,switch循環語句中不允許出現多個default默認值,否則會出現Fatal錯誤:Fatal error: Switch statements may only contain one default clause in ...
Session_start函數中的選項數組
在PHP 7之前,使用session時,必須先調用session_start()函數,這個函數并沒有參數要傳遞,所有session相關的配置都在php,ini文件中。從PHP 7開始,可以在調用函數時傳遞參數選項數組,這些設置信息將覆蓋php.ini中的session配置,且實參部分傳遞的選項數組將優先于php.ini中的session配置而被使用,代碼示例如下:1
2
3
4session_start([
'cookie_lifetime' => 3600,
'read_and_close' => true
]);Unserialize函數引入過濾器
通常使用serialize和unserialize兩個方法分別對對象進行序列化和反序列化。然而,unserialize()函數并不安全,因為沒有任何過濾項,并且可以反序列化任何類型的對象。因此PHP 7引入過濾器,默認情況下允許反序列化所有類型的對象。使用方法如下:1 $result = unserilize($object, ['allowed_classes' => ['Packt', 'Books', 'Ebooks']]);
PHP 7應用性能提升
為了提升性能,PHP 7已經完全基于PHPNG進行重寫。不過依然有很多其他的方法可以用來進一步提升PHP 7的性能,譬如高性能的代碼、采用最佳實踐、WebServer調優、緩存等。
Nginx與Apache
目前有很多HHTP Server軟件可供使用,目前最流行的是Nginx與Apache。
Apache
Apache具有足夠靈活、廣泛支持、能力強化、以模塊方式集成大多數語言(如PHP)的優點。因為Apache是在進程內部解析大多數腳本語言的,所以沒有軟件間通信的開銷。Apache處理請求的模式有三種:prefork模式(線程創建進程)、worker模式(進程創建線程)、事件驅動模式(與worker模式相似,但這種模式會為連接保持創建專用線程,活動請求使用另外創建的線程),因此它能提供更高的靈活度。由于每個請求都會由一個進程或者線程處理,所有Apache在處理請求時開銷很大。當它應用于高并發場景時,其性能低下的問題就凸顯出來了。
Nginx
Nginx提供的異步、事件驅動、非阻塞請求處理。Nginx不必等待每個請求完成,避免了鎖住資源。Nginx創建許多工作進程,每個工作進程可以處理數千個鏈接,因此可以使用很少的進程來承載高并發流量。
Nginx沒有內置任何解釋性語言,這樣Nginx就可以專注處理請求的接收和響應,而具體的解析腳本語言的進程則在Nginx之外。
通常認為Nginx快于Apache,但是在如靜態資源(圖片、css、js等文件)場景下,Apache有自己的優勢。在構建高性能服務器時,Apache并不是問題所在PHP才是真正的瓶頸。
HTTP服務器性能優化
每款HTTP Server程序提供的功能,都可以實現優化請求處理和服務內容。優化HTTP Server程序的性能和可擴展性的技術包括:
緩存靜態文件
將靜態文件(如圖片、css文件、js文件、字體文件等變更不頻繁的文件)緩存在用戶設備上。要實現這樣的效果,Web Server程序需要添加特殊的響應頭信息,以便讓用戶在瀏覽內容的同時將靜態內容緩存到用戶設備上。
HTTP持久鏈接(HTTP Keep-alive技術)
HTTP持久鏈接表示一條TCP/IP鏈接上承載著過多個上下行請求。相比于傳統的單鏈接模式(一次請求需要創建一條單獨的BS鏈接的模式)而言,HTTP Keep-alive技術有大幅度性能提升。優點如下:
- CPU和內存的負載會減輕。因為同一時刻的打開的TCP鏈接數變少了,后續請求和響應無需打開新鏈接,可以繼續基于這些TCP鏈接發送上下行請求。
- 當TCP鏈接建立后,請求的等待時間就會減少。
網絡阻塞情況減輕。因為在同一時刻,只會有少數的鏈接保持著。
不足:許多服務器有并發數限制,當并發數上升到一定程度時,程序的性能將大大降低。為了解決這個問題,設置鏈接超時時間便非常必要。設置之后,超過設定時間的鏈接將會自動關閉。
GZIP壓縮
將網絡中傳輸的內容進行壓縮后再傳遞,可以有效減輕傳輸負擔,從而提升HTTP請求的響應速度。Apache和Nginx都支持GZIP壓縮,大部分的瀏覽器也都支持GZIP壓縮數據的解析。
- PHP獨立部署服務
Apache是以mod_php模塊的方式加載PHP的,在這種方式,PHP與Apache耦合的很緊,所有的請求都會由Apache模塊處理,這會非常消耗機器的硬件資源。我們可以通過讓PHP-FPM與Apache結合,它們都獨立部署,通過FastCGI協議互相傳遞數據。這樣Apache只需關注HTTP請求鏈接即可,PHP進程則由PHP-FPM創建與維護。
Nginx本身就是與PHP相互獨立的。
當PHP獨立服務時,Web服務器不必對動態內容進行處理,僅僅將請求轉發給另外一個服務即可,這大大減輕Web服務器的負載能力。
關閉不用的模塊
Apache與逐個Nginx都有許多默認攜帶的模塊,大多數情況下是用不到的,因此最好的方式是關閉不用的模塊,方法:1)先將所有的模塊禁用并重啟服務器,然后逐個開啟并檢查應用程序是否運行正常;2)將所有模塊默認全部開啟。然后逐個關閉并檢查應用程序是否運行正常,剔除不用的模塊。通過這個方法可以做一個列表標識 啟用或禁用的模塊。
Web服務器資源
每個Web服務器都會默認一些全局配置以供使用,然而這些設置可能并沒有完美匹配服務器硬件情況,最大的硬件方面的問題是RAM內存,內存越多,服務器可處理的請求就越多。
內容分布網絡(CDN)
CDN網絡通常服務于媒體文件,例如圖片文件、css文件、js文件和音視頻文件,這些文件會被緩存在各地的服務器上,這些服務器在地域上足夠分散,當收到請求時,CDN網絡會選擇最適合用戶的最近節點,將內容下發給用戶。
CDN網絡的特性:
- 由于內容是靜態的,不頻繁更改,因此CDN會將他們緩存在內存中。當某個文件的請求到達時,CDN直接從緩存中發送文件,這比從磁盤中加載文件并將其發送到瀏覽器更快。
- CDN服務器位于不同的位置。
- 每個瀏覽器都具有向域發送同時請求的限制。
- 通常,存在對動態內容的少量請求和對靜態內容的更多請求。
CDN根據客戶的位置決定了內容的地理可用性,緩存廣泛,優化網絡中的文件傳輸。
CSS與JavaScript優化
每個Web應用程序都會有CSS和JavaScript文件,現在大多數應用程序都包含很多css和js文件,用來增強應用的粘度和互動效果。css和js文件越大,瀏覽器需要發送的請求就越多,從而容易影響其性能。針對css和js文件的優化方法是:
- 合并:將所有的css和js文件分別合并為一個文件。
- 縮小:刪除css和js文件中所有空行、注釋和格外空格,這樣能夠減小文件遲勛,提高文件加載速度。
Minify是一組完全使用PHP編寫的庫,支持CSS與JavaScript文件的合并與壓縮,代碼完全面對對象和命名空間,可以將其嵌入開發框架中。下載地址,注意minify依賴同一作者寫的路徑轉換庫,
Grunt:是一個JavaScript任務運行器,它能夠將某些重復的任務自動化,避免反復工作。注意Grunt的安裝需要Node.js和npm。
全頁緩存技術
網站的完整頁面存儲在緩存正,為下一次請求提供此緩存頁面。如果網站的內容不經常更改,全頁緩存的效果更好。
Varnish:開源Web應用程序加速器,在Web服務器軟件之前運行,但是它必須配置在端口80上,這樣才能使每個請求到達。
基礎設施:
在應用程序流量一次性增加到上千萬的用戶,若在單個服務器上運行,性能受到很大的影響。針對程序的可擴展性和可用性,可在多個服務器上部署程序。構成部分:
- 負載均衡(LB):根據每個Web服務器上的負載情況,將外網流量以一定的規則分發給Web服務器。
- Web服務器:可以根據需要盡可能多地部署Web服務器,它們可以很輕易地連接到LB。
- 數據庫服務器:用于安裝數據庫。
提升數據庫性能
數據庫在動態網站中扮演著一個關鍵的角色,所有流入流出數據杜輝與數據庫進行交互。因此,如果PHP應用數據庫沒有進行較好的設計或優化,其性能將會受到非常大的影響。
MySQL數據庫
查詢緩存(Query Caching)
存儲引擎
存儲引擎(又稱表類型)是MySQL的核心部分,負責處理表的操作。MySQL提供9個存儲引擎,使用最廣泛的是MyISAM和InnoDB。
MyISAM
- 為速度而設計,和Select搭配起來使用更好
- 如果表的數據偏向于靜態,使用MyISAM最好。
- MyISAM支持表級鎖。
- MyISAM支持全文搜索。
- MyISAM數據壓縮、自我復制、查詢緩存、數據加密。
- MyISAM不支持外鍵。
- MyISAM不支持事務。
- MyISAM支持集群數據庫。
InnoDB(默認存儲引擎)
- InnoDB是為高可靠性和高性能而設計的,適合處理大量數據。
- InnoDB支持行級鎖。
- InnoDB支持外鍵。
- InnoDB支持事務。
- InnoDB數據壓縮、自我復制、查詢緩存、數據加密。
- InnoDB可以用在集群環境下,但是并沒有完全支持,不過InnoDB表可以完全轉換為NDB存儲引擎,這樣既可用于集群環境。
Percona數據庫和Percona XtraDB存儲引擎
- Percona是免費、開源的數據庫,對于MySQL完全兼容且提供加強功能,可完全代替MySQL并能提供更好的文檔、性能、擴展性。
- Percona由MySQL衍生,支持MySQL的所有特性,并在此基礎上提供更多、更好的性能。Percona使用一種改進的存儲引擎——XtraDB,它是InnoDB的加強版,有更多的特性和更快的速度,在現代硬件上有有著更好的擴展性,Percona XtraDB在高負載環境下使用內存的效率更高。
Percona只能在Linux系統上使用,目前不能在Windows系統上使用。具體安裝可參考Percona安裝手冊
MySQL性能監控數據:PHPMyAdmin的status展示MySQL服務器的相關信息和一些統計。
Redis鍵值緩存存儲
Memcached鍵值緩存存儲
調試和分析
調試過程中,需要知道一個腳本程序消耗了多少資源,包括內存消耗、CPU以及執行時間。Xdebug是一種PHP擴展,為PHP腳本提供調試和分析信息,能夠得到錯誤的 全部追蹤信息。同時Xdebug提供與IDE的交互式調試腳本的能力。
與Subline text 3,可安裝Xdebug client插件
PHP編程最佳實踐
Composer:PHP依賴管理工具。
Git:代碼托管和版本控制。
Grunt watch:JavaScript任務運行器。
參考:
總結
以上是生活随笔為你收集整理的《高性能PHP》学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: uvm_reg_predictor——寄
- 下一篇: 动态规划算法php,php算法学习之动态