進程
什么是進程
進程Process
是計算機中的程序關(guān)于某數(shù)據(jù)集合上的一次運行活動,是系統(tǒng)分配資源和調(diào)度的基本單位祭刚,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ)预柒。在早期面向進程設(shè)計的計算機結(jié)構(gòu)中,進程是程序的基本執(zhí)行實體袁梗。在當代面向線程設(shè)計的計算機結(jié)構(gòu)中宜鸯,進程是線程的容器。簡單來說遮怜,程序是指令淋袖、數(shù)據(jù)以及其組織形式的描述,而進程則是程序的實體锯梁。
在操作系統(tǒng)中即碗,進程表示正在運行的程序焰情,例如在終端中使用PHP命令運行PHP腳本,此時就相當于創(chuàng)建了一個進程剥懒,這個進程會在系統(tǒng)中駐存内舟,申請屬于它自己的內(nèi)存空間和系統(tǒng)資源,并且運行相應的程序初橘。
$ php build.php
<?php
//獲取當前進程的PID
echo posix_getpid();
//修改所在進程的名稱
swoole_set_process_name("swoole process master");
//模擬持續(xù)運行100秒的程序
sleep(100);//持續(xù)運行100秒的目的是為了在進程中可以查看而不至于很快結(jié)束
運行程序
$ php build.php
71
查看進程
$ ps aux | grep 71
root 1 0.0 0.1 18188 1712 pts/0 Ss+ 11:07 0:00 /bin/bash
root 71 0.0 3.0 340468 30788 pts/2 S+ 13:41 0:00 swoole process master
root 76 0.0 0.0 11112 940 pts/1 S+ 13:42 0:00 grep 71
對于一個進程來說验游,最核心的內(nèi)容可分為兩部分:一部分是它的內(nèi)存,這個內(nèi)存是在創(chuàng)建初始時從系統(tǒng)中分配的保檐,進程中所有創(chuàng)建的變量都會存儲在內(nèi)存環(huán)境中耕蝉。另一部分是上下文環(huán)境, 進程是運行在操作系統(tǒng)中的夜只,對于程序而言垒在,它的運行依賴于操作系統(tǒng)分配的資源、操作系統(tǒng)的狀態(tài)以及程序自身的狀態(tài)扔亥,這些就構(gòu)成了進程的上下文環(huán)境场躯。
父子進程
- 子進程會復制父進程的內(nèi)存空間和上下文環(huán)境
- 子進程會復制父進程的IO句柄即
fd
描述符 - 子進程的內(nèi)存空間與父進程的內(nèi)存空間是獨立,是互不影響的旅挤。
- 修改子進程的內(nèi)存空間并不會修改父進程或其他子進程的內(nèi)存空間
例如:父進程通過fopen
打開文件后得到一個IO句柄fd
推盛,子進程復制父進程后同樣會得到這個fd
。如果父進程和子進程同時對一個文件進行操作谦铃,會造成文件混亂耘成,因此需要加互斥鎖。
例如:父進程中的變量x=1
驹闰,父進程派生子進程后瘪菌,子進程也會存在變量x=1
,但是修改父進程中的變量x
并不會影響子進程的變量x
的值嘹朗。
多進程
PHP是單進程執(zhí)行的师妙,在處理高并發(fā)時主要依賴于Web服務器或PHP-FPM的多進程管理以及進程的復用,但在PHP實現(xiàn)多進程尤其是后臺PHP-CLI模式下處理大量數(shù)據(jù)或運行后臺Deamon守護進程時屹培,多進程的優(yōu)勢自然是最好的默穴。
PHP的多線程也曾被人提及,但進程內(nèi)多線程資源共享和分配問題難以解決褪秀,PHP有一個多線程過的擴展pthreads
蓄诽,它要求PHP環(huán)境必須是線程安全的。
多進程簡單來說就是多個進程同時執(zhí)行多個任務媒吗,可以將耗時但又必須執(zhí)行的查詢分成多個子進程進行操作仑氛。
- PHP多進程不支持PHP-FPM和CGI模式,只能通過PHP-CLI模式。
- PHP多進程適用于定時任務執(zhí)行锯岖,互斥且耗時的任務介袜。
開發(fā)使用PHP多進程的場景也就是使用PHP-FPM,PHP-FPM作為PHP的多進程管理器出吹,當使用Nginx作為WebServer時遇伞,來自客戶端的請求會根據(jù)Nginx的路由配置,將以PHP為后綴的文件轉(zhuǎn)發(fā)給PHP-FPM捶牢。當多個用戶同時請求API時鸠珠,PHP-FPM會開啟多個PHP的處理進程進行處理。
檢查PHP是否支持多進程擴展
$ php -m | grep pcntl
多進程的優(yōu)勢
PHP相比C叫确、C++、Java少了多線程芍锦,PHP中只有多進程的方案竹勉,所以PHP中的全局變量和對象不是共享的,數(shù)據(jù)結(jié)構(gòu)也不能跨進程操作娄琉,另外Socket文件描述符也不能共享...
多線程看似比多進程強大的多次乓,多線程的缺陷也同樣明顯:
- 數(shù)據(jù)同步時,要么犧牲性能到處加鎖孽水,要么使用地獄難度的無鎖并發(fā)編程票腰。
- 當程序邏輯復雜后,鎖會越來越難以控制女气。一旦死鎖杏慰,程序基本上就完了。
- 某個線程掛掉后所有的線程都會退出
相比較多線程炼鞠,多進程擁有的優(yōu)勢是
- 配合進程間通信缘滥,基本可以實現(xiàn)任意數(shù)據(jù)共享。
- 多進程不需要鎖
- 多進程可以共享內(nèi)存的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)一些多線程的功能
對于并發(fā)服務器核心是IO谒主,并非大規(guī)模密集運算朝扼,高并發(fā)的服務器單機能維持10W連接,每秒可以處理3~5W筆消息收發(fā)霎肯。
普通的Web應用都是IO密集型的程序擎颖,瓶頸在MySQL上,所以體現(xiàn)不出PHP的性能優(yōu)勢观游。但在密集計算方面比C/C++搂捧、Java等靜態(tài)編譯語言相差幾十倍甚至上百倍。
例如:使用多進程方式同時訪問Web地址
$ vim multi.php
<?php
echo "process begin: ".date("Y-m-d H:i:s").PHP_EOL;
//初始化地址數(shù)組
$urls = [
"http://www.baidu.com",
"http://www.360.com",
"http://www.qq.com",
"http://www.sina.com"
];
//初始化數(shù)組用于回收線程管道內(nèi)容
$workers = [];
//按照任務分配線程
for($i=0; $i<count($urls); $i++){
$url = $urls[$i];
//創(chuàng)建進程
$process = new swoole_process(function(swoole_process $worker) use($url){
//模擬執(zhí)行耗時任務
file_get_contents($url);
//sleep(1);//模擬耗時1秒
echo $url.PHP_EOL;
}, true);
//開啟進程
$pid = $process->start();
$workers[$pid] = $process;
}
//打印管道內(nèi)容
foreach($workers as $worker){
echo "pid : ".$worker->read();
}
echo "process end: ".date("Y-m-d H:i:s").PHP_EOL;
運行代碼
$ php multi.php
process begin: 2019-06-22 16:19:48
pid : http://www.baidu.com
pid : http://www.360.com
pid : http://www.qq.com
pid : http://www.sina.com
process end: 2019-06-22 16:19:49
內(nèi)存共享
進程之間是相互獨立的懂缕,那么如何實現(xiàn)進程之間的通信呢异旧? 這里可以使用共享內(nèi)存的方式來實現(xiàn)。
共享內(nèi)存ShareMemory
是映射一段能被其他進程所訪問的內(nèi)存提佣,這段共享內(nèi)存由一個進程創(chuàng)建吮蛹,但多個進程都可以訪問荤崇。共享內(nèi)存是最快的IPC
方式,是針對其他進程之間通信效率低下而專門設(shè)計的潮针,它往往與其它通信機制术荤,如信號量配置使用以實現(xiàn)進程之間的同步和通信。
共享內(nèi)存是操作系統(tǒng)中比較特殊的內(nèi)存每篷,它并不依賴于任何進程瓣戚, 也不屬于任何進程。通過調(diào)用系統(tǒng)函數(shù)創(chuàng)建共享內(nèi)存焦读,并指定它的索引子库,也就是它的IDshmid
,通過索引任何進程都可以在共享內(nèi)存中申請內(nèi)存空間并存儲對應的值矗晃。
- 共享內(nèi)存并不屬于任何一個進程
- 在共享內(nèi)存中分配的內(nèi)存空間可以被任何進程訪問
- 即使進程關(guān)閉仑嗅,共享內(nèi)存仍然可以繼續(xù)保存在操作系統(tǒng)中。
查看操作系統(tǒng)中共享內(nèi)存的分片
$ ipcs -m
------------ 共享內(nèi)存段 --------------
鍵 shmid 擁有者 權(quán)限 字節(jié) 連接數(shù) 狀態(tài)
0x00000000 131072 jc 777 16384 1 目標
0x00000000 327681 jc 600 67108864 2 目標
0x00000000 262146 jc 777 8077312 2 目標
Swoole沒有采用多線程模型而使用了多線程模型张症,在一定程度上減少了訪問數(shù)據(jù)時加鎖解鎖的開銷仓技,但同時也引入了新的需求 共享內(nèi)存。Swoole中為了更好的進行內(nèi)存管理俗他,減少頻繁分配釋放內(nèi)存空間造成的損耗和內(nèi)存碎片脖捻,Rango實際并實現(xiàn)了三種不同功能的內(nèi)存池分別時FixedPool
、RingBuffer
兆衅、MemoryGlobal
地沮。
Swoole開發(fā)模式
對于傳統(tǒng)PHP的Web開發(fā)而言,最常用的是LNMP架構(gòu)羡亩。在LNMP架構(gòu)中诉濒,當請求進入時,WebServer會將請求轉(zhuǎn)交給PHP-FPM夕春,PHP-FPM是一個進程池架構(gòu)的FastCGI服務未荒,內(nèi)置了PHP解釋器。PHP-FPM負責解釋執(zhí)行PHP文件并生成響應及志,最終返回給WebServer展現(xiàn)至前端片排。由于PHP-FPM本身是同步阻塞進程模型,在請求結(jié)束后會釋放掉所有資源速侈,包括框架初始化創(chuàng)建的一些列對象率寡,從而導致PHP進程進入“空轉(zhuǎn)”消耗大量CPU資源,最終導致單機的吞吐能力有限倚搬。
另外冶共,在每次請求處理的過程都意味著一次PHP文件解析、環(huán)境設(shè)置等不必要的耗時操作,當PHP進程處理完后就會銷毀捅僵,無法在PHP程序中使用連接池等技術(shù)實現(xiàn)性能優(yōu)化家卖。
針對傳統(tǒng)架構(gòu)的問題,Swoole從PHP擴展下手庙楚,解決了上述問題上荡。相比較傳統(tǒng)的Web架構(gòu),Swoole進程模型最大的特點在于多線程Reactor模式處理網(wǎng)絡(luò)請求馒闷,使其能輕松應對大量連接酪捡。
除此之外,Swoole是全異步非阻塞纳账,因此占用資源少逛薇,程序執(zhí)行效率高。在Swoole中程序運行只解析加載一次PHP文件疏虫,避免每次請求的重復加載永罚。再者,Swoole進程常駐议薪,使得連接池和請求之間的信息傳遞的實現(xiàn)成為可能尤蛮。
使用Swoole開發(fā)時媳友,需要開發(fā)人員對多進程的運行模式有著清晰的認識斯议。另外,Swoole很容易造成內(nèi)存泄露醇锚。在處理全局變量哼御、靜態(tài)變量的時候要小心,這種不會被GC清理的變量會存在整個生命周期中焊唬。如果沒有正確的處理恋昼,很容易消耗完內(nèi)存。而在PHP-FPM下赶促,PHP代碼執(zhí)行完畢內(nèi)存就會被完全釋放掉液肌。
Swoole進程結(jié)構(gòu)
LNMP
架構(gòu)中PHP
是需要依賴Nginx
這樣的Web
服務器以及PHP-FPM
這樣的多進程的PHP
解析器。當一個請求到來時PHP-FPM
會去創(chuàng)建一個新的進程去處理這個請求鸥滨,在這種情況下嗦哆,系統(tǒng)的開銷很大程序上都用在創(chuàng)建和銷毀進程上,導致了程序的響應效率并不是非常高婿滓。
Swoole的強大之處在于進程模型的設(shè)計老速,即解決了異步問題,又解決了并發(fā)問題凸主。
Swoole的進程可分為四種角色
- Master進程
保證Swoole機制運行橘券,同時利用它創(chuàng)建Master主線程(負責接收連接、定時器等)和Reactor線程(處理連接并將請求分發(fā)給各個Worker進程)。 - Manager進程
Worker進程和Task進程均由Manager進程派生旁舰,Manager管理進程負責結(jié)束時回收子進程锋华,避免僵尸進程的存在。 - Worker進程
用PHP回調(diào)函數(shù)處理由Reactor分發(fā)過來的請求數(shù)據(jù)鬓梅,并生成響應數(shù)據(jù)發(fā)送給Reactor供置,由Reactor發(fā)送給TCP客戶端。 - Task進程
接收由Worker進程分發(fā)給它的任務绽快,以多進程方式運行芥丧,處理好后將結(jié)果返回給它的Worker進程。
在Swoole
中采用了和PHP-FPM
完全不同的架構(gòu)坊罢,整個Swoole
擴展可以分為三層:
第1層:Master主進程
Master進程是Swoole的主進程续担,主要用于處理Swoole的核心事件驅(qū)動。Master主進程是一個多線程模型活孩,擁有多個獨立的Reactor線程物遇。
Master主進程包含Master線程、Reactor線程憾儒、心跳檢測線程询兴、UDP收包線程。每個Reactor子線程中都運行著一個epoll
函數(shù)的實例起趾,Swoole對于事件的監(jiān)聽都會在Reactor線程中實現(xiàn)诗舰,比如來自客戶端的連接、本地通信使用的管道训裆、異步操作使用的文件以及文件描述符都會注冊在epoll
函數(shù)中眶根。
Master主進程使用select/poll
進行IO
事件循環(huán),Master主進程中的文件描述符只有幾個边琉,Reactor線程使用epoll
属百,因為Reactor線程中會監(jiān)聽大量連接的可讀事件,使用epoll
可以支持大量的文件描述符变姨。
以HTTP
服務器為例族扰,Master
主進程負責監(jiān)聽端口,然后接收新的連接定欧,并將這個連接分配給一個Reactor
線程涯鲁,由這個Reactor
線程監(jiān)聽此連接剩盒,一旦此連接可讀時,它會讀取數(shù)據(jù)并解析協(xié)議,然后將請求投遞到Worker
工作進程中去執(zhí)行劈猪。
Master主進程內(nèi)的回調(diào)函數(shù)
-
onStart
服務器啟動時主進程的主線程回調(diào)此函數(shù) -
onShutdown
服務器正常結(jié)束時發(fā)生
Master 線程
Swoole啟動后Master主線程會負責監(jiān)聽服務器的socket
须鼎,如果有新的連接accept
喜爷,Master主線程會評估每個Reactor線程的連接數(shù)量母市,并將此連接分配給連接最少的Reactor線程耍属。這樣做的好處是:
- 每個Reactor線程持有的連接數(shù)非常均衡,沒有單個線程負載過高的問題巩检。
- 解決了驚群問題厚骗,尤其是擁有多個
listen socket
時,節(jié)約了線程喚醒和切換的開銷兢哭。 - 主線程接管了所有信號
signal
的處理领舰,使Reactor線程運行中可以不被信號打斷。
主線程Master在accept
新的連接后迟螺,會將這個連接分配給一個固定的Reactor線程冲秽,并由這個線程負責監(jiān)聽此socket
,在socket
可讀時讀取數(shù)據(jù)矩父,并進行協(xié)議解析锉桑,最后將請求投遞到Worker進程。
Reactor線程
- 負責維護客戶端
TCP
連接窍株、處理網(wǎng)絡(luò)IO民轴、處理協(xié)議、收發(fā)數(shù)據(jù)球订。 - 完全是異步非阻塞的模式
- 全部都是C代碼后裸,除了
Start/Shutdown
事件回調(diào)外,不執(zhí)行任何PHP
代碼冒滩。 - 將
TCP
客戶端發(fā)送來的數(shù)據(jù)緩沖微驶、拼接、拆分為完整的請求數(shù)據(jù)包旦部。 -
Reactor
以多線程的方式運行
Swoole擁有多線程Reactor祈搜,所以可以充分利用多核较店,開啟CPU親和設(shè)置后士八,Reactor線程可以綁定單獨的核,節(jié)省CPU Cache開銷梁呈。
Reactor線程負責處理TCP
連接婚度,是收發(fā)數(shù)據(jù)的線程。Swoole的Master主線程在accept
新的連接后官卡,會將這個連接分配給一個固定的Reactor線程蝗茁,并由這個線程負責監(jiān)聽此socket
。在socket
可讀時讀取數(shù)據(jù)寻咒,并進行協(xié)議解析哮翘,將請求投遞到Worker工作進程。在socket
可寫時毛秘,將數(shù)據(jù)發(fā)送給TCP
客戶端饭寺。
Reactor線程負責維護客戶端TCP連接阻课、處理網(wǎng)絡(luò)IO、處理協(xié)議艰匙、收發(fā)數(shù)據(jù)限煞,它完全是異步非阻塞的模式。
Reactor線程是全異步非阻塞的员凝,即使Worker進程采用了同步模式署驻,依然不響應Reactor線程的性能。在Worker進程組很繁忙的狀態(tài)下健霹,Reactor線程完全不受影響旺上,依然可以收發(fā)處理數(shù)據(jù)。
由于TCP是流式的沒有邊界糖埋,所以處理起來很麻煩抚官。Reactor線程可以使用EOF或者包頭長度,自動緩存數(shù)據(jù)阶捆、組裝數(shù)據(jù)包凌节,等一個請求完全收到后,再次遞交給Worker洒试。
Reactor全部是C代碼倍奢,除了Start/Shutdown事件回調(diào)外,不執(zhí)行任何PHP代碼垒棋。它將TCP客戶端發(fā)來的數(shù)據(jù)緩沖卒煞、拼接、拆分成完整的一個請求數(shù)據(jù)包叼架。
綜上所述畔裕,Master主進程中包含兩個關(guān)鍵線程:Master主線程和Reactor線程,Master主線程用來處理accept()
事件乖订,創(chuàng)建新的socket fd
扮饶,當它接收到新連接后會將新的socket
連接放到Reactor線程的事件監(jiān)聽循環(huán)中,Reactor線程負責接收從客戶端發(fā)送過來的數(shù)據(jù)乍构,并按協(xié)議解析后通過管道pipe
傳遞給Worker工作進程進行處理甜无。Worker工作進程處理完畢后,會將結(jié)果通過管道pipe
回傳給Reactor線程哥遮,Reactor線程再按照協(xié)議將結(jié)果通過socket
發(fā)送給客戶端岂丘。可以看到Reactor線程負責數(shù)據(jù)的IO和傳輸眠饮,在Linux系統(tǒng)下這些IO事件都是通過epoll
機制來處理的奥帘。
心跳包檢測線程HeartbeatCheck
Swoole配置了心跳檢測后心跳包線程會在固定事件內(nèi)對所有之前在線的連接發(fā)送檢測數(shù)據(jù)包。
UDP收包線程UdpRecv
接收并處理客戶端UDP數(shù)據(jù)包
第2層:Manager管理進程
Swoole運行中會創(chuàng)建一個單獨的管理進程仪召,所有的Worker進程和Task進程都是從管理進程fork
創(chuàng)建出來的寨蹋。
Manager管理進程會監(jiān)聽所有子進程的退出事件牲距,當Worker進程發(fā)生致命錯誤或運行生命周期結(jié)束時,Manager管理進程會回收此進程并創(chuàng)建新的進程钥庇。
Manager管理進程還可以平滑地重啟所有工作進程Worker牍鞠,以實現(xiàn)程序代碼的重新加載。
Manager管理進程管理著Worker工作進程或Task任務進程评姨,Worker工作進程或Task任務進程都被Manager管理進程fork
創(chuàng)建并管理著难述。
Manager進程負責創(chuàng)建和管理下層的Worker進程組和Task進程組,Manager進程中不會運行任何用戶層面的業(yè)務邏輯吐句,僅僅只做進程的管理和分配胁后。
Manager進程會fork
創(chuàng)建出指定數(shù)量的Worker進程和Task進程。
Manager進程的工作職責
- Worker工作進程和Task任務進程都是由Manager管理進程
fork
創(chuàng)建并管理的 - 子進程結(jié)束運行時嗦枢,Manager管理進程負責回收子進程攀芯,以避免成為僵尸進程,并創(chuàng)建新的子進程文虏。
- 服務器關(guān)閉時侣诺,Manager管理進程發(fā)送信號給所有子進程,并通知子進程關(guān)閉服務氧秘。
- 服務器重啟時年鸳,Manager管理進程會逐個關(guān)閉或重啟子進程。
Manager進程內(nèi)的回調(diào)函數(shù)
-
onManagerStart
當管理進程啟動時調(diào)用 -
onManagerStop
當管理進程結(jié)束時調(diào)用 -
onWorkerError
當Worker進程或Task進程發(fā)生異常后會在Manager進程會回調(diào)此函數(shù)
第3層:工作進程
- 工作進程主要用于處理客戶端請求
- 工作進程接收由Reactor線程投遞的請求數(shù)據(jù)包丸相,并執(zhí)行PHP回調(diào)函數(shù)處理數(shù)據(jù)搔确。
- 工作進程生成響應數(shù)據(jù)并發(fā)送給Reactor線程,由Reactor線程發(fā)送給TCP客戶端灭忠。
- 工作進程可以是異步非阻塞模式也可以是同步阻塞模式膳算。
- 工作進程以多進程的方式運行
與傳統(tǒng)的半同步半異步服務器不同是,Swoole的工作進程可以同步的也可以異步的弛作。這樣帶來了工作進程類似于PHP-FPM進程涕蜂,它接收由Reactor線程投遞的請求數(shù)據(jù)包,并執(zhí)行PHP回調(diào)函數(shù)處理數(shù)據(jù)缆蝉。工作線程生成響應數(shù)據(jù)并發(fā)送給Reactor線程宇葱,由Reactor線程發(fā)送給TCP客戶端瘦真。工作線程可以是異步模式刊头,也可以是同步模式。另外诸尽,工作線程以多進程的方式運行原杂。
Swoole想要實現(xiàn)最好的性能就必須創(chuàng)建出多個工作進程幫助處理任務,但是工作進程必須fork
操作您机,而fork
操作又是不安全的穿肄。如果沒有管理將會出現(xiàn)很多僵尸進程年局,進而影響服務器性能。同時工作進程被誤殺或由于程序原因會引起異常退出咸产,為了保證服務的穩(wěn)定性矢否,需要重新創(chuàng)建工作進程。
工作進程可分為兩類:Worker進程和Task進程
Worker進程是Swoole的主邏輯進程脑溢,用于處理來自客戶端的請求僵朗。
Task進程是Swoole提供的異步工作進程,用于處理耗時較長的同步任務屑彻。
Worker工作進程
Worker工作進程接收Reactor線程投遞過來的數(shù)據(jù)验庙,執(zhí)行PHP代碼,然后生成數(shù)據(jù)并交給Reactor線程社牲,由Reactor線程通過TCP
將數(shù)據(jù)返回給客戶端粪薛。如果是UDP
,Worker工作進程會直接將數(shù)據(jù)發(fā)送給客戶端搏恤。
Worker進程中執(zhí)行的PHP
代碼违寿,它等同于PHP-FPM
。PHP-FPM
在處理異步操作時是很無力的熟空,但Swoole提供的Task進程可以很好的解決這個問題陨界。Worker進程可以將一些異步任務投遞給Task進程,然后直接返回痛阻,處理其他由Reactor線程投遞過來的事件菌瘪。
Worker進程內(nèi)的回調(diào)函數(shù)
-
onWorkerStart
當Worker工作進程或Task任務進程啟動時觸發(fā) -
onWorkerStop
當Worker進程終止時觸發(fā) -
onConnect
當有新的連接進入時觸發(fā) -
onClose
當TCP客戶端連接關(guān)閉后觸發(fā) -
onReceive
當接收到數(shù)據(jù)時觸發(fā) -
onPacket
當接收到UDP數(shù)據(jù)包是時觸發(fā) -
onFinish
當Worker工作進程投遞的任務在task_worker
中完成時,Task進程會通過finish()
方法將任務處理的結(jié)果發(fā)送給Worker進程阱当。 -
onWorkerExit
當開啟reload_async
特性后有效俏扩,即異步重啟特性。 -
onPipeMessage
當工作進程收到由sendMessage
發(fā)送的管道消息時觸發(fā)
Task任務進程
- 異步工作進程
- 接收由Worker工作進程通過
swoole_server->task
或swoole_server->taskwait
方法投遞的任務弊添。 - 處理任務录淡,并將結(jié)果數(shù)據(jù)返回給Worker工作進程
swoole_server->finish
。 - 同步阻塞模式
- 以多進程的方式運行
Swoolen除了Reactor線程油坝,Task任務工作進程是以異步的方式處理其它任務的進程嫉戚,使用方式類似于Gearman。它接收由Worker進程通過swoole_server->task/taskwait
方法投遞的任務澈圈,然后處理任務彬檀,并將結(jié)果數(shù)據(jù)使用swoole_server->finish
返回給Worker進程。Task以多進程的方式進行運行瞬女。
簡單來說窍帝,可以將Reactor理解為Nginx,將Worker理解為PHP-FPM诽偷。Reactor線程異步并行地處理網(wǎng)絡(luò)請求坤学,然后再轉(zhuǎn)發(fā)給Worker工作進程中去處理(在回調(diào)函數(shù)中處理)疯坤。Reactor和Worker之間通過UnixSocket進行通信。
Swoole除了Reactor線程深浮,Worker工作進程還提供了Task進程池压怠。目的是為了解決業(yè)務代碼中,有些邏輯部分不需要馬上執(zhí)行飞苇。利用Task進程池刑峡,可以方便的投遞一個異步任務區(qū)執(zhí)行。
Task進程以完全同步阻塞的方式運行玄柠,一個Task進程在執(zhí)行任務期間是不接受從Worker進程投遞的任務的突梦,當Task進程執(zhí)行完任務后,會異步的通知Worker進程并告訴它任務已經(jīng)完成羽利。
Task進程內(nèi)的回調(diào)函數(shù)
-
onTask
在Task線程內(nèi)被調(diào)用宫患,Worker進程可使用swoole_server_task
函數(shù)向Task進程投遞新的任務。 -
onWorkerStart
在Worker或Task進程啟動時觸發(fā) -
onPipeMessage
當工作進程收到由sendMessage
發(fā)送的管道消息時觸發(fā)
Swoole進程協(xié)作
- 當客戶端主動連入服務器的時候这弧,客戶端實際上是與Master主進程中的某個Reactor線程發(fā)生了連接娃闲。
- 當TCP三次握手成功后,由這個Reactor線程將連接成功的消息告知Manager管理進程匾浪,再由Manager管理進程轉(zhuǎn)交給Worker工作進程皇帮,最終在Worker工作進程中觸發(fā)
onConnect
事件對應的方法。 - 當客戶端向服務器發(fā)送一個數(shù)據(jù)包的時候蛋辈,首先接收到數(shù)據(jù)包的是Reactor線程属拾,同時Reactor線程會完成組包,再將組裝好的包交給Manager管理進程冷溶,由Manager管理進程轉(zhuǎn)交給Worker工作進程渐白,此時Worker工作進程觸發(fā)
onReceive
事件。 - 如果Worker工作進程中做了處理操作后再使用
Send
方法將數(shù)據(jù)發(fā)回給客戶端時逞频,數(shù)據(jù)會沿著這個路徑逆流而上纯衍。 - Task任務進程用來處理一些占用時間較長的業(yè)務,主要處理Worker工作進程中占用時間較長的任務苗胀。
形象來說
- Master主進程 = 業(yè)務窗口
- Reactor線程 = 前臺接待員
- Manager管理進程 = 項目經(jīng)理
- Worker工作進程 = 工人
當在業(yè)務窗口辦理業(yè)務時襟诸,如果用戶很多,后邊的用戶需要排隊等待服務基协,Reactor負責與客戶直接溝通歌亲,對客戶的請求進行初步的整理(傳輸層級別的整理,組包)堡掏,然后Manager負責將業(yè)務分配給合適的Worker应结,如空閑的Worker,最終Worker負責實現(xiàn)具體的業(yè)務泉唁。
Swoole進程關(guān)系
Reactor和Worker與Task的關(guān)系鹅龄,簡單理解可認為:Reactor = Nginx、Worker = PHP-FPM
Reactor線程異步并行地處理網(wǎng)絡(luò)請求亭畜,然后再轉(zhuǎn)發(fā)給Worker工作進程中去處理扮休。
Reactor線程和Worker工作進程之間通過socket
進行通信。
在PHP-FPM的應用中拴鸵,經(jīng)常會將一個任務異步投遞到Redis等隊列中玷坠,并在后臺啟動一些PHP進程異步地處理這些任務。
Swoole提供的Worker工作進程是一套更加完整的方案劲藐,它將任務投遞八堡、隊列、PHP任務進程管理融為一體聘芜。通過底層的API實現(xiàn)異步任務的處理兄渺。另外,Task任務進程可以在任務執(zhí)行完畢后汰现,再返回一個結(jié)果反饋到Worker工作進程挂谍。
Swoole的Reactor、Worker瞎饲、Task之間可以緊密的結(jié)合起來口叙,提供更加高級的使用方式。
假設(shè)Server是一個工廠
- Reactor:銷售嗅战,接收客戶訂單妄田。
- Worker:工人,當銷售接單后驮捍,Worker去工作生產(chǎn)出客戶需要的東西形庭。
- Task:行政人員,幫助Worker干些雜事兒厌漂,讓Worker專心工作萨醒。
底層會為Worker工作進程、Task任務進程分配一個唯一的ID苇倡,不同的Worker和Task任務進程之間可以通過sendMessage
接口進行通信富纸。
Swoole執(zhí)行流程
- 當客戶端請求進入Master主進程后會被Master主線程接收到
- 將讀寫操作的監(jiān)聽注冊到對應的Reactor線程中,并通知Worker工作進程處理
onConnect
旨椒,也就是接收到連接的回調(diào)晓褪。 - 客戶端的數(shù)據(jù)會通知對應的Reactor線程并發(fā)送給Worker工作進程進行處理。
- 如果Worker工作進程投遞任務综慎,將數(shù)據(jù)通過管道發(fā)送給Task任務進程涣仿,Task任務進程處理完后會發(fā)送給Worker工作進程。
- Worker工作進程會通知Reactor線程發(fā)送數(shù)據(jù)給客戶端。
- 當Worker工作進程出現(xiàn)異常時關(guān)閉好港,Manager管理進程會重新創(chuàng)建一個Worker工作進程愉镰,保證Worker工作進程的數(shù)量是固定的。