swoole 學習筆記

寫的有點亂, 僅供本人蔘考
官方手冊: 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步

  1. onStart
  2. onWorkStart
  3. onReceive
  4. onWorkerStop (worker退出時會回調此函數)
  5. 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()

  • 平滑重啓只對 onWorkerStartonReceive 等在 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,
));





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章