swoole 的認識和強大之處

huxiaobai_001發表於2020-03-29

首先swoole是php的一個擴充套件程式
swoole是一個為php用c和c++編寫的基於事件的高效能非同步&協程並行網路通訊引擎

swoole是一個多程式模型的框架,當啟動一個程式swoole應用時,一共會建立2+n+m個程式,n為worker程式數,m為TaskWorker程式數,1個master程式和一個manager程式,關係如下圖所示
Master程式為主程式,該程式會建立Manager程式、Reactor執行緒等工作進/執行緒
程式、執行緒、協程理解

  1. Reactor執行緒:
  • 負責維護客戶端TCP連線、處理網路IO、處理協議、收發資料
  • 完全是非同步非阻塞的模式
  • 全部為C程式碼,除Start/Shudown事件回撥外,不執行任何PHP程式碼
  • TCP客戶端發來的資料緩衝、拼接、拆分成完整的一個請求資料包
  • Reactor以多執行緒的方式執行
  1. Worker程式:
  • 接受由Reactor執行緒投遞的請求資料包,並執行PHP回撥函式處理資料
  • 生成響應資料併發給Reactor執行緒,由Reactor執行緒傳送給TCP客戶端
  • 可以是非同步非阻塞模式,也可以是同步阻塞模式
  • Worker以多程式的方式執行
  1. TaskWorker程式 :
  • 接受由Worker程式通過swoole_server->task/taskwait方法投遞的任務
  • 處理任務,並將結果資料返回(使用swoole_server->finish)給Worker程式
  • 完全是同步阻塞模式
  • TaskWorker以多程式的方式執行

A:網路通訊引擎

網路通訊引擎,是為php提供網路通訊能力的,傳統的php程式都是啟動php-fpm,前邊再有一層nginx或者apache,開啟瀏覽器訪問就OK,但是從瀏覽器訪問到伺服器這一階段涉及到了網路強求,但是這一階段跟php指令碼沒有任何的關係,php只需要處理好資料生成需要展示的內容就完成使命了,宣告週期當中,請求到來前和請求完成後都沒有php指令碼什麼事,而swoole提供的一大能力就是擴充套件了php的生命週期,無需php-fpm或者nginx或者apache之類的工具幫助就可以啟動一個web服務,並且從服務啟動前,啟動後,連結進入,請求到來,請求結束,連結切斷,服務終止都在php指令碼的掌控之中,這樣的話php指令碼就會涉及到大量的網路通訊處理,而這個網路通訊處理的能力正是來源於swoole! 網路通訊引擎,是為php提供網路通訊能力的,傳統的php程式都是啟動php-fpm,前邊再有一層nginx或者apache,開啟瀏覽器訪問就OK,但是從瀏覽器訪問到伺服器這一階段涉及到了網路強求,但是這一階段跟php指令碼沒有任何的關係,php只需要處理好資料生成需要展示的內容就完成使命了,宣告週期當中,請求到來前和請求完成後都沒有php指令碼什麼事,而swoole提供的一大能力就是擴充套件了php的生命週期,無需php-fpm或者nginx或者apache之類的工具幫助就可以啟動一個web服務,並且從服務啟動前,啟動後,連結進入,請求到來,請求結束,連結切斷,服務終止都在php指令碼的掌控之中,這樣的話php指令碼就會涉及到大量的網路通訊處理,而這個網路通訊處理的能力正是來源於swoole!

B:基於事件的高效能非同步

同步:
swoole的認識和強大之處
就拿讀取檔案內容來說吧
file_get_contents()執行完才能執行下邊的程式碼 這樣就很容易造成程式的阻塞
否則下邊的程式碼就無法輸出檔案的內容
傳統php都是這樣阻塞式的順序執行的
這是常見的同步程式設計
非同步:
swoole的認識和強大之處
程式碼在執行到ajax的時候,函式會直接返回,你馬上就可以看到螢幕上列印出的lol
這就是非同步,這樣你永遠不會被IO阻塞,但是它帶來了新的問題,在你執行到lol之後你就不知道現在程式碼執行到哪裡去了,你只能等待回撥被觸發,然後螢幕上列印響應的log,它的執行不是單層順序的,而是巢狀的
如果在業務程式碼當中 這樣層層巢狀可讀性可想而知
當然這是前端非同步請求後端介面

