Swoole - 基本概念
Process 與 Thread
Process(行程、進程、處理程序)與 Thread(執行緒、線程)是作業系統中相當重要的概念。因為他們相對比較抽象,且通常 PHP 開發者對於兩者的概念較薄弱,但是在 Swoole 開發中會運用大量 Process 與 Thread 的觀念,所以在開始學 Swoole 之前對於他們必須有基本的了解。
Process
Process 是一個程式執行後實體化的概念,在分時系統
年代中 Process 是程式運作的基本單位。一個程式可以產生多個 Process(一對多關係),若干 Process 有時可能與同一個程式相關聯,且每個 Process 皆可以同步(循序)或非同步(平行)的方式獨立執行。Process 需要一些資源才能完成工作,如:CPU、記憶體、檔案以及 I/O 裝置,且為依序逐一進行,也就是每個 CPU 核心任何時間內僅能執行一個 Process。
在 Windows
系統當中可以在工作管理員
當中來檢視,如:audiodg.exe
、CISVC.exe
等都是一個個 Process。
在 Unix
系統中則可以透過 ps
命令來查看。
ps aux | grep php
Albert 1976 0.0.0.0 2511500 992 s000 ... php artisan socket:serve
Albert 1975 0.0.0.0 2511500 1188 s000 ... php artisan socket:serve
Albert 1974 0.0.0.0 2503180 724 s000 ... php artisan socket:serve
Process 中還另外包含了 Parent
與 Child
的概念,Process 可以透過系統中的 fork
函數來產生自己的 Child Process
,而所產生出來的 Child Process
會從 Parent Process
中複製一份相同的資源出來。如:Process A
同時 fork 出 Process B
與 Process C
,假使 Process A
中宣告了 $a = 1
則 Process B
與 Process C
中也會各有一份一樣的 $a = 1
,但是不同 Process 間的變數修改是不會相互干擾的,也就是說假使今天在 Process A
中更改了 $a = 2
則 Process B
與 Process C
的變數依然還會是 1。
簡單來說,每個 Process 所擁有的資源在執行期間是獨立的,不與其它 Process 共用,所以每個 Process 只能存取屬於自己的部份,不能直接去更改其它 Process 的資料。
Thread
Thread 是作業系統中能夠進行運算排程的最小單位,Thread 被包含在 Process 中,在現代作業系統中,被設計為 Process 中的實際運作單位。也就是一個 Process 中可以包含一個或多個 Thread。在此不考慮 Hyper-Threading
的狀況下,通常一個 CPU Core 在同一時間只能提供一個 Thread 讓系統調用去執行程式。
Process 與 Thread 之間的關係我們可以把它想像成是工廠與員工的關係,工廠擁有資源與設備,但需要由員工去操作才能生產產品。所以要做出一件成品,工廠內至少要有一位員工在做事,不同的 Process 就像是不同的工廠,而 Thread 就像是一位派遣員工一樣,由作業系統負責調度他在什麼時間該去那家工廠上班,如果電腦擁有多個處理核心,即代表系統可以同時調用多個員工。
多任務的實現
試想一下,如果我們今天要使系統能夠同時執行多個任務該如何處理?根據上文的理解,我們可以:
- 啟動多個 Process
- 啟動一個 Process,並在該 Process 中啟動多個 Thread
- 啟動多個 Process,並在每個 Process 中啟動多個 Thread
Linux的 fork
函數通過系统調用即可實現建立一個與原來 Process 幾乎相同的 Process。對於多任務,通常我們會設計 Master-Worker
模式,即一個 Master Process
負責分配任務,多個 Worker Process
負責執行任務。同理,如果是 Multi-Thread
,Master 就是Master Thread
,Worker 就是 Child Thread
。
Multi-Process 與 Multi-Thread
Multi-Process
的優點就是穩定度相當高,如果其中一個 Process 掛掉了,不會影響到其他的 Process。而對於 Multi-Thread
,因為所有的 Thread 都共享同一個 Process 的資源,如果某一個 Thread 掛了,那這個 Process 幾乎就崩溃了。
在性能方面,不論是 Process 還是 Thread,不是啟用越多,效能就會越高,如果啟用數目太多,將會為 CPU 的產生資源調度的問題,因為 Process 或者 Thread 間的切換,本身就非常耗費系統資源。當啟用數量達到一定程度的时候,CPU 和記憶體會被大量消耗,反而會拖累效能甚至使整個系統當掉。
Multi-Thread
與 Multi-Process
相比更加輕量一些,而且 Thread 之間是可以互相共享資源的,所以不同 Thread 之間的交互就比較容易實現。而 Multi-Process
之間的通信,則需透過共享記憶體
或是 Message Queue
等較複雜的方式才可實現。
Swoole 的架構模型
Swoole Server 中有以下幾種角色,分別是:Master(Process)、Reactor(Thread)、Manager(Process)、Worker(Process) 和 TaskWoker(Process)
Master(Process)
Master 是 Swoole 的主要 Process,當我們啟動 Swoole 時,當下的 Process 就會成為 Master Process 並負責建立 Main Reactor、建立和管理 Reactor、建立 Manager 並開始接受客戶端請求等工作。
首先 Master 會透過 fork
函數建立一個 Manager,接著建立 Reactor,當全部建立完成後會呼叫 onStart
的 callback function
,此時可以在這個 callback function
中對 Master 做一些處理或修改。如:將 Master 重新命名,保存 Master 的 PID 檔案等。
這裡要特別注意,因為 Master 不應該存在任何商業邏輯,因此 Swoole 底層會禁止你在此處做一些行為,如:發送請求、呼叫 Swoole 的異步 API 等等。
Reactor(Thread)
Reactor 以 Multi-Thread
的方式執行,Reactor 底層由 epoll
函數來實現(在Mac系統是透過 Kqueue
),用於實際監聽和處理來自客戶端的 Connect 請求,並完全是透過異步、非阻塞的模式來運作。
Manager(Process)
Swoole 中 Worker
/Task worker
都是由 Manager 所 fork
並管理的。當 Manager 被建立時,會呼叫 onManagerStart
的 callback function
通知上層的應用。
當底下的某個 Worker
意外結束執行時,Manager 會負責回收的工作,並同時建立新的 Worker
補足固定的數量維持 Swoole 的正常運作。
當 Manager 退出時,會呼叫 onManagerStop
的 callback function
,可以利用此時進行一些回收邏輯。
Worker(Process)
Worker 以 Multi-Process
的方式執行,是 Swoole 中執行大部分邏輯的地方,因此在 Worker 中所對應的 callback function
數量也是最多的。
當 Worker 啟動後會呼叫 onWorkerStart
的 callback function
,在這裡我們就能夠使用全部 Swoole 所提供的 API 了。
當上層的 Reactor 收到客戶端的請求後,就會將數據打包發送給 Worker,並在相對應的 callback function
中(如:onReceive
、onRequest
、onMessage
等)處理這些資料,並可以將處理結果回傳給 Reactor,再回傳至客戶端中。如果 Worker 正常退出,會呼叫 onWorkerStop
的 callback function
,若是處理數據過程中出現嚴重錯誤或者 Worker 請求達到處理上限時,則會呼叫 onWorkerError
的 callback function
並結束該 Worker。
Task Worker(Process)
Task Worker 一樣以 Multi-Process
的方式執行,只接受由 Worker 中透過 swoole_server->task/taskwait
方法指派過來的任務,並將結果回傳給 Worker。
Process 間的通訊
在 Swoole 中,Process 間的通訊可以分為以下幾三種狀況:
Master
<=>Worker
Worker
<=>Worker
Worker
<=>Task Worker
前兩種情形,在 Swoole 底層統一使用 Unix Socket
進行通訊,這些 socket 也都歸併到各自 Process 的 Reactor 中進行管理。而第三種情況,除了預設的 Unix Socket
外,還可以使用由系統提供的 Message Queue
来實作。
跨 Process 通訊
若你有不同 Worker Process 間需要共用變數的需求,基本上你有幾個方案:
- 使用 apcu, swoole table, yac, shmop 等共享記憶體的擴展
- 使用資料庫 redis, memcache, mysql
- 使用檔案系統 /dev/shm, /tmp 等記憶體檔案