摘要:當(dāng)某種網(wǎng)絡(luò)事件發(fā)生時,會回調(diào)用戶設(shè)置的指定回調(diào)函數(shù)。承擔(dān)了底層網(wǎng)絡(luò)事件的監(jiān)聽及各種底層事件處理,當(dāng)收到請求時,會觸發(fā)事件提醒,然后將控制權(quán)轉(zhuǎn)交預(yù)先注冊的事件回調(diào)函數(shù),來進(jìn)行后續(xù)的處理。請求到來時創(chuàng)建,請求結(jié)束后銷毀。
運行流程圖
當(dāng)啟動一個Swoole應(yīng)用時,一共會創(chuàng)建2+n+m個進(jìn)程,2為一個Master進(jìn)程和一個Manager進(jìn)程,其中n為Worker進(jìn)程數(shù),m為TaskWorker進(jìn)程數(shù)。名詞解釋 Master進(jìn)程
主進(jìn)程,該進(jìn)程會創(chuàng)建Manager進(jìn)程、Reactor線程,UDP收包線程,心跳檢測線程等線程Manger進(jìn)程
管理進(jìn)程,該進(jìn)程的作用是創(chuàng)建、管理所有的Worker進(jìn)程和TaskWorker進(jìn)程。
子進(jìn)程結(jié)束運行時,manager進(jìn)程負(fù)責(zé)回收此子進(jìn)程,避免成為僵尸進(jìn)程。并創(chuàng)建新的子進(jìn)程
服務(wù)器關(guān)閉時,manager進(jìn)程將發(fā)送信號給所有子進(jìn)程,通知子進(jìn)程關(guān)閉服務(wù)
服務(wù)器reload時,manager進(jìn)程會逐個關(guān)閉/重啟子進(jìn)程
Worker進(jìn)程工作進(jìn)程,所有的業(yè)務(wù)邏輯代碼均在此進(jìn)程上運行。當(dāng)Reactor線程接收到來自客戶端的數(shù)據(jù)后,會將數(shù)據(jù)打包通過管道發(fā)送給某個Worker進(jìn)程。
接受由Reactor線程投遞的請求數(shù)據(jù)包,并執(zhí)行PHP回調(diào)函數(shù)處理數(shù)據(jù)
生成響應(yīng)數(shù)據(jù)并發(fā)給Reactor線程,由Reactor線程發(fā)送給TCP客戶端
可以是異步非阻塞模式,也可以是同步阻塞模式
Worker以多進(jìn)程的方式運行
TaskWorker進(jìn)程一種特殊的工作進(jìn)程,該進(jìn)程的作用是處理一些耗時較長的任務(wù),以達(dá)到釋放Worker進(jìn)程的目的。
接受由Worker進(jìn)程通過swoole_server->task/taskwait方法投遞的任務(wù)
處理任務(wù),并將結(jié)果數(shù)據(jù)返回(使用swoole_server->finish)給Worker進(jìn)程
完全是同步阻塞模式
TaskWorker以多進(jìn)程的方式運行
Reactor線程實際運行Linux中是epoll實例,MacOS中為Kqueue實例,用于accept客戶端連接以及接收客戶端數(shù)據(jù)。 Swoole的主進(jìn)程是一個多線程的程序。其中有一組很重要的線程,稱之為Reactor線程。它就是真正處理TCP連接,收發(fā)數(shù)據(jù)的線程。 Swoole的主線程在Accept新的連接后,會將這個連接分配給一個固定的Reactor線程,并由這個線程負(fù)責(zé)監(jiān)聽此socket。 在socket可讀時讀取數(shù)據(jù),并進(jìn)行協(xié)議解析,將請求投遞到Worker進(jìn)程。在socket可寫時將數(shù)據(jù)發(fā)送給TCP客戶端。
負(fù)責(zé)維護(hù)客戶端TCP連接、處理網(wǎng)絡(luò)IO、處理協(xié)議、收發(fā)數(shù)據(jù)
完全是異步非阻塞的模式
全部為C代碼,除Start/Shudown事件回調(diào)外,不執(zhí)行任何PHP代碼
將TCP客戶端發(fā)來的數(shù)據(jù)緩沖、拼接、拆分成完整的一個請求數(shù)據(jù)包
Reactor以多線程的方式運行
運行機(jī)制Swoole是php的擴(kuò)展,一旦運行后就會接管PHP的控制權(quán),進(jìn)入事件循環(huán)。 當(dāng)某種IO(網(wǎng)絡(luò)IO)事件發(fā)生時,Swoole 會回調(diào)用戶設(shè)置的指定回調(diào)函數(shù)。 Swoole承擔(dān)了底層網(wǎng)絡(luò)事件的監(jiān)聽及各種底層事件處理,當(dāng)收到請求時,會觸發(fā)事件提醒,然后將控制權(quán)轉(zhuǎn)交預(yù)先注冊的事件回調(diào)函數(shù),來進(jìn)行后續(xù)的處理。 可以理解為Reactor就是nginx,Worker就是php-fpm。Reactor線程異步并行地處理網(wǎng)絡(luò)請求,然后再轉(zhuǎn)發(fā)給Worker進(jìn)程中去處理。Reactor和Worker間通過UnixSocket進(jìn)行通信。 Swoole提供的TaskWorker是一套更完整的方案,將任務(wù)的投遞、隊列、php任務(wù)處理進(jìn)程管理合為一體。通過底層提供的API可以非常簡單地實現(xiàn)異步任務(wù)的處理。 另外TaskWorker還可以在任務(wù)執(zhí)行完成后,再返回一個結(jié)果反饋到Worker。 Swoole的Reactor、Worker、TaskWorker之間可以緊密的結(jié)合起來,提供更高級的使用方式。 一個更通俗的比喻,假設(shè)Server就是一個工廠,Master是董事長,Manager是CEO,那Reactor就是銷售經(jīng)理,接受客戶訂單。而Worker就是工人,當(dāng)銷售接到訂單后,Worker去工作生產(chǎn)出客戶要的東西。 而TaskWorker可以理解為行政人員,可以幫助Worker干些雜事,讓W(xué)orker專心工作。
所謂的回調(diào)函數(shù)(CallBack) 就好比是張開了夾子的捕鼠器,我們設(shè)定當(dāng)有老鼠踩到捕鼠器的時候,他會關(guān)閉夾子然后捉住老鼠,我們放置捕鼠器的時候,捕鼠器并沒有真的抓老鼠。這個設(shè)定就是回調(diào),他不立刻執(zhí)行,會在遇到觸發(fā)條件(事件)時執(zhí)行,在上面的示例當(dāng)中我們放置了3個捕鼠器(回調(diào)函數(shù)),我們只需要知道他會在特定老鼠(事件)踩到的時候(發(fā)生的時候)去執(zhí)行我們期望的功能就好。
底層會為Worker進(jìn)程、TaskWorker進(jìn)程分配一個唯一的ID
不同的Worker和TaskWorker進(jìn)程之間可以通過sendMessage接口進(jìn)行通信
運行周期 程序全局期在swoole_server->start之前就創(chuàng)建好的對象,我們稱之為程序全局生命周期。 這些變量在程序啟動后就會一直存在,直到整個程序結(jié)束運行才會銷毀。 有一些服務(wù)器程序可能會連續(xù)運行數(shù)月甚至數(shù)年才會關(guān)閉/重啟,那么程序全局期的對象在這段時間持續(xù)駐留在內(nèi)存中的。 程序全局對象所占用的內(nèi)存是Worker進(jìn)程間共享的,不會額外占用內(nèi)存。 這部分內(nèi)存會在寫時分離(COW),在Worker進(jìn)程內(nèi)對這些對象進(jìn)行寫操作時,會自動從共享內(nèi)存中分離,變?yōu)檫M(jìn)程全局對象。 程序全局期include/require的代碼,必須在整個程序shutdown時才會釋放,reload無效。進(jìn)程全局期
swoole擁有進(jìn)程生命周期控制的機(jī)制,一個Worker子進(jìn)程處理的請求數(shù)超過max_request配置后,就會自動銷毀。 Worker進(jìn)程啟動后創(chuàng)建的對象(onWorkerStart中創(chuàng)建的對象),在這個子進(jìn)程存活周期之內(nèi),是常駐內(nèi)存的。 onConnect/onReceive/onClose 中都可以去訪問它。 進(jìn)程全局對象所占用的內(nèi)存是在當(dāng)前子進(jìn)程內(nèi)存堆的,并非共享內(nèi)存。對此對象的修改僅在當(dāng)前Worker進(jìn)程中有效。 進(jìn)程期include/require的文件,在reload后就會重新加載。會話期
會話期是在onConnect后創(chuàng)建,或者在第一次onReceive時創(chuàng)建,onClose時銷毀。一個客戶端連接進(jìn)入后,創(chuàng)建的對象會常駐內(nèi)存,直到此客戶端離開才會銷毀。 swoole中會話期的對象直接是常駐內(nèi)存,不需要session_start之類操作。 可以直接訪問對象,并執(zhí)行對象的方法。請求期
請求期就是指一個完整的請求發(fā)來,也就是onReceive收到請求開始處理,直到返回結(jié)果發(fā)送response。 這個周期所創(chuàng)建的對象,會在請求完成后銷毀。 swoole中請求期對象與普通PHP程序中的對象就是一樣的。 請求到來時創(chuàng)建,請求結(jié)束后銷毀。4種PHP回調(diào)函數(shù)風(fēng)格 匿名函數(shù)
$server->on("Request", function ($req, $resp) use ($a, $b, $c) { echo "hello world"; });
可使用use向匿名函數(shù)傳遞參數(shù)類靜態(tài)方法
class A { static function test($req, $resp) { echo "hello world"; } } $server->on("Request", "A::Test"); $server->on("Request", array("A", "Test"));對象方法
class A { function test($req, $resp) { echo "hello world"; } } $object = new A(); $server->on("Request", array($object, "test"));函數(shù)
function my_onRequest($req, $resp) { echo "hello world"; } $server->on("Request", "my_onRequest");編程須知 注意事項
不要在代碼中執(zhí)行sleep以及其他睡眠函數(shù),這樣會導(dǎo)致整個進(jìn)程阻塞
在swoole程序中禁止使用exit/die,如果PHP代碼中有exit/die,當(dāng)前工作的Worker進(jìn)程、Task進(jìn)程、User進(jìn)程、以及swoole_process進(jìn)程會立即退出。使用exit/die后Worker進(jìn)程會因為異常退出, 被master進(jìn)程再次拉起, 最終造成進(jìn)程不斷退出又不斷啟動和產(chǎn)生大量警報日志。
mt_rand隨機(jī)數(shù),在Swoole中如果在父進(jìn)程內(nèi)調(diào)用了mt_rand,不同的子進(jìn)程內(nèi)再調(diào)用mt_rand返回的結(jié)果會是相同的。所以必須在每個子進(jìn)程內(nèi)調(diào)用mt_srand重新播種。
while循環(huán)的影響,異步程序如果遇到死循環(huán),事件將無法觸發(fā)。異步IO程序使用Reactor模型,運行過程中必須在reactor->wait處輪詢。如果遇到死循環(huán),那么程序的控制權(quán)就在while中了,reactor無法得到控制權(quán),無法檢測事件,所以IO事件回調(diào)函數(shù)也將無法觸發(fā)。
可通過register_shutdown_function來捕獲致命錯誤,在進(jìn)程異常退出時做一些清理工作
PHP代碼中如果有異常拋出,必須在回調(diào)函數(shù)中進(jìn)行try/catch捕獲異常,否則會導(dǎo)致工作進(jìn)程退出
不支持set_exception_handler,必須使用try/catch方式處理異常
Worker進(jìn)程不得共用同一個Redis或MySQL等網(wǎng)絡(luò)服務(wù)客戶端,Redis/MySQL創(chuàng)建連接的相關(guān)代碼可以放到onWorkerStart回調(diào)函數(shù)中。
異步編程異步程序要求代碼中不得包含任何同步阻塞操作
異步與同步代碼不能混用,一旦應(yīng)用程序使用了任何同步阻塞的代碼,程序即退化為同步模式
類/函數(shù)重復(fù)定義新手非常容易犯這個錯誤,由于Swoole是常駐內(nèi)存的,所以加載類/函數(shù)定義的文件后不會釋放。因此引入類/函數(shù)的php文件時必須要使用include_once或require_once,否會發(fā)生cannot redeclare function/class 的致命錯誤。進(jìn)程隔離
進(jìn)程隔離也是很多新手經(jīng)常遇到的問題。修改了全局變量的值,為什么不生效,原因就是全局變量在不同的進(jìn)程,內(nèi)存空間是隔離的,所以無效。所以使用Swoole開發(fā)Server程序需要了解進(jìn)程隔離問題。
不同的進(jìn)程中PHP變量不是共享,即使是全局變量,在A進(jìn)程內(nèi)修改了它的值,在B進(jìn)程內(nèi)是無效的
如果需要在不同的Worker進(jìn)程內(nèi)共享數(shù)據(jù),可以用Redis、MySQL、文件、SwooleTable、APCu、shmget等工具實現(xiàn)
不同進(jìn)程的文件句柄是隔離的,所以在A進(jìn)程創(chuàng)建的Socket連接或打開的文件,在B進(jìn)程內(nèi)是無效,即使是將它的fd發(fā)送到B進(jìn)程也是不可用的
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/29731.html
摘要:前言都是為了生存有收獲的話請加顆小星星,沒有收獲的話可以反對沒有幫助舉報三連代碼倉庫初始上一什么是面向生產(chǎn)環(huán)境的異步網(wǎng)絡(luò)通信引擎使開發(fā)人員可以編寫高性能的異步并發(fā),服務(wù)。 前言:都是為了生存 有收獲的話請加顆小星星,沒有收獲的話可以 反對 沒有幫助 舉報三連 代碼倉庫 初始swoole【上】 一、什么是swoole Swoole:面向生產(chǎn)環(huán)境的 PHP 異步網(wǎng)絡(luò)通信引擎 使 PHP...
摘要:目錄初識創(chuàng)建服務(wù)器上創(chuàng)建服務(wù)器下異步任務(wù)持續(xù)更新中。。。參加工作有一段時間了,偶爾會聽到,對我這種小白粗略看下文檔都會覺得很牛逼。個人理解就是解決這樣應(yīng)用場景的。 目錄 初識Swoole 創(chuàng)建服務(wù)器(上) 創(chuàng)建服務(wù)器(下) 異步任務(wù)task 持續(xù)更新中。。。 參加工作有一段時間了,偶爾會聽到swoole,對我這種PHP小白粗略看下文檔都會覺得很牛逼。由于學(xué)習(xí)成本比較高,自身對網(wǎng)絡(luò)異...
摘要:前言接初識上,這篇主要是異步問題有收獲的話請加顆小星星,沒有收獲的話可以反對沒有幫助舉報三連代碼倉庫初識下異步任務(wù)設(shè)置異步任務(wù)的工作進(jìn)程數(shù)量連接連接歡迎大山驢回調(diào)投遞異步任務(wù)觸發(fā)異步任務(wù)服務(wù)端回復(fù)說處理異步任務(wù)新的異步任務(wù) 前言:接初識swoole【上】,這篇主要是異步問題 有收獲的話請加顆小星星,沒有收獲的話可以 反對 沒有幫助 舉報三連 代碼倉庫 初識swoole【下】 6、異...
摘要:是一個請求對象,包含了客戶端發(fā)來的握手請求信息事件函數(shù)中可以調(diào)用向客戶端發(fā)送數(shù)據(jù)或者調(diào)用關(guān)閉連接事件回調(diào)是可選的當(dāng)服務(wù)器收到來自客戶端的數(shù)據(jù)幀時會回調(diào)此函數(shù)。 前言:了解概念之后就應(yīng)該練練手啦,不然就是巨嬰 有收獲的話請加顆小星星,沒有收獲的話可以 反對 沒有幫助 舉報三連 代碼倉庫 實戰(zhàn)swoole【聊天室】 在線體驗 準(zhǔn)備工作 需要先看初識swoole【上】,了解基本的服務(wù)端...
摘要:安裝命令環(huán)境下必須關(guān)閉選項需要修改關(guān)閉在下開發(fā)可以使用來方便的開發(fā)應(yīng)用,安裝好后再里的選項里共享代碼所在磁盤。為源碼所在路徑為容器內(nèi)路徑在里執(zhí)行編譯安裝擴(kuò)展是按照標(biāo)準(zhǔn)擴(kuò)展構(gòu)建的。 環(huán)境依賴 僅支持 Linux、FreeBSD、MacOS 三種操作系統(tǒng) 在Windows平臺,可使用CygWin或WSL(Windows Subsystem for Linux) Linux 內(nèi)核版本 2....
閱讀 3774·2021-09-24 09:48
閱讀 1262·2021-09-10 10:51
閱讀 3446·2019-08-30 13:03
閱讀 3483·2019-08-30 12:51
閱讀 1530·2019-08-30 11:22
閱讀 1250·2019-08-29 18:38
閱讀 2207·2019-08-29 16:41
閱讀 3437·2019-08-29 15:32