swoole入門4-初識swoole

運行流程圖



圖片描述

當啓動一個Swoole應用時,一共會建立2+n+m個進程,2爲一個Master進程和一個Manager進程,其中n爲Worker進程數,m爲TaskWorker進程數。

名詞解釋

Master進程

主進程,該進程會建立Manager進程、Reactor線程,UDP收包線程,心跳檢測線程等線程

Manger進程

管理進程,該進程的做用是建立、管理全部的Worker進程和TaskWorker進程。
  • 子進程結束運行時,manager進程負責回收此子進程,避免成爲殭屍進程。並建立新的子進程
  • 服務器關閉時,manager進程將發送信號給全部子進程,通知子進程關閉服務
  • 服務器reload時,manager進程會逐個關閉/重啓子進程

Worker進程

工做進程,全部的業務邏輯代碼均在此進程上運行。當Reactor線程接收到來自客戶端的數據後,會將數據打包經過管道發送給某個Worker進程。
  • 接受由Reactor線程投遞的請求數據包,並執行PHP回調函數處理數據
  • 生成響應數據併發給Reactor線程,由Reactor線程發送給TCP客戶端
  • 能夠是異步非阻塞模式,也能夠是同步阻塞模式
  • Worker以多進程的方式運行

TaskWorker進程

一種特殊的工做進程,該進程的做用是處理一些耗時較長的任務,以達到釋放Worker進程的目的。
  • 接受由Worker進程經過swoole_server->task/taskwait方法投遞的任務
  • 處理任務,並將結果數據返回(使用swoole_server->finish)給Worker進程
  • 徹底是同步阻塞模式
  • TaskWorker以多進程的方式運行

Reactor線程

實際運行Linux中是epoll實例,MacOS中爲Kqueue實例,用於accept客戶端鏈接以及接收客戶端數據。

Swoole的主進程是一個多線程的程序。其中有一組很重要的線程,稱之爲Reactor線程。它就是真正處理TCP鏈接,收發數據的線程。

Swoole的主線程在Accept新的鏈接後,會將這個鏈接分配給一個固定的Reactor線程,並由這個線程負責監聽此socket。

在socket可讀時讀取數據,並進行協議解析,將請求投遞到Worker進程。在socket可寫時將數據發送給TCP客戶端。
  • 負責維護客戶端TCP鏈接、處理網絡IO、處理協議、收發數據
  • 徹底是異步非阻塞的模式
  • 所有爲C代碼,除Start/Shudown事件回調外,不執行任何PHP代碼
  • 將TCP客戶端發來的數據緩衝、拼接、拆分紅完整的一個請求數據包
  • Reactor以多線程的方式運行

運行機制

Swoole是php的擴展,一旦運行後就會接管PHP的控制權,進入事件循環。 

當某種IO(網絡IO)事件發生時,Swoole 會回調用戶設置的指定回調函數。
    
Swoole承擔了底層網絡事件的監聽及各類底層事件處理,當收到請求時,會觸發事件提醒,而後將控制權轉交預先註冊的事件回調函數,來進行後續的處理。

能夠理解爲Reactor就是nginx,Worker就是php-fpm。Reactor線程異步並行地處理網絡請求,而後再轉發給Worker進程中去處理。Reactor和Worker間經過UnixSocket進行通訊。

Swoole提供的TaskWorker是一套更完整的方案,將任務的投遞、隊列、php任務處理進程管理合爲一體。經過底層提供的API能夠很是簡單地實現異步任務的處理。

另外TaskWorker還能夠在任務執行完成後,再返回一個結果反饋到Worker。

Swoole的Reactor、Worker、TaskWorker之間能夠緊密的結合起來,提供更高級的使用方式。

一個更通俗的比喻,假設Server就是一個工廠,Master是董事長,Manager是CEO,那Reactor就是銷售經理,接受客戶訂單。而Worker就是工人,當銷售接到訂單後,Worker去工做生產出客戶要的東西。

而TaskWorker能夠理解爲行政人員,能夠幫助Worker幹些瑣事,讓Worker專心工做。
所謂的回調函數(CallBack) 就比如是張開了夾子的捕鼠器,咱們設定當有老鼠踩到捕鼠器的時候,他會關閉夾子而後捉住老鼠,咱們放置捕鼠器的時候,捕鼠器並無真的抓老鼠。這個設定就是回調,他不馬上執行,會在遇到觸發條件(事件)時執行,在上面的示例當中咱們放置了3個捕鼠器(回調函數),咱們只須要知道他會在特定老鼠(事件)踩到的時候(發生的時候)去執行咱們指望的功能就好。
  • 底層會爲Worker進程、TaskWorker進程分配一個惟一的ID
  • 不一樣的Worker和TaskWorker進程之間能夠經過sendMessage接口進行通訊

運行週期

程序全局期

