1. 程式人生 > 實用技巧 >面試官:swoole 的認識和強大之處你不知道?回去吧!!!

面試官:swoole 的認識和強大之處你不知道?回去吧!!!

首先 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 以多執行緒的方式執行

2、Worker 程序:

  • 接受由 Reactor 執行緒投遞的請求資料包,並執行 PHP 回撥函式處理資料

  • 生成響應資料併發給 Reactor 執行緒,由 Reactor 執行緒傳送給 TCP 客戶端

  • 可以是非同步非阻塞模式,也可以是同步阻塞模式

  • Worker 以多程序的方式執行

3、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:基於事件的高效能非同步

同步:

就拿讀取檔案內容來說吧

file_get_contents () 執行完才能執行下邊的程式碼 這樣就很容易造成程式的阻塞

否則下邊的程式碼就無法輸出檔案的內容

傳統 php 都是這樣阻塞式的順序執行的

這是常見的同步程式設計

非同步:

程式碼在執行到 ajax 的時候,函式會直接返回,你馬上就可以看到螢幕上打印出的 lol

這就是非同步,這樣你永遠不會被 IO 阻塞,但是它帶來了新的問題,在你執行到 lol 之後你就不知道現在程式碼執行到哪裡去了,你只能等待回撥被觸發,然後螢幕上列印響應的 log, 它的執行不是單層順序的,而是巢狀的

如果在業務程式碼當中 這樣層層巢狀可讀性可想而知

當然這是前端非同步請求後端介面

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

你知道什麼叫協程嗎?你知道執行緒是幹啥的嗎?你又知道程序嗎?

如果想深入瞭解 swoole 的強大之處 你還得要了解傳統 php 的 lnmp 環境的整套執行機制,這些你都瞭解嗎?

目前 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 阻塞導致的時間損失!

協程在遇到 IO 阻塞的時候會讓出 cpu 的控制權,其他協程拿到去執行其他協程的任務,當 IO 阻塞過去之後回過頭來繼續往下執行!

要點:

  1. 協程在阻塞的時候只是阻塞了當前這個協程 並不會阻塞整個的程序 因為協程是線上程內部的,即使阻塞了也會讓出控制權,掛起,等待當前協程的 IO 不阻塞在回過頭來繼續執行,也就是同步的程式碼完成了非同步的功能!相當強悍!

  2. 從巨集觀的角度看,程式設計師搞出來的多個協程在不發生任何協程阻塞的前提是是順序執行的 一旦發生阻塞 你可以把多個協程理解為並行的 同時在執行的!

  3. 協程是在單程序單執行緒當中實現的 你可以在裡面實現成千上萬的協程 並且效果極高! 每個協程去幹不同的事!協作制的無需加鎖沒有搶佔,序列的!什麼叫序列呢?每次執行一個協程 遇到 IO 阻塞 掛起 執行接下來的程式 可能還是個協程 如果再遇到 IO 阻塞再掛起 繼續往下執行 當 IO 阻塞完成回過頭來繼續往下執行沒執行完的協程程式 每次都是一個協程在執行,序列化的!

  4. 協程之間每秒可以進行百萬千萬次切換! 執行緒之間切換需要加鎖 加鎖就很浪費資源!程序間切換更浪費資源,因為上線文很大!更多詳細內容請自行百度!

  5. 協程很小切換還快 每秒百萬千萬級別的切換 所以 一個程序裡面 只要你的記憶體夠用 你就可以無止境的創造協程出來幹事情!

  6. 事件驅動和非同步為 swoole 提供了高效能 而協程解決了非同步回撥程式碼巢狀的問題 提高了程式碼可讀性和維護性 也是 swoole 最大的特色!

d:混合伺服器

你可以隨意建立一個 http tcp websocket http2 服務 並且能輕鬆承載成千上萬的請求

這個可以去官網 看如何建立 http 服務 tcp 服務 websocket 服務

websocket 是 http 升級而來的 建立了 websocket 服務就自帶了 http 服務

建立好服務 直接 start 就可以執行一個 http 服務或者其他 tcp 等的服務了 而無需 nginx apache 的任何參與!

有服務端必有客戶端 我們可以在程式碼裡面通過
new Swoole\Coroutine\Http\Client(127.0.0.1,9501);
去連結 http 伺服器

當然其他的也一樣 自己去看手冊 這裡只是說明 swoole 有多麼的強大

e:協程之間通訊 channel

通道(channel)是協程之間通訊交換資料的唯一渠道,而協程 + 通道的開發組合即為著名的 csp 程式設計模型

在 swoole 當中 channel 常用於連線池的實現和協程併發的排程

如圖所示 第一個協程執行完成之後我們會往 channel 通道當中 push 一個元素 第二個也是 當然第一個一定是 IO 阻塞的 第二個沒有 我們在 for 迴圈裡面 獲取 channel 裡面的值的時候由於第一個阻塞 $chan->pop () 也是阻塞的 因為整體都是在一個協程裡面 只有當大的協程裡面的兩個小協程都完成了 這裡的 $chan->pop 才會執行 接觸阻塞 最後才會執行 echo 語句! 這是 swoole 當中協程併發的一個很好的案例應用

f:毫秒定時器

毫秒定時器是非同步回撥的方式來實現的!

還可以使用協程方式 採用同步阻塞的方式來實現定時器!
之前用 sleep 會阻塞整個程序 現在你在協程裡面搞 Co:sleep (0.1) 阻塞的是當前協程 而不會阻塞整個程序!

點關注,不迷路

好了各位,以上就是這篇文章的全部內容了,能看到這裡的人呀,都是人才。之前說過,PHP方面的技術點很多,也是因為太多了,實在是寫不過來,寫過來了大家也不會看的太多,所以我這裡把它整理成了PDF和文件,如果有需要的可以

點選進入暗號: PHP+「平臺」


更多學習內容可以訪問【對標大廠】精品PHP架構師教程目錄大全,只要你能看完保證薪資上升一個臺階(持續更新)

以上內容希望幫助到大家,很多PHPer在進階的時候總會遇到一些問題和瓶頸,業務程式碼寫多了沒有方向感,不知道該從那裡入手去提升,對此我整理了一些資料,包括但不限於:分散式架構、高可擴充套件、高效能、高併發、伺服器效能調優、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql優化、shell指令碼、Docker、微服務、Nginx等多個知識點高階進階乾貨需要的可以免費分享給大家,需要的可以加入我的 PHP技術交流群