swoole當中處理非同步回撥巢狀使用的是協程

你知道什麼叫協程嗎?你知道執行緒是幹啥的嗎?你又知道程式嗎?
如果想深入瞭解swoole的強大之處 你還得要了解傳統php的lnmp環境的整套執行機制,這些你都瞭解嗎?
如果不瞭解不要緊 兄嘚 看看我的另一篇部落格吧:
swoole 當中協程的理解包括(程式、執行緒的講解)
講解的也未必清晰,不懂的話自己多去百度!
目前swoole當中的非同步也是協程化的,所以你必須充分理解協程到底是個什麼東西!

C:協程

到底什麼是協程?
通俗的說,協程就是一段段協作方式執行的程式,協作來完成一件事,協作就是協同作業。我們知道團隊協同來做事,比個人單獨來做事,效率肯定要高,因為團隊協同可以發揮各成員的能動性、優勢互補。這是拿人來比喻。我們拿做事來比喻舉個例子:比如我們做飯,比如有以下環節洗菜、切菜、燒水、炒菜、煮米飯,人作為主體來操作,那麼如果按部就班的做,先燒水,再洗菜,在切菜,再炒菜,再煮飯,那這頓飯要做很長時間比如總共30分鐘吧,如果我們通過協同方式,先燒水,放灶火上就可以做其他洗菜、切菜的準備,再煮米飯,然後再來洗菜、切菜,再檢視煮米飯,再炒菜,…,如此迴圈往復切換,最後水燒好,米飯也煮好了,菜也炒好了,飯也OK了,這樣我們耗時可能只有10-15分鐘,看到了嗎,這就是生活中的“協程”,由人來合理排程安排不同的環節,充分利用各種不同的資源和時間,來達到提高效率。協程是計算機程式,呼叫的則是不同的程式,處理者主要由CPU完成,處理物件是各種IO資源,處理的方式是不同的語言編寫的程式。我們知道,CPU可以排程不同的程式,讓程式呼叫不同的IO資源,最初的程式是通過CPU頻繁的切換來完成呼叫程式的,是作業系統按一定演算法分配的時間片搶佔被動方式來切換的,未考慮程式實際執行狀況,這樣切換程式會帶來一定問題,而協程作為一種新的工作模式,可以讓程式協作方式來執行,在需要使用CPU時,交給程式處理,遇到耗時的IO資源操作時會讓出CPU,交給處理其他程式,這樣互相協作來執行,而不是搶佔式的,就像交通規則,大家都遵守按一定規則禮讓先行,不隨便搶道,協同方式,程式都會執行的良好。
我們來看具體的案例:

go(function () {
    echo "hello go1 \n";
});
echo "hello main \n";
go(function () {
    echo "hello go2 \n";
});

上面的程式碼執行結果:

root@b98940b00a9b /v/w/c/p/swoole# php co.php
hello go1
hello main
hello go2

