swoole是一個php擴充,提供了php語言的異步多線程伺服器,異步tcp/udp網絡用戶端,異步mysql,異步redis,資料庫連接配接池,asynctask,消息隊列,毫秒定時器,異步檔案讀寫,異步dns查詢。 swoole内置了http/websocket伺服器端/用戶端、http2.0伺服器端。
swoole: php的異步、并行、高性能網絡通信引擎
<a href="http://www.swoole.com/">http://www.swoole.com/</a>
github:
<a href="https://github.com/swoole">https://github.com/swoole</a>
<a href="https://github.com/matyhtf">https://github.com/matyhtf</a>
swoole需要使用源碼安裝。暫無windows版擴充。
由于pecl是需要編譯的,是以需要先安裝編譯器(已安裝編譯器可以忽略):
然後:
pecl安裝擴充完成後會提示添加so檔案到php.ini。示例:
添加示例:
建議使用的版本(截止時間2017-6-3)
<code>1.9.x</code> 分支已進入特性鎖定期,不再開發新功能,僅修複bug。
最低版本:
建議<code>1.8.6+</code>。php7建議使用<code>1.9.2+</code>。
建議使用的php版本
使用<code>tp3.1+</code>架構的朋友更新到<code>php7.1.0</code>可能會出現rewrite失效問題。建議<code>php7.0.x</code>系列。
快速檢視目前swoole的版本:
我們來使用執行個體進行分析:
繼續在shell中輸入以下指令:
從系統的輸出中,我們可以很容看出server其實有3個程序,程序的pid分别是2454、2456、2458,其中2454是2456的父程序,而2456又是2458的父程序。
是以,其實我們雖然看起來隻是啟動了一個server,其實最後産生的是三個程序。
這三個程序中,所有程序的根程序(2454),就是所謂的<code>master</code>程序;而2456程序,則是<code>manager</code>程序;最後的2458程序,是<code>worker</code>程序。
基于此,我們簡單梳理一下,當執行的start方法之後,發生了什麼:
守護程序模式下,目前程序fork出master程序,然後退出,master程序觸發onmasterstart事件。
master程序啟動成功之後,fork出manager程序,并觸發onmanagerstart事件。
manager程序啟動成功時候,fork出worker程序,并觸發onworkerstart事件。
非守護程序模式下,則目前程序直接作為master程序工作。
是以,一個最基礎的swoole server,至少需要有3個程序,分别是master程序、manager程序和worker程序。
事實上,一個多程序模式下的swoole server中,有且隻有一個master程序;有且隻有一個manager程序;卻可以有n個worker程序。
<code>master</code>程序是一個多線程程序,其中有一組非常重要的線程,叫做<code>reactor</code>線程(組),每當一個用戶端連接配接上伺服器的時候,都會由master程序從已有的reactor線程中,根據一定規則挑選一個,專門負責向這個用戶端提供維持連結、處理網絡io與收發資料等服務。分包拆包等功能也是在這裡完成。
<code>manager</code>程序,某種意義上可以看做一個代理層,它本身并不直接處理業務,其主要工作是将master程序中收到的資料轉交給worker程序,或者将worker程序中希望發給用戶端的資料轉交給master程序進行發送。
<code>manager</code>程序還負責監控worker程序,如果worker程序因為某些意外挂了,manager程序會重新拉起新的worker程序,有點像supervisor的工作。而這個特性,也是最終實作熱重載的核心機制。
<code>worker</code>程序其實就是處理各種業務工作的程序,manager将資料包轉交給worker程序,然後worker程序進行具體的處理,并根據實際情況将結果回報給用戶端。
我們可以總結出來上面簡單的server,當用戶端連接配接的時候這個過程中,三種程序之間是怎麼協作的:
client主動connect的時候,client實際上是與master程序中的某個reactor線程發生了連接配接。
當tcp的三次握手成功了以後,由這個reactor線程将連接配接成功的消息告訴manager程序,再由manager程序轉交給worker程序。
在這個worker程序中觸發了onconnect的方法。
當client向server發送了一個資料包的時候,首先收到資料包的是reactor線程,同時reactor線程會完成組包,再将組好的包交給manager程序,由manager程序轉交給worker。
此時worker程序觸發onreceive事件。
如果在worker程序中做了什麼處理,然後再用send方法将資料發回給用戶端時,資料則會沿着這個路徑逆流而上。
swoole程序/線程結構圖:

現在,我們基于上面的例子修改代碼,來看看一個簡單的多程序swoole server的幾個基本配置:
<code>reactor_num</code>:表示master程序中,reactor線程總共開多少個,注意,這個可不是越多越好,因為計算機的cpu是有限的,是以一般設定為與cpu核心數量相同,或者兩倍即可。
<code>worker_num</code>:表示啟動多少個worker程序,同樣,worker程序數量不是越多越好,仍然設定為與cpu核心數量相同,或者兩倍即可。
我們可以在shell裡運作,使用pstree檢視程序模型結構:
1) 程序啟動時執行的:onstart、onmanagerstart、onworkerstart;onworkerstop、onmanagerstop、onshutdown;onworkererror
2) 用戶端互動時觸發的:onreceive/onrequest/onpacket/onmessage、onopen/onconnect、onclose
3) task:ontask、onfinish
4) timer:ontimer
事件執行順序:
所有事件回調均在<code>$server->start</code>後發生
伺服器關閉程式終止時最後一次事件是<code>onshutdown</code>
伺服器啟動成功後,<code>onstart/onmanagerstart/onworkerstart</code>會在不同的程序内并發執行。
<code>onreceive/onconnect/onclose/ontimer</code>在worker程序(包括task程序)中各自觸發
worker/task程序啟動/結束時會分别調<code>用onworkerstart/onworkerstop</code>
<code>ontask</code>事件僅在task程序中發生
onfinish事件僅在worker程序中發生
<code>onstart/onmanagerstart/onworkerstart</code> 3個事件的執行順序是不确定的
udp協定下隻有<code>onreceive</code>事件,沒有<code>onconnect/onclose</code>事件
如果未設定<code>onpacket</code>回調函數,收到udp資料包預設會回調<code>onreceive</code>函數
<code>onopen</code>事件回調是可選的:當websocket用戶端與伺服器建立連接配接并完成握手後會回調此函數
實際使用的時候不是所有回調都可以使用的,例如udp伺服器沒有<code>onconnect/onclose</code>;例如接收資料,在websocket裡使用onreceive,在httpserver使用onrequest,在udpserver使用onpacket。
示例:
不要在代碼中執行<code>sleep</code>以及其他睡眠函數,這樣會導緻整個程序阻塞
<code>exit/die</code>是危險的,會導緻worker程序退出
php代碼中如果有異常抛出,必須在回調函數中進行<code>try/catch</code>捕獲異常,否則會導緻工作程序退出
swoole不支援<code>set_exception_handler</code>,必須使用<code>try/catch</code>方式處理異常
不能使用類的屬性儲存用戶端連接配接資訊,因為一個worker程序可以處理多個用戶端連接配接,導緻類屬性資料錯亂。常量則是可以的。