php多進(jìn)程單線(xiàn)程之php-cgi枫吧、php-fpm
php從代碼級(jí)別的執(zhí)行上是單線(xiàn)程的, 但是由php-fpm進(jìn)程管理機(jī)制是多進(jìn)程單線(xiàn)程的, 也就是php是多進(jìn)程執(zhí)行的. 有效提高并發(fā)的響應(yīng)效率。
了解關(guān)于cgi宇色、fastCGI九杂、php-cgi、php-fpm的概念更能加深理解宣蠕。
下面是我查閱大量資料后整理的關(guān)系圖供大家參考例隆,也歡迎留言一起討論
1. CGI
CGI全稱(chēng)是“公共網(wǎng)關(guān)接口”(Common Gateway Interface),HTTP服務(wù)器與你的或其它機(jī)器上的程序進(jìn)行“交談”的一種工具抢蚀,其程序須運(yùn)行在網(wǎng)絡(luò)服務(wù)器上(在fastcig未使用前是運(yùn)行在網(wǎng)絡(luò)服務(wù)器上的镀层,具體請(qǐng)看下文)。CGI可以用任何一種語(yǔ)言編寫(xiě)皿曲,只要這種語(yǔ)言具有標(biāo)準(zhǔn)輸入唱逢、輸出和環(huán)境變量吴侦。如php,perl,tcl等。cgi是一個(gè)web server與cgi程序(這里可以理解為是php解釋器)之間進(jìn)行數(shù)據(jù)傳輸?shù)膮f(xié)議坞古,保證了傳遞的是標(biāo)準(zhǔn)數(shù)據(jù)备韧。
2. FastCGI
FastCGI像是一個(gè)常駐(long-live)型的CGI,它可以一直執(zhí)行著绸贡,只要激活后盯蝴,不會(huì)每次都要花費(fèi)時(shí)間去fork一次(這是CGI最為人詬病的fork-and-execute 模式)毅哗。它還支持分布式的運(yùn)算听怕,即 FastCGI 程序可以在網(wǎng)站服務(wù)器以外的主機(jī)上執(zhí)行并且接受來(lái)自其它網(wǎng)站服務(wù)器來(lái)的請(qǐng)求。
FastCGI是語(yǔ)言無(wú)關(guān)的虑绵、可伸縮架構(gòu)的CGI開(kāi)放擴(kuò)展尿瞭,其主要行為是將CGI解釋器進(jìn)程保持在內(nèi)存中并因此獲得較高的性能。眾所周知翅睛,CGI解釋器的反復(fù)加載是CGI性能低下的主要原因声搁,如果CGI解釋器保持在內(nèi)存中并接受FastCGI進(jìn)程管理器調(diào)度,則可以提供良好的性能捕发、伸縮性疏旨、Fail- Over特性等等
2.1 FastCGI特點(diǎn)
FastCGI具有語(yǔ)言無(wú)關(guān)性.
FastCGI在進(jìn)程中的應(yīng)用程序,獨(dú)立于核心web服務(wù)器運(yùn)行扎酷,提供了一個(gè)比API更安全的環(huán)境檐涝。APIs把應(yīng)用程序的代碼與核心的web服務(wù)器鏈接在一起,這意味著在一個(gè)錯(cuò)誤的API的應(yīng)用程序可能會(huì)損壞其他應(yīng)用程序或核心服務(wù)器法挨。 惡意的API的應(yīng)用程序代碼甚至可以竊取另一個(gè)應(yīng)用程序或核心服務(wù)器的密鑰谁榜。
FastCGI技術(shù)目前支持語(yǔ)言有:C/C++、Java凡纳、Perl窃植、Tcl、Python荐糜、SmallTalk巷怜、Ruby等。相關(guān)模塊在A(yíng)pache, ISS, Lighttpd等流行的服務(wù)器上也是可用的暴氏。
FastCGI的不依賴(lài)于任何Web服務(wù)器的內(nèi)部架構(gòu)丛版,因此即使服務(wù)器技術(shù)的變化, FastCGI依然穩(wěn)定不變。
2.2 FastCGI的工作原理
Web Server啟動(dòng)時(shí)載入FastCGI進(jìn)程管理器(IIS ISAPI或Apache Module)
FastCGI進(jìn)程管理器自身初始化偏序,啟動(dòng)多個(gè)CGI解釋器進(jìn)程(可見(jiàn)多個(gè)php-cgi)并等待來(lái)自Web Server的連接页畦。
當(dāng)客戶(hù)端請(qǐng)求到達(dá)Web Server時(shí),F(xiàn)astCGI進(jìn)程管理器選擇并連接到一個(gè)CGI解釋器研儒。Web server將CGI環(huán)境變量和標(biāo)準(zhǔn)輸入發(fā)送到FastCGI子進(jìn)程php-cgi豫缨。
FastCGI子進(jìn)程完成處理后將標(biāo)準(zhǔn)輸出和錯(cuò)誤信息從同一連接返回Web Server独令。當(dāng)FastCGI子進(jìn)程關(guān)閉連接時(shí),請(qǐng)求便告處理完成好芭。FastCGI子進(jìn)程接著等待并處理來(lái)自FastCGI進(jìn)程管理器(運(yùn)行在Web Server中)的下一個(gè)連接燃箭。 在CGI模式中,php-cgi在此便退出了舍败。
在上述情況中招狸,你可以想象CGI通常有多慢。每一個(gè)Web請(qǐng)求PHP都必須重新解析php.ini邻薯、重新載入全部擴(kuò)展并重初始化全部數(shù)據(jù)結(jié)構(gòu)裙戏。使用FastCGI,所有這些都只在進(jìn)程啟動(dòng)時(shí)發(fā)生一次厕诡。一個(gè)額外的好處是累榜,持續(xù)數(shù)據(jù)庫(kù)連接(Persistent database connection)可以工作。
2.3 FastCGI的不足
因?yàn)槭嵌噙M(jìn)程灵嫌,所以比CGI多線(xiàn)程消耗更多的服務(wù)器內(nèi)存壹罚,PHP-CGI解釋器每進(jìn)程消耗7至25兆內(nèi)存,將這個(gè)數(shù)字乘以50或100就是很大的內(nèi)存數(shù)寿羞。
Nginx 0.8.46+PHP 5.2.14(FastCGI)服務(wù)器在3萬(wàn)并發(fā)連接下猖凛,開(kāi)啟的10個(gè)Nginx進(jìn)程消耗150M內(nèi)存(15M10=150M),開(kāi)啟的64個(gè)php-cgi進(jìn)程消耗1280M內(nèi)存(20M64=1280M)绪穆,加上系統(tǒng)自身消耗的內(nèi)存辨泳,總共消耗不到2GB內(nèi)存。如果服務(wù)器內(nèi)存較小霞幅,完全可以只開(kāi)啟25個(gè)php-cgi進(jìn)程漠吻,這樣php-cgi消耗的總內(nèi)存數(shù)才500M。
上面的數(shù)據(jù)摘自Nginx 0.8.x + PHP 5.2.13(FastCGI)搭建勝過(guò)Apache十倍的Web服務(wù)器(第6版)
3. PHP-CGI
php-cgi 是 php 的解釋器司恳,就是上文提到的cgi程序途乃。
關(guān)于PHP-CGI是PHP自帶的FastCGI管理器的說(shuō)法。
我是這么理解的:第一:CGI解釋器(php-cgi)保持在內(nèi)存中并接受FastCGI進(jìn)程管理器調(diào)度扔傅,則可以提供良好的性能耍共、伸縮性、Fail- Over特性等等猎塞,也就是在fast-cgi層面是管理著多個(gè)CGI程序(php-cgi)的進(jìn)程的.第二:fpm官方的解釋是:FPM(FastCGI 進(jìn)程管理器)用于替換 PHP FastCGI 的大部分附加功能试读,對(duì)于高負(fù)載網(wǎng)站是非常有用的(https://www.php.net/manual/zh/install.fpm.php)。
所以我不認(rèn)同PHP-CGI是PHP自帶的FastCGI管理器的說(shuō)法荠耽。
3.1 PHP-CGI 的不足
php-cgi變更php.ini配置后需重啟php-cgi才能讓新的php-ini生效钩骇,不可以平滑重啟。
直接殺死php-cgi進(jìn)程,php就不能運(yùn)行了倘屹。(PHP-FPM和Spawn-FCGI就沒(méi)有這個(gè)問(wèn)題银亲,守護(hù)進(jìn)程會(huì)平滑從新生成新的子進(jìn)程。)
4. PHP-FPM(php Fastcgi Process Manager)
PHP-FPM是一個(gè)PHP FastCGI管理器纽匙,是只用于PHP的务蝠,可以在 http://php-fpm.org/download下載得到。
PHP-FPM其實(shí)是PHP源代碼的一個(gè)補(bǔ)丁烛缔,旨在將FastCGI進(jìn)程管理整合進(jìn)PHP包中馏段。必須將它patch到你的PHP源代碼中,在編譯安裝PHP后才可以使用践瓷。
現(xiàn)在我們可以在最新的PHP 5.3.2的源碼樹(shù)里下載得到直接整合了PHP-FPM的分支院喜,據(jù)說(shuō)下個(gè)版本會(huì)融合進(jìn)PHP的主分支去。相對(duì)Spawn-FCGI当窗,PHP-FPM在CPU和內(nèi)存方面的控制都更勝一籌够坐,而且前者很容易崩潰寸宵,必須用crontab進(jìn)行監(jiān)控崖面,而PHP-FPM則沒(méi)有這種煩惱。
PHP5.3.3已經(jīng)集成php-fpm了梯影,不再是第三方的包了巫员。PHP-FPM提供了更好的PHP進(jìn)程管理方式,可以有效控制內(nèi)存和進(jìn)程甲棍、可以平滑重載PHP配置简识,比spawn-fcgi具有更多有點(diǎn),所以被PHP官方收錄了感猛。在./configure的時(shí)候帶 –enable-fpm參數(shù)即可開(kāi)啟PHP-FPM七扰。
5 PHP對(duì)并發(fā)訪(fǎng)問(wèn)的處理
5.1 進(jìn)程和線(xiàn)程
PHP從代碼級(jí)別來(lái)講不支持多線(xiàn)程操作,不能像Java陪白、C#等語(yǔ)言一樣可以編寫(xiě)多線(xiàn)程代碼颈走。但多線(xiàn)程和并發(fā)沒(méi)有直接關(guān)系,多線(xiàn)程只是代碼被運(yùn)行時(shí)在同一時(shí)間同時(shí)執(zhí)行多個(gè)線(xiàn)程任務(wù)咱士,來(lái)提高服務(wù)器CPU的利用率立由,提高代碼效率。但php是可以多進(jìn)程執(zhí)行的序厉,上文所述的FPM進(jìn)程管理機(jī)制就是多進(jìn)程單線(xiàn)程的锐膜,有效提高了并發(fā)訪(fǎng)問(wèn)的響應(yīng)效率。
5.2 簡(jiǎn)單的web server + php-fpm 模式
- 當(dāng)客戶(hù)端發(fā)送一個(gè)請(qǐng)求時(shí)弛房,web server會(huì)通過(guò)一個(gè)php-fpm進(jìn)程(這里和下文所說(shuō)指的fpm進(jìn)程都是fpm開(kāi)啟的worker進(jìn)程道盏,關(guān)于fpm的工作原理這里不再累述)去執(zhí)行php代碼,php代碼的執(zhí)行是單線(xiàn)程的。
- 那么荷逞,當(dāng)有多個(gè)客戶(hù)端同時(shí)發(fā)送請(qǐng)求時(shí)(并發(fā))牺堰,web server會(huì)通過(guò)php-fpm為每個(gè)請(qǐng)求開(kāi)啟一個(gè)單獨(dú)進(jìn)程去執(zhí)行php代碼。
- 請(qǐng)求執(zhí)行過(guò)后颅围,空閑的php-fpm進(jìn)程被銷(xiāo)毀伟葫,內(nèi)存得以釋放。
- 但并發(fā)的問(wèn)題在于院促,在某一時(shí)間筏养,客戶(hù)端請(qǐng)求讓php-fpm進(jìn)程數(shù)量達(dá)到了最大限制數(shù),這個(gè)時(shí)候常拓,新來(lái)的請(qǐng)求只能等待空閑的php-fpm進(jìn)程來(lái)處理渐溶,這就是多進(jìn)程同步阻塞模式的弊端,當(dāng)然還有進(jìn)程過(guò)多所帶來(lái)的內(nèi)存占用問(wèn)題弄抬。
6茎辐。 高并發(fā)和多線(xiàn)程
“高并發(fā)和多線(xiàn)程”總是被一起提起,給人感覺(jué)兩者好像相等掂恕,實(shí)則 高并發(fā) ≠ 多線(xiàn)程拖陆。多線(xiàn)程是完成任務(wù)的一種方法,高并發(fā)是系統(tǒng)運(yùn)行的一種狀態(tài)懊亡,通過(guò)多線(xiàn)程有助于系統(tǒng)承受高并發(fā)狀態(tài)的實(shí)現(xiàn)依啰。
高并發(fā)是一種系統(tǒng)運(yùn)行過(guò)程中遇到的一種“短時(shí)間內(nèi)遇到大量操作請(qǐng)求”的情況,主要發(fā)生在web系統(tǒng)集中大量訪(fǎng)問(wèn)或者socket端口集中性收到大量請(qǐng)求(例如:12306的搶票情況店枣;天貓雙十一活動(dòng))速警。該情況的發(fā)生會(huì)導(dǎo)致系統(tǒng)在這段時(shí)間內(nèi)執(zhí)行大量操作,例如對(duì)資源的請(qǐng)求鸯两,數(shù)據(jù)庫(kù)的操作等闷旧。如果高并發(fā)處理不好,不僅僅降低了用戶(hù)的體驗(yàn)度(請(qǐng)求響應(yīng)時(shí)間過(guò)長(zhǎng))钧唐,同時(shí)可能導(dǎo)致系統(tǒng)宕機(jī)忙灼,嚴(yán)重的甚至導(dǎo)致OOM異常,系統(tǒng)停止工作等逾柿。
如果要想系統(tǒng)能夠適應(yīng)高并發(fā)狀態(tài)缀棍,則需要從各個(gè)方面進(jìn)行系統(tǒng)優(yōu)化,包括机错,硬件爬范、網(wǎng)絡(luò)、系統(tǒng)架構(gòu)弱匪、開(kāi)發(fā)語(yǔ)言的選取青瀑、數(shù)據(jù)結(jié)構(gòu)的運(yùn)用璧亮、算法優(yōu)化、數(shù)據(jù)庫(kù)優(yōu)化……而多線(xiàn)程只是其中解決方法之一斥难。實(shí)現(xiàn)高并發(fā)需要考慮:
1. 系統(tǒng)的架構(gòu)設(shè)計(jì)枝嘶,如何在架構(gòu)層面減少不必要的處理(網(wǎng)絡(luò)請(qǐng)求,數(shù)據(jù)庫(kù)操作等)
2. 網(wǎng)絡(luò)拓?fù)鋬?yōu)化減少網(wǎng)絡(luò)請(qǐng)求時(shí)間哑诊、如何設(shè)計(jì)拓?fù)浣Y(jié)構(gòu)群扶,分布式如何實(shí)現(xiàn)?
3. 系統(tǒng)代碼級(jí)別的代碼優(yōu)化镀裤,使用什么設(shè)計(jì)模式來(lái)進(jìn)行工作竞阐?哪些類(lèi)需要使用單例,哪些需要盡量減少new操作暑劝?
4. 提高代碼層面的運(yùn)行效率骆莹、如何選取合適的數(shù)據(jù)結(jié)構(gòu)進(jìn)行數(shù)據(jù)存取担猛?如何設(shè)計(jì)合適的算法幕垦?
任務(wù)執(zhí)行方式級(jí)別的同異步操作,在哪里使用同步傅联,哪里使用異步先改?
5. JVM調(diào)優(yōu),是以server模式還是以clien模式運(yùn)行纺且,如何設(shè)置Heap盏道、Stack稍浆、Eden的大小载碌,如何選擇GC策略,控制Full GC的頻率?
6. 數(shù)據(jù)庫(kù)優(yōu)化減少查詢(xún)修改時(shí)間衅枫。數(shù)據(jù)庫(kù)的選燃尥А?數(shù)據(jù)庫(kù)引擎的選认伊谩步咪?數(shù)據(jù)庫(kù)表結(jié)構(gòu)的設(shè)計(jì)?數(shù)據(jù)庫(kù)索引益楼、觸發(fā)器等設(shè)計(jì)猾漫?是否使用讀寫(xiě)分離?還是需要考慮使用數(shù)據(jù)倉(cāng)庫(kù)感凤?
7. 緩存數(shù)據(jù)庫(kù)的使用悯周,如何選擇緩存數(shù)據(jù)庫(kù)?是Redis還是Memcache? 如何設(shè)計(jì)緩存機(jī)制陪竿?
8. 數(shù)據(jù)通信問(wèn)題禽翼,如何選擇通信方式?是使用TCP還是UDP,是使用長(zhǎng)連接還是短連接闰挡?NIO還是BIO锐墙?netty、mina還是原生socket长酗?
9. 操作系統(tǒng)選取溪北,是使用winserver還是Linux?或者Unix夺脾?
10 硬件配置刻盐?是8G內(nèi)存還是32G,網(wǎng)卡10G還是1G?
...
以上的這些問(wèn)題在高并發(fā)中都是必須要深入考慮的劳翰,就像木桶原理一樣敦锌,只要其中的某一方面沒(méi)有考慮到,都會(huì)造成系統(tǒng)瓶頸佳簸,影響整個(gè)系統(tǒng)的運(yùn)行乙墙。而高并發(fā)問(wèn)題不僅僅涉及面之廣,同時(shí)又要求有足夠的深度IL搿!
而多線(xiàn)程在這里只是在同/異步角度上解決高并發(fā)問(wèn)題的其中的一個(gè)方法手段马胧,是在同一時(shí)刻利用計(jì)算機(jī)閑置資源的一種方式汉买。多線(xiàn)程在解決高并發(fā)問(wèn)題中所起到的作用就是使計(jì)算機(jī)的資源在每一時(shí)刻都能達(dá)到最大的利用率,不至于浪費(fèi)計(jì)算機(jī)資源使其閑置佩脊。
參考:
https://www.php.cn/php-weizijiaocheng-377248.html蛙粘、https://www.cnblogs.com/zuochuang/p/7885941.html、https://www.php.net/manual/zh/install.fpm.php威彰、https://www.cnblogs.com/itxiongwei/p/9072075.html