在 Mix V2.1 之前的版本一直采用與現(xiàn)在主流的 Swoole 框架一樣基于多進(jìn)程 Swoole\Server 開(kāi)發(fā),V2.1 開(kāi)始 Mix 全部基于 Swoole 的 Coroutine/Server 開(kāi)發(fā) (單線程協(xié)程),讓 Server 也可使用完全同步的編程方式,底層自動(dòng)實(shí)現(xiàn)異步IO。
Reactor+Manager+Worker 多進(jìn)程優(yōu)缺點(diǎn)
Master+Worker 的進(jìn)程模型是一種應(yīng)用廣泛的傳統(tǒng)模型,像 Nginx、PHP-FPM 均采用這種模型,但 Swoole 的模型中多了一個(gè) Manager 進(jìn)程。

優(yōu)點(diǎn):
- 可自行重啟:
Reactor+Manager+Worker 進(jìn)程架構(gòu)最大的優(yōu)點(diǎn)就是,當(dāng) Worker 出現(xiàn)任何異常,無(wú)需借助第三方 (如:supervisor、pm2) 即可由程序本身的 Manager 進(jìn)程重啟一個(gè) Worker 進(jìn)程。 - 可利用多CPU:
由于是多進(jìn)程模型,Worker 進(jìn)程執(zhí)行在多個(gè)CPU中,因此可利用到多核。 - 阻塞影響?。?br> 因?yàn)?Reactor 多線程負(fù)責(zé)連接處理,而多個(gè) Worker 進(jìn)程負(fù)責(zé)執(zhí)行 PHP 代碼,因此單個(gè) Worker 的阻塞IO并不會(huì)影響其他請(qǐng)求 (協(xié)程模式,會(huì)影響到分配到該進(jìn)程但沒(méi)有執(zhí)行完成的請(qǐng)求)
缺點(diǎn):
- 編碼復(fù)雜、不夠靈活:
Swoole\Server 在啟動(dòng)前與啟動(dòng)后 class 的屬性操作的作用域,對(duì)不熟悉的用戶非常不友好,容易掉坑,難以理解。處理一些全局業(yè)務(wù)時(shí)需要跨進(jìn)程處理,帶來(lái)并發(fā)安全鎖的問(wèn)題,且無(wú)法同時(shí)啟動(dòng)多個(gè) Server 或者自由的在代碼中隨意啟動(dòng)停止 Server。 - 平滑shutdown困難:
由于程序執(zhí)行在很多個(gè) Worker 進(jìn)程中,如果存在耗時(shí)任務(wù),多進(jìn)程模型很難精細(xì)的控制每個(gè)子進(jìn)程執(zhí)行完管道的全部請(qǐng)求,然后平滑有序的退出所有進(jìn)程,Swoole\Server 雖然提供了 異步安全重啟特性 但是大部分框架是沒(méi)有處理 onWorkerExit 的,用戶也很難在框架基礎(chǔ)上擴(kuò)展。
單線程協(xié)程優(yōu)缺點(diǎn)
單線程模型在新創(chuàng)軟件領(lǐng)域非常流行,像 Redis、Node.js 都是單線程模型,而優(yōu)缺點(diǎn)剛好與 Reactor+Manager+Worker 相反,但是 Mix 針對(duì)這些缺點(diǎn)提供了解決方案。
缺點(diǎn):
- 無(wú)法自行重啟:
Mix V2.1 這個(gè)問(wèn)題普遍存在于所有單進(jìn)程程序中,包括 Golang、Node.js 開(kāi)發(fā)的程序,在出現(xiàn)致命異常時(shí)都會(huì)導(dǎo)致進(jìn)程退出,由于是單進(jìn)程所以無(wú)法自行重啟,都需要借助第三方 (如:supervisor、pm2) - 無(wú)法利用多CPU:
由于是單進(jìn)程模型,執(zhí)行在一個(gè)CPU中,因此和 Node.js 一樣無(wú)法利用多核 (使用 docker 部署則無(wú)此問(wèn)題)。 - 阻塞影響大:
單進(jìn)程模型由于 Server 與業(yè)務(wù)邏輯一同執(zhí)行在單個(gè)進(jìn)程內(nèi),因此當(dāng)遇到阻塞IO時(shí),所有請(qǐng)求處理都會(huì)一起阻塞,導(dǎo)致響應(yīng)時(shí)間變長(zhǎng),長(zhǎng)時(shí)間的阻塞還會(huì)導(dǎo)致服務(wù)無(wú)法接收新的請(qǐng)求,Swoole 雖然 Hook 了大量的阻塞IO能支持協(xié)程,但還是有許多擴(kuò)展是無(wú)法支持的,因此該問(wèn)題最為突出。
優(yōu)點(diǎn):
- 編碼簡(jiǎn)單、靈活:
由于是單進(jìn)程單線程模型,加上 Server 全部基于 Swoole 的 Coroutine/Server 開(kāi)發(fā),完全同步的編程方式,因此代碼非常簡(jiǎn)單易懂。也沒(méi)有了多進(jìn)程帶來(lái)的:跨進(jìn)程對(duì)象屬性分歧問(wèn)題,并發(fā)安全鎖問(wèn)題,Server 也可隨意啟動(dòng) N 個(gè)、隨意停止。 - 平滑shutdown簡(jiǎn)單:
單線程模型,處理信號(hào)只需處理一個(gè)進(jìn)程,退出也只需處理一個(gè)進(jìn)程,由于是同步編程方式,退出的處理邏輯也非常簡(jiǎn)單易懂。
對(duì)缺點(diǎn)的解決方案
通過(guò)上面分析可以得出結(jié)論:?jiǎn)尉€程更加簡(jiǎn)單靈活,但是最大的缺點(diǎn)就是阻塞問(wèn)題,和多CPU利用問(wèn)題,Mix V2.1 如何解決這些問(wèn)題:
多CPU利用:
與 Node.js 解決該問(wèn)題的方法一樣,通過(guò)多開(kāi)進(jìn)程來(lái)解決多CPU利用問(wèn)題,由于同一個(gè)端口只能被一個(gè)程序綁定,因此多開(kāi)只能綁定不同端口,勢(shì)必增加了反向代理的復(fù)雜度,但在 Linux v3.10 或更高版本內(nèi)核中加入了端口復(fù)用功能,Mix 內(nèi)置的 Server 只需啟動(dòng)時(shí)增加-r參數(shù)即可在 Linux 中多開(kāi)端口復(fù)用,達(dá)到與 Node.js 類似的解決多CPU利用的效果,查看范例。阻塞:
單進(jìn)程單線程模型中要徹底解決 PHP 歷史遺留的阻塞IO問(wèn)題,只能通過(guò)把阻塞代碼集中放入到其他一個(gè)或者多個(gè)進(jìn)程中去執(zhí)行,類似 Swoole 的 Task,不同的是 Mix 獲取執(zhí)行結(jié)果是同步編程方式,Mix 會(huì)封裝一個(gè)同步執(zhí)行器,在代碼中調(diào)用同步執(zhí)行器傳入一個(gè)閉包,閉包中包含阻塞IO調(diào)用代碼,調(diào)用后同步獲取結(jié)果(協(xié)程方式),該閉包的代碼將通過(guò) unixsocket 的方式傳遞到同步執(zhí)行器的進(jìn)程執(zhí)行,執(zhí)行完成后回傳結(jié)果。該同步執(zhí)行器進(jìn)程與其他 Server 一樣可自行添加代碼,可在啟動(dòng)時(shí)增加-r參數(shù)端口復(fù)用多開(kāi),Mix 通過(guò)這種方式將阻塞代碼集中在同步執(zhí)行器進(jìn)程中執(zhí)行,徹底避免阻塞帶來(lái)的影響。
解決這些問(wèn)題后,綜合考量單線程協(xié)程顯然更加符合 Mix 短小精悍、簡(jiǎn)單易用的定位,Mix V2.1 是截止現(xiàn)在唯一全面使用 Coroutine/Server 的框架。