執行結果和我們平時寫程式碼的順序, 好像沒啥區別. 實際執行過程:

  • 執行此段程式碼, 系統啟動一個新程式
  • 遇到 go(), 當前程式中生成一個協程, 協程中輸出 heelo go1, 協程退出
  • 程式繼續向下執行程式碼, 輸出 hello main
  • 再生成一個協程, 協程中輸出 heelo go2, 協程退出
    我們來稍微改一改, 體驗協程的排程:
    use Co;
    go(function () {
    Co::sleep(1); // 只新增了一行程式碼
    echo "hello go1 \n";
    });
    echo "hello main \n";
    go(function () {
    echo "hello go2 \n";
    });
    \Co::sleep() 函式功能和 sleep() 差不多, 但是它模擬的是 IO等待(IO後面會細講). 執行的結果如下:
    root@b98940b00a9b /v/w/c/p/swoole# php co.php
    hello main
    hello go2
    hello go1
    怎麼不是順序執行的呢? 實際執行過程:
  • 執行此段程式碼, 系統啟動一個新程式
  • 遇到 go(), 當前程式中生成一個協程
  • 協程中遇到 IO阻塞 (這裡是 Co::sleep() 模擬出的 IO等待), 協程讓出控制, 進入協程排程佇列
  • 程式繼續向下執行, 輸出 hello main
  • 執行下一個協程, 輸出 hello go2
  • 之前的協程準備就緒, 繼續執行, 輸出 hello go1
    到這裡, 已經可以看到 swoole 中 協程與程式的關係, 以及 協程的排程, 我們再改一改剛才的程式
    go(function () {
    Co::sleep(1);
    echo "hello go1 \n";
    });
    echo "hello main \n";
    go(function () {
    Co::sleep(1);
    echo "hello go2 \n";
    });
    我想你已經知道輸出是什麼樣子了:
    root@b98940b00a9b /v/w/c/p/swoole# php co.php
    hello main
    hello go1
    hello go2
    協程快在哪? 減少IO阻塞導致的效能損失
    大家可能聽到使用協程的最多的理由, 可能就是 協程快. 那看起來和平時寫得差不多的程式碼, 為什麼就要快一些呢? 一個常見的理由是, 可以建立很多個協程來執行任務, 所以快. 這種說法是對的, 不過還停留在表面
    首先, 一般的計算機任務分為 2 種
  • CPU密集型, 比如加減乘除等科學計算
  • IO 密集型, 比如網路請求, 檔案讀寫等
    其次, 高效能相關的 2 個概念
  • 並行: 同一個時刻, 同一個 CPU 只能執行同一個任務, 要同時執行多個任務, 就需要有多個 CPU 才行
  • 併發: 由於 CPU 切換任務非常快, 快到人類可以感知的極限, 就會有很多工 同時執行 的錯覺
    瞭解了這些, 我們再來看協程, 協程適合的是 IO 密集型 應用, 因為協程在 IO阻塞 時會自動排程, 減少IO阻塞導致的時間損失!
    更多案例請參考:https://www.jianshu.com/p/745b0b3ffae7
    協程在遇到IO阻塞的時候會讓出cpu的控制權,其他協程拿到去執行其他協程的任務,當IO阻塞過去之後回過頭來繼續往下執行!
    要點:

1.協程在阻塞的時候只是阻塞了當前這個協程 並不會阻塞整個的程式 因為協程是線上程內部的,即使阻塞了也會讓出控制權,掛起,等待當前協程的IO不阻塞在回過頭來繼續執行,也就是同步的程式碼完成了非同步的功能!相當強悍!
2.從巨集觀的角度看,程式設計師搞出來的多個協程在不發生任何協程阻塞的前提是是順序執行的 一旦發生阻塞 你可以把多個協程理解為並行的 同時在執行的!
3.協程是在單程式單執行緒當中實現的 你可以在裡面實現成千上萬的協程 並且效果極高! 每個協程去幹不同的事!協作制的無需加鎖沒有搶佔,序列的!什麼叫序列呢?每次執行一個協程 遇到IO阻塞 掛起 執行接下來的程式 可能還是個協程 如果再遇到IO阻塞再掛起 繼續往下執行 當IO阻塞完成回過頭來繼續往下執行沒執行完的協程程式 每次都是一個協程在執行,序列化的!
4.協程之間每秒可以進行百萬千萬次切換! 執行緒之間切換需要加鎖 加鎖就很浪費資源!程式間切換更浪費資源,因為上線文很大!更多詳細內容請自行百度!
5.協程很小切換還快 每秒百萬千萬級別的切換 所以 一個程式裡面 只要你的記憶體夠用 你就可以無止境的創造協程出來幹事情!
6.事件驅動和非同步為swoole提供了高效能 而協程解決了非同步回撥程式碼巢狀的問題 提高了程式碼可讀性和維護性 也是swoole最大的特色!

本作品採用《CC 協議》,轉載必須註明作者和本文連結

胡軍

相關文章