swoole的官網文檔其實非常詳細,基礎知識可以直接上官網查看文檔:
https://wiki.swoole.com/wiki/page/1.html
>[info] 本小冊只針對swoole的常用特性以及實際應用做記錄以及部分框架擴展源碼分析,因為我們一般都是基于框架開發,而絕大多數框架都有對應的擴展包(就算沒有,我們知道原理后也可以自己封裝一個),我們這里是**拿thinkphp-swoole來分析其源碼和使用場景**,弄清楚thinkphp-swoole的原理后,easyswoole等其他swoole框架都是大同小異。
## 使用swoole要注意的
* 在`4.2.0`以前的版本不要在代碼中執行`sleep`以及其他睡眠函數,這樣會導致整個進程阻塞。**最新的`4.2.0`版本增加了對`sleep`函數的`Hook`**,底層替換了`sleep`、`usleep`、`time_nanosleep`、`time_sleep_until`四個函數。當調用這些睡眠函數時會自動切換為協程定時器調度。不會阻塞進程。注意要先開啟 `\Swoole\Runtime::enableCoroutine();`
* `exit/die`是危險的,會導致worker進程退出
* 可通過`register_shutdown_function`來捕獲致命錯誤,在進程異常退出時做一些請求工作,具體參看[/wiki/page/305.html](https://wiki.swoole.com/wiki/page/305.html)
* PHP代碼中如果有異常拋出,必須在回調函數中進行`try/catch`捕獲異常,否則會導致工作進程退出
* swoole不支持`set_exception_handler`,必須使用`try/catch`方式處理異常
* Worker進程不得共用同一個`Redis`或`MySQL`等網絡服務客戶端,Redis/MySQL創建連接的相關代碼可以放到`onWorkerStart`回調函數中。原因是如果共用1個連接,那么返回的結果無法保證被哪個進程處理。持有連接的進程理論上都可以對這個連接進行讀寫,這樣數據就發生錯亂了。具體參考[/wiki/page/325.html](https://wiki.swoole.com/wiki/page/325.html)
* 不能使用類的屬性保存客戶端連接信息,因為一個worker進程可以處理多個客戶端連接,導致類屬性數據錯亂。常量則是可以的。
* 要注意 [進程隔離](https://wiki.swoole.com/wiki/page/1038.html) 問題,多進程要共用的變量數據建議通過用`Redis`來處理。
## 知識點理解
1. 不管是http,還是WebSocket,都是繼承于server。所以Server中方法httpserver和websocketServer都可以用。
2. 要理解Swoole是基于**事件回調**的,當作為Server時,[回調函數](https://wiki.swoole.com/wiki/page/41.html)有很多
* 進程啟動時執行的:onStart、onManagerStart、onWorkerStart;onWorkerStop、onManagerStop、onShutdown;onWorkerError
* 客戶端交互時觸發的:onReceive/onRequest/onPacket/onMessage、onOpen/onConnect、onClose
* Task:onTask、onFinish
* Timer:onTimer
3. 事件執行順序
* 所有事件回調均在`$server->start`后發生
* 服務器關閉程序終止時最后一次事件是`onShutdown`
* 服務器啟動成功后,`onStart/onManagerStart/onWorkerStart`會在不同的進程內并發執行。
* `onReceive/onConnect/onClose/onTimer`在worker進程(包括task進程)中各自觸發
* worker/task進程啟動/結束時會分別調`用onWorkerStart/onWorkerStop`
* `onTask`事件僅在task進程中發生
* `onFinish`事件僅在worker進程中發生
* `onStart/onManagerStart/onWorkerStart`3個事件的執行順序是不確定的
* UDP協議下只有`onReceive`事件,沒有`onConnect/onClose`事件
* 如果未設置`onPacket`回調函數,收到UDP數據包默認會回調`onReceive`函數
* `onOpen`事件回調是可選的:當WebSocket客戶端與服務器建立連接并完成握手后會回調此函數
4. 理解其進程和線程模型
* `Master`進程是一個多線程進程,其中有一組非常重要的線程,叫做`Reactor`線程(組),每當一個客戶端連接上服務器的時候,都會由Master進程從已有的Reactor線程中,根據一定規則挑選一個,專門負責向這個客戶端提供維持鏈接、處理網絡IO與收發數據等服務。分包拆包等功能也是在這里完成。
* `Manager`進程,某種意義上可以看做一個代理層,它本身并不直接處理業務,其主要工作是將Master進程中收到的數據轉交給Worker進程,或者將Worker進程中希望發給客戶端的數據轉交給Master進程進行發送。同時還負責監控Worker進程,如果Worker進程因為某些意外掛了,Manager進程會重新拉起新的Worker進程,有點像Supervisor的工作。而這個特性,也是最終實現熱重載的核心機制。
* `Worker`進程其實就是處理各種業務工作的進程,Manager將數據包轉交給Worker進程,然后Worker進程進行具體的處理,并根據實際情況將結果反饋給客戶端

5. `Manager`進程下的`TaskWorker進程`
* 接受由`Worker`進程通過`swoole_server->task/taskwait`方法投遞的任務
* 處理任務,并將結果數據返回(使用`swoole_server->finish`)給`Worker`進程
* 完全是**同步阻塞**模式
* `TaskWorker`以多進程的方式運行
6. Reactor、Worker、TaskWorker的關系
* 可以理解為`Reactor`就是`nginx`,`Worker`就是`php-fpm`。`Reactor`線程異步并行地處理網絡請求,然后再轉發給`Worker`進程中去處理。`Reactor`和`Worker`間通過`UnixSocket`進行通信。
* 在`php-fpm`的應用中,經常會將一個任務異步投遞到`Redis`等隊列中,并在后臺啟動一些`php`進程異步地處理這些任務。`Swoole`提供的`TaskWorker`是一套更完整的方案,將任務的投遞、隊列、`php`任務處理進程管理合為一體。通過底層提供的`API`可以非常簡單地實現異步任務的處理。另外`TaskWorker`還可以在任務執行完成后,再返回一個結果反饋到`Worker`。
* `Swoole`的`Reactor`、`Worker`、`TaskWorker`之間可以緊密的結合起來,提供更高級的使用方式。一個更通俗的比喻,假設`Server`就是一個工廠,那`Reactor`就是銷售,接受客戶訂單。而`Worker`就是工人,當銷售接到訂單后,`Worker`去工作生產出客戶要的東西。而`TaskWorker`可以理解為行政人員,可以幫助`Worker`干些雜事,讓`Worker`專心工作。