寫的有點亂, 僅供本人蔘考
官方手冊: https://wiki.swoole.com/
協程
,是爲了提高併發的,如果我的應用就沒有高併發,或者必須要用某些無法異步化 IO 的操作 (例如上文的 MongoDB),那麼你完全可以不開啓一鍵協程化,關閉 enable_coroutine
,多開一些 Worker
進程,這就是和 Fpm/Apache
是一樣的模型了,值得一提的是由於 Swoole 是常駐進程的,即使同步 IO 性能也會有很大提升,實際應用中也有很多公司這樣做。
PHP-FPM vs Swoole
https://learnku.com/articles/9450/php-fpm-vs-swoole
MINIT -> RINIT -> 執行PHP腳本 -> RSHUTDOWN -> MSHUTDOWN
PHP-FPM
每個請求都是在執行2~4
步, 只有停止PHP-FPM, 纔會執行MSHUTDOWN
swoole
在第3
步後進入swoole的生命週期, 只有停止swoole服務, 纔會退出然後繼續php的4, 5步
- onStart
- onWorkStart
- onReceive
- onWorkerStop (worker退出時會回調此函數)
- onShutDown (swoole服務停止會回調此函數)
參考: https://blog.csdn.net/sb___itfk/article/details/52957539
Server中對象的4層生命週期 (重要)
https://wiki.swoole.com/wiki/page/354.html
程序全局期
在Server->start
之前就創建好的對象,在Worker
進程內對這些對象進行寫操作時,會自動從共享內存中分離,變爲進程全局對象
程序全局期include/require
的代碼,必須在整個程序shutdown
時纔會釋放,reload
無效
進程全局期
Worker
進程啓動後創建的對象(onWorkerStart
中創建的對象),在這個子進程存活週期之內,是常駐內存的,onConnect/onReceive/onClose
中都可以去訪問它
每個Worker
子進程處理的請求數超過max_request
配置後,就會自動銷燬
進程全局對象所佔用的內存是在當前子進程內存堆的,並非共享內存,對此對象的修改僅在當前Worker
進程中有效
進程期include/require
的文件,在reload
後就會重新加載
會話期
會話期是在onConnect
後創建,或者在第一次onReceive/onRequest
時創建,onClose
時銷燬
請求期
指一個完整的請求發來,也就是onReceive/onRequest
收到請求開始處理,直到返回結果發送response
請求期對象與普通PHP程序中的對象就是一樣的。請求到來時創建,請求結束後銷燬
Server內存管理機制
https://wiki.swoole.com/wiki/page/p-zend_mm.html
Master 進程、Reactor 線程、Worker 進程、Task 進程、Manager 進程的區別與聯繫
https://wiki.swoole.com/#/learn?id=diff-process
Server 的兩種運行模式介紹 (SWOOLE_BASE 和 SWOOLE_PROCESS)
https://wiki.swoole.com/#/learn?id=server的兩種運行模式介紹
SWOOLE_PROCESS
如最下圖的模型 有Master
進程、Reactor
線程、Worker
進程、Task
進程、Manager
進程
SWOOLE_BASE
沒有Master
進程,如nginx
一樣,連接請求進來的時候,所有的 Worker
進程去爭搶這一個連接
當設置 worker_num=1
時,沒有Manager
進程
Swoole 如何正確的重啓服務
https://wiki.swoole.com/#/question/use?id=swoole如何正確的重啓服務
$server->reload()
- 平滑重啓只對
onWorkerStart
或onReceive
等在 Worker 進程中include/require
的 PHP 文件有效 - Server 啓動前就已經
include/require
的 PHP 文件,不能通過平滑重啓重新加載 - 對於 Server 的配置即
$serv->set()
中傳入的參數設置,必須關閉 / 重啓整個 Server 纔可以重新加載 - Server 可以監聽一個內網端口,然後可以接收遠程的控制命令,去重啓所有 Worker 進程
全局變量
協程使得原有的異步邏輯同步化,但是在協程的切換是隱式發生的,所以在協程切換的前後不能保證全局變量以及 static 變量的一致性。
在 PHP-FPM
下可以通過全局變量獲取到請求的參數,服務器的參數等,在 Swoole 內,無法 通過 $_GET/$_POST/$_REQUEST/$_SESSION/$_COOKIE/$_SERVER
等 $_
開頭的變量獲取到任何屬性參數。
可以使用 context
用協程 id 做隔離,實現全局變量的隔離。
是否可以共用 1 個 Redis 或 MySQL 連接
絕對不可以。必須每個進程單獨創建 Redis、MySQL、PDO 連接,其他的存儲客戶端同樣也是如此。
原因是如果共用 1 個連接,那麼返回的結果無法保證被哪個進程處理,持有連接的進程理論上都可以對這個連接進行讀寫,這樣數據就發生錯亂了。
創建連接的相關代碼可以放到 onWorkerStart
回調函數中
swoole 協程 與 go 協程 區別
https://wiki.swoole.com/wiki/page/p-differences_with_go.html
- Swoole的協程調度器是單線程的,因此不存在數據同步問題,同一時間只會有一個協程在運行
- Go協程調度器是多線程的,同一時間可能會有多個協程同時執行
- Swoole協程中操作全局變量是不需要加鎖,Go是多線程需加鎖避免數據錯亂
swoole多進程間通信需要 Swoole\Table
(共享內存) 或 Swoole\Atomic
(無鎖計算器), 加鎖用 Swoole\Lock
(進程間鎖)
配置 (Server::set())
https://wiki.swoole.com/#/server/setting
worker_num
設置啓動的 Worker
進程數。【默認值:CPU 核數】
心跳檢測1 (主動
發送心跳包)
open_tcp_keepalive
$server->set(array(
'open_tcp_keepalive' => 1,
'tcp_keepidle' => 4, //4s沒有數據傳輸就進行檢測
'tcp_keepinterval' => 1, //1s探測一次
'tcp_keepcount' => 5, //探測的次數,超過5次後還沒回包close此連接
));
心跳檢測2 (僅支持TCP連接
) 被動
等待數據包
heartbeat_check_interval
【默認值:false】
此選項表示每隔多久輪循一次,單位爲秒,沒有向服務器發送任何數據,此連接將被強制關閉。
heartbeat_idle_time
連接最大允許空閒的時間【未設置時默認爲 heartbeat_check_interval
的兩倍】
配合客戶端使用 swoole_timer_tick
定時發送數據包
解決內存泄漏問題
max_request
設置 worker
進程的最大任務數。【默認值:0 即不會退出進程】
一個 worker
進程在處理完超過此數值的任務後將自動退出,進程退出後會釋放所有內存和資源
TCP沾包問題
TCP 協議是流式的,數據包沒有邊界,併發高了就會有粘包問題
解決方案:
固定包頭 + 包體協議
$server->set(array(
'open_length_check' => true,
'package_max_length' => 81920,
'package_length_type' => 'n', //see php pack()
'package_length_offset' => 0,
'package_body_offset' => 4,
));