[ 典型場景 ]html
典型的多進程服務器模型是這樣的,主進程綁定ip,監聽port,fork幾個子進程,子進程安裝信號處理器,隨後輪詢資源描述符檢查是否可讀可寫;git
子進程的輪詢又涉及到 IO複用,accept鏈接,事件處理 系列操做。github
如下用僞碼錶示這個過程:服務器
Master: bind -> listen -> fork { // Child install signal select loop accept connection event callback } -> monitor
[ 存在缺陷 ]框架
主進程監聽port,存在驚羣問題,客戶端鏈接請求到來時,全部子進程被喚醒,嘗試接受鏈接,但只有一個鏈接成功,其他產生EAGAIN錯誤,並繼續進入輪詢。socket
經過 strace -p pid 能夠看到空閒進程的執行過程:oop
$ select(7, [6], [6], [], {5, 0}) = 0 (Timeout) $ select(7, [6], [6], [], {5, 0}) = 1 (in [6], left {2, 22060}) $ poll([{fd=6, events=POLLIN|POLLERR|POLLHUP}], 1, 10000) = 1 ([{fd=6, revents=POLLIN}]) $ accept(6, 0x7ffe34ffe390, 0x7ffe34ffe380) = -1 EAGAIN (Resource temporarily unavailable) $ poll([{fd=6, events=POLLIN|POLLERR|POLLHUP}], 1, 10000) = 0 (Timeout) $ select(7, [6], [6], [], {5, 0}) = 0 (Timeout)
可想而知,當子進程數量較多時,進程調度(上下文切換)須要耗費的資源很是多。spa
[ 解決方式 ]3d
在PHP7,啓用 socket 的 so_reuseport 選項以後,子進程就能夠在每一個端口上監聽了,有鏈接時才被喚醒。
如下用僞碼錶示這個過程:code
Master: fork { // Child install signal bind listen select loop accept connection event callback } -> monitor
[ 框架應用 ]
phvia/firman(https://github.com/phvia/firman/commit/bace0b2ffda915cc8cf5c73dc009d78a215637d3)