在swoole_server->start以前就建立好的對象,咱們稱之爲程序全局生命週期。
 
 這些變量在程序啓動後就會一直存在,直到整個程序結束運行纔會銷燬。
 
 有一些服務器程序可能會連續運行數月甚至數年纔會關閉/重啓,那麼程序全局期的對象在這段時間持續駐留在內存中的。
 
 程序全局對象所佔用的內存是Worker進程間共享的,不會額外佔用內存。
 
 這部份內存會在寫時分離(COW),在Worker進程內對這些對象進行寫操做時,會自動從共享內存中分離,變爲進程全局對象。
 
 程序全局期include/require的代碼,必須在整個程序shutdown時纔會釋放,reload無效。

進程全局期

swoole擁有進程生命週期控制的機制,一個Worker子進程處理的請求數超過max_request配置後,就會自動銷燬。
 
 Worker進程啓動後建立的對象(onWorkerStart中建立的對象),在這個子進程存活週期以內,是常駐內存的。
 
 onConnect/onReceive/onClose 中均可以去訪問它。
 
 進程全局對象所佔用的內存是在當前子進程內存堆的,並不是共享內存。對此對象的修改僅在當前Worker進程中有效。
 
 進程期include/require的文件,在reload後就會從新加載。

會話期

會話期是在onConnect後建立,或者在第一次onReceive時建立,onClose時銷燬。一個客戶端鏈接進入後,建立的對象會常駐內存,直到此客戶端離開纔會銷燬。
 
 swoole中會話期的對象直接是常駐內存,不須要session_start之類操做。
 
 能夠直接訪問對象,並執行對象的方法。

請求期

請求期就是指一個完整的請求發來,也就是onReceive收到請求開始處理,直到返回結果發送response。
 
 這個週期所建立的對象,會在請求完成後銷燬。
 
 swoole中請求期對象與普通PHP程序中的對象就是同樣的。
 
 請求到來時建立,請求結束後銷燬。

4種PHP回調函數風格

匿名函數

$server->on('Request', function ($req, $resp) use ($a, $b, $c) {
       echo "hello world";
});
可以使用use向匿名函數傳遞參數

類靜態方法

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'));

函數

function my_onRequest($req, $resp)
{
    echo "hello world";
}
$server->on('Request', 'my_onRequest');

編程須知

注意事項

  • 不要在代碼中執行sleep以及其餘睡眠函數,這樣會致使整個進程阻塞
  • 在swoole程序中禁止使用exit/die,若是PHP代碼中有exit/die,當前工做的Worker進程、Task進程、User進程、以及swoole_process進程會當即退出。使用exit/die後Worker進程會由於異常退出, 被master進程再次拉起, 最終形成進程不斷退出又不斷啓動和產生大量警報日誌。
  • mt_rand隨機數,在Swoole中若是在父進程內調用了mt_rand,不一樣的子進程內再調用mt_rand返回的結果會是相同的。因此必須在每一個子進程內調用mt_srand從新播種。
  • while循環的影響,異步程序若是遇到死循環,事件將沒法觸發。異步IO程序使用Reactor模型,運行過程當中必須在reactor->wait處輪詢。若是遇到死循環,那麼程序的控制權就在while中了,reactor沒法獲得控制權,沒法檢測事件,因此IO事件回調函數也將沒法觸發。
  • 可經過register_shutdown_function來捕獲致命錯誤,在進程異常退出時作一些清理工做
  • PHP代碼中若是有異常拋出,必須在回調函數中進行try/catch捕獲異常,不然會致使工做進程退出
  • 不支持set_exception_handler,必須使用try/catch方式處理異常
  • Worker進程不得共用同一個Redis或MySQL等網絡服務客戶端,Redis/MySQL建立鏈接的相關代碼能夠放到onWorkerStart回調函數中。

異步編程

  • 異步程序要求代碼中不得包含任何同步阻塞操做
  • 異步與同步代碼不能混用,一旦應用程序使用了任何同步阻塞的代碼,程序即退化爲同步模式

類/函數重複定義

新手很是容易犯這個錯誤,因爲Swoole是常駐內存的,因此加載類/函數定義的文件後不會釋放。所以引入類/函數的php文件時必需要使用include_once或require_once,否會發生cannot redeclare function/class 的致命錯誤。

進程隔離

進程隔離也是不少新手常常遇到的問題。修改了全局變量的值,爲何不生效,緣由就是全局變量在不一樣的進程,內存空間是隔離的,因此無效。因此使用Swoole開發Server程序須要瞭解進程隔離問題。
  • 不一樣的進程中PHP變量不是共享,即便是全局變量,在A進程內修改了它的值,在B進程內是無效的
  • 若是須要在不一樣的Worker進程內共享數據,能夠用Redis、MySQL、文件、SwooleTable、APCu、shmget等工具實現
  • 不一樣進程的文件句柄是隔離的,因此在A進程建立的Socket鏈接或打開的文件,在B進程內是無效,即便是將它的fd發送到B進程也是不可用的
相關文章
相關標籤/搜索