一、涉及的計算機基礎(chǔ)知識
通信網(wǎng)絡(luò)
全雙工/單工/半雙工:
1、單工:數(shù)據(jù)只在一個方向上傳輸,不能實現(xiàn)雙方通信。如:閉路電視、廣播。
2、半雙工:允許數(shù)據(jù)在兩個方向上傳輸,但是同一時間數(shù)據(jù)只能在一個方向上傳輸,其實際上是切換的單工。如:對講機
3、全雙工:允許數(shù)據(jù)在兩個方向上同時傳輸。如:手機通話IP/TCP/UDP:
1、ip協(xié)議(網(wǎng)絡(luò)層):IP的責(zé)任就是把數(shù)據(jù)從源傳送到目的地。它不負(fù)責(zé)保證傳送可靠性,流控制,包順序和其它對于主機到主機協(xié)議來說很普通的服務(wù)。
2、tcp(傳輸層):是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議,TCP為了保證不發(fā)生丟包,就給每個包一個序號,同時序號也保證了傳送到接收端實體的包的按序接收。然后接收端實體對已成功收到的包發(fā)回一個相應(yīng)的確認(rèn)(ACK);TCP用一個校驗和函數(shù)來檢驗數(shù)據(jù)是否有錯誤;在發(fā)送和接收時都要計算校驗和。
3、udp(傳輸層):一種無連接的傳輸層協(xié)議,提供面向事務(wù)的簡單不可靠信息傳送服務(wù),UDP有不提供數(shù)據(jù)包分組、組裝和不能對數(shù)據(jù)包進(jìn)行排序的缺點,也就是說,當(dāng)報文發(fā)送之后,是無法得知其是否安全完整到達(dá)的。socket:
socket本質(zhì)是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程序員做網(wǎng)絡(luò)開發(fā)所用的接口,這就是Socket編程接口。在設(shè)計模式中,Socket其實就是一個門面模式,它把復(fù)雜的TCP/IP協(xié)議族隱藏在Socket接口后面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數(shù)據(jù),以符合指定的協(xié)議。websocket:
WebSocket協(xié)議是基于TCP的一種新的網(wǎng)絡(luò)協(xié)議。它實現(xiàn)了瀏覽器與服務(wù)器全雙工(full-duplex)通信,它只需要一次http握手,就可以保持一個長連接,使得服務(wù)器可以主動發(fā)送消息給客戶端,大大減少了輪詢機制的消耗操作系統(tǒng)
進(jìn)程:進(jìn)程是一個具有一定獨立功能的程序關(guān)于某個數(shù)據(jù)集合的一次運行活動。它是操作系統(tǒng)動態(tài)執(zhí)行的基本單元,在傳統(tǒng)的操作系統(tǒng)中,進(jìn)程既是基本的分配單元,也是基本的執(zhí)行單元。進(jìn)程是一個實體。每一個進(jìn)程都有它自己的地址空間,一般情況下,包括文本區(qū)域(text region)、數(shù)據(jù)區(qū)域(data region)和堆棧(stack region)。
線程:通常在一個進(jìn)程中可以包含若干個線程,當(dāng)然一個進(jìn)程中至少有一個線程,不然沒有存在的意義。線程可以利用進(jìn)程所擁有的資源,在引入線程的操作系統(tǒng)中,通常都是把進(jìn)程作為分配資源的基本單位,而把線程作為獨立運行和獨立調(diào)度的基本單位,由于線程比進(jìn)程更小,基本上不擁有系統(tǒng)資源,故對它的調(diào)度所付出的開銷就會小得多,能更高效的提高系統(tǒng)多個程序間并發(fā)執(zhí)行的程度。
協(xié)程:你可能看的最多的就是這樣一句話“協(xié)程就是用戶態(tài)的線程”,用戶態(tài)的線程不是由操作系統(tǒng)來調(diào)度的,而是由程序員來調(diào)度的。PHP程序中,yield這個關(guān)鍵字就是用來產(chǎn)生中斷, 并保存當(dāng)前的上下文的, 比如說程序的一段代碼是訪問遠(yuǎn)程服務(wù)器,那這個時候CPU就是空閑的,就用yield讓出CPU,接著執(zhí)行下一段的代碼,如果下一段代碼還是訪問除CPU以外的其它資源,還可以調(diào)用yield讓出CPU. 繼續(xù)往下執(zhí)行,這樣就可以用同步的方式寫異步的代碼了。
上下文:上下文切換(有時也稱做進(jìn)程切換或任務(wù)切換)是指CPU從一個進(jìn)程或線程切換到另一個進(jìn)程或線程。上下文是指某一時間點 CPU 寄存器和程序計數(shù)器的內(nèi)容。寄存器是 CPU 內(nèi)部的數(shù)量較少但是速度很快的內(nèi)存(與之對應(yīng)的是 CPU 外部相對較慢的 RAM 主內(nèi)存)。寄存器通過對常用值(通常是運算的中間值)的快速訪問來提高計算機程序運行的速度。程序計數(shù)器是一個專用的寄存器,用于表明指令序列中 CPU 正在執(zhí)行的位置,存的值為正在執(zhí)行的指令的位置或者下一個將要被執(zhí)行的指令的位置,具體依賴于特定的系統(tǒng)。
參考文章:計算機操作系統(tǒng)系列文章
二、涉及的PHP基礎(chǔ)知識
五種常見運行模式
- cgi 協(xié)議模式
- fast-cgi 協(xié)議模式,一個常駐內(nèi)存型的cgi。
- 模塊模式,如php作為apache的模塊隨apache啟動而啟動
- php-cli模式,屬于命令行模式
- 其他,如SwooleServer
fast-cgi和cgi都是一種協(xié)議,開啟的進(jìn)程是單獨實現(xiàn)該協(xié)議的進(jìn)程,php-fpm是fastcgi進(jìn)程管理器。
php-fpm啟動->生成n個fast-cgi協(xié)議處理進(jìn)程->監(jiān)聽一個端口等待任務(wù)->用戶請求->web服務(wù)器接收請求->請求轉(zhuǎn)發(fā)給php-fpm->php-fpm交給一個空閑進(jìn)程處理->進(jìn)程處理完成->php-fpm返回給web服務(wù)器->web服務(wù)器接收數(shù)據(jù)->返回給用戶。更詳細(xì)內(nèi)容可參考FastCGI相關(guān)文章
SwooleServer則是相當(dāng)于取代了php-fpm作為管理器的位置, 由于Swoole 是運行在CLI模式下, 所以可以常駐運行和以守護進(jìn)程運行, 但也正因為如此,也需要開發(fā)者自行處理變量的銷毀及各種異常和超時的處理。pcntl_fork多進(jìn)程使用
- pcntl是php官方的多進(jìn)程擴展,只能在linux環(huán)境使用
- 當(dāng)pcntl_fork()創(chuàng)建子進(jìn)程成功時,在父進(jìn)程執(zhí)行線程內(nèi)返回產(chǎn)生的子進(jìn)程的PID,在子進(jìn)程執(zhí)行線程內(nèi)返回0。失敗時,在 父進(jìn)程上下文返回-1,不會創(chuàng)建子進(jìn)程,并且會引發(fā)一個PHP錯誤。
- 子進(jìn)程會復(fù)制父進(jìn)程的狀態(tài),如果在第五行執(zhí)行了pcntl_fork,那么創(chuàng)建出的子進(jìn)程,代碼也是從第五行開始執(zhí)行的。又子進(jìn)程復(fù)制了數(shù)據(jù),代碼。
具體可參考php中pcntl_fork詳解 / PHP多進(jìn)程編之pcntl_forkswoole涉及的其他PHP知識
三、swoole基礎(chǔ)
應(yīng)用場景
Swoole是一個為PHP用C和C++編寫的基于事件的高性能異步&協(xié)程并行網(wǎng)絡(luò)通信引擎。使 PHP 開發(fā)人員可以編寫高性能的異步并發(fā) TCP、UDP、Unix Socket、HTTP,WebSocket 服務(wù)。Swoole 可以廣泛應(yīng)用于互聯(lián)網(wǎng)、移動通信、企業(yè)軟件、云計算、網(wǎng)絡(luò)游戲、物聯(lián)網(wǎng)(IOT)、車聯(lián)網(wǎng)、智能家居等領(lǐng)域。 使用 PHP + Swoole 作為網(wǎng)絡(luò)通信框架,可以使企業(yè) IT 研發(fā)團隊的效率大大提升,更加專注于開發(fā)創(chuàng)新產(chǎn)品。
Swoole高并發(fā)高性能保證:IO復(fù)用、常駐內(nèi)存型、協(xié)程、異步、進(jìn)程池、耗時任務(wù)處理、高性能數(shù)據(jù)結(jié)構(gòu)(hashTable)等。swoole服務(wù)運行結(jié)構(gòu)圖
運行流程圖
進(jìn)程/線程結(jié)構(gòu)圖1
進(jìn)程/線程結(jié)構(gòu)圖2Reactor、Worker、TaskWorker的關(guān)系
三種角色分別的職責(zé)是:
Reactor線程
- 負(fù)責(zé)維護客戶端TCP連接、處理網(wǎng)絡(luò)IO、處理協(xié)議、收發(fā)數(shù)據(jù)
- 完全是異步非阻塞的模式
- 全部為C代碼,除Start/Shudown事件回調(diào)外,不執(zhí)行任何PHP代碼
- 將TCP客戶端發(fā)來的數(shù)據(jù)緩沖、拼接、拆分成完整的一個請求數(shù)據(jù)包
- Reactor以多線程的方式運行
Worker進(jìn)程
- 接受由Reactor線程投遞的請求數(shù)據(jù)包和處理,包括協(xié)議解析和響應(yīng)請求,并執(zhí)行PHP回調(diào)函數(shù)處理數(shù)據(jù)
- 生成響應(yīng)數(shù)據(jù)并發(fā)給Reactor線程,由Reactor線程發(fā)送給TCP客戶端
- 可以是異步非阻塞模式,也可以是同步阻塞模式
- Worker以多進(jìn)程的方式運行
- 未設(shè)置worker_num,底層會啟動與CPU數(shù)量一致的Worker進(jìn)程。
TaskWorker進(jìn)程
- 接受由Worker進(jìn)程通過swoole_server->task/taskwait方法投遞的任務(wù)
- 處理任務(wù),并將結(jié)果數(shù)據(jù)返回(使用swoole_server->finish)給Worker進(jìn)程
- 完全是同步阻塞模式
- TaskWorker以多進(jìn)程的方式運行
三者關(guān)系
可以理解為Reactor就是nginx,Worker就是php-fpm。Reactor線程異步并行地處理網(wǎng)絡(luò)請求,然后再轉(zhuǎn)發(fā)給Worker進(jìn)程中去處理。Reactor和Worker間通過UnixSocket進(jìn)行通信。swoole與nginx有很多原理相似之處,具體可參考 swoole原理和nginx原理對比。
在php-fpm的應(yīng)用中,經(jīng)常會將一個任務(wù)異步投遞到Redis等隊列中,并在后臺啟動一些php進(jìn)程異步地處理這些任務(wù)。Swoole提供的TaskWorker是一套更完整的方案,將任務(wù)的投遞、隊列、php任務(wù)處理進(jìn)程管理合為一體。通過底層提供的API可以非常簡單地實現(xiàn)異步任務(wù)的處理。另外TaskWorker還可以在任務(wù)執(zhí)行完成后,再返回一個結(jié)果反饋到Worker。
Swoole的Reactor、Worker、TaskWorker之間可以緊密的結(jié)合起來,提供更高級的使用方式。
一個更通俗的比喻,假設(shè)Server就是一個工廠,那Reactor就是銷售,接受客戶訂單。而Worker是管理層及行政人員,執(zhí)行小任務(wù)或派發(fā)耗時任務(wù)。當(dāng)銷售接到訂單后,Worker就去處理一些簡單的雜事,而TaskWorker可以理解工人,專門處理worker派發(fā)下來的一些耗時比較長的任務(wù)。
底層會為Worker進(jìn)程、TaskWorker進(jìn)程分配一個唯一的ID,不同的Worker和TaskWorker進(jìn)程之間可以通過sendMessage接口進(jìn)行通信Master 主進(jìn)程、Manager 進(jìn)程
Master 主進(jìn)程:主進(jìn)程內(nèi)有多個Reactor線程,基于epoll/kqueue進(jìn)行網(wǎng)絡(luò)事件輪詢。收到數(shù)據(jù)后轉(zhuǎn)發(fā)到Worker進(jìn)程去處理
Manager 進(jìn)程:對所有Worker進(jìn)程進(jìn)行管理,Worker進(jìn)程生命周期結(jié)束或者發(fā)生異常時自動回收,并創(chuàng)建新的Worker進(jìn)程
swoole常見功能
Swoole 使用純 C 語言編寫,提供了 PHP 語言的異步多線程服務(wù)器,異步 TCP/UDP 網(wǎng)絡(luò)客戶端,異步 MySQL,異步 Redis,數(shù)據(jù)庫連接池,AsyncTask,消息隊列,毫秒定時器,異步文件讀寫,異步DNS查詢。 Swoole內(nèi)置了Http/WebSocket服務(wù)器端/客戶端、Http2.0服務(wù)器端?;A(chǔ)入門可參考快速起步
除了異步 IO 的支持之外,Swoole 為 PHP 多進(jìn)程的模式設(shè)計了多個并發(fā)數(shù)據(jù)結(jié)構(gòu)和IPC(進(jìn)程間通信)通信機制,可以大大簡化多進(jìn)程并發(fā)編程的工作。其中包括了并發(fā)原子計數(shù)器,并發(fā) HashTable,Channel,Lock,進(jìn)程間通信IPC等豐富的功能特性。
Swoole2.0 支持了類似 Go 語言的協(xié)程,可以使用完全同步的代碼實現(xiàn)異步程序。PHP 代碼無需額外增加任何關(guān)鍵詞,底層自動進(jìn)行協(xié)程調(diào)度,實現(xiàn)異步。常見服務(wù)類型
Swoole\Server
創(chuàng)建一個異步服務(wù)器程序,支持TCP、UDP、UnixSocket 3種協(xié)議,支持IPv4和IPv6,支持SSL/TLS單向雙向證書的隧道加密。使用者無需關(guān)注底層實現(xiàn)細(xì)節(jié),僅需要設(shè)置網(wǎng)絡(luò)事件的回調(diào)函數(shù)即可。
請勿在使用Server創(chuàng)建之前調(diào)用其他異步IO的API,否則將會創(chuàng)建失敗??梢栽赟erver啟動后onWorkerStart回調(diào)函數(shù)中使用。
Server只能用于php-cli環(huán)境,在其他環(huán)境下會拋出致命錯誤Swoole\Http\Server
Http\Server繼承自Server,是一個的Http服務(wù)器實現(xiàn)。Http\Server支持同步和異步2種模式。
無論是同步模式還是異步模式,Http\Server都可以維持大量TCP客戶端連接。同步/異步僅僅體現(xiàn)在對請求的處理方式上。
swoole_http_server是swoole_server的子類,內(nèi)置了Http的支持。當(dāng)然不單單只有http,如swoole_redis_server也是swoole_server的子類,內(nèi)置了Redis服務(wù)器端協(xié)議的支持。
http\Server對Http協(xié)議的支持并不完整,建議僅作為應(yīng)用服務(wù)器。并且在前端增加Nginx作為代理Swoole\WebSocket\Server
swoole內(nèi)置的WebSocket服務(wù)器支持,通過幾行PHP代碼就可以寫出一個異步非阻塞多進(jìn)程的WebSocket服務(wù)器。
swoole_websocket_server是swoole_http_server的子類,內(nèi)置了WebSocket的支持自定義進(jìn)程Process:1.7.2版本增加了一個進(jìn)程管理模塊,用來替代PHP的pcntl,Swoole\Process提供了如下特性:
- 基于Unix Socket和sysvmsg消息隊列的進(jìn)程間通信,只需調(diào)用write/read或者push/pop即可
- 支持重定向標(biāo)準(zhǔn)輸入和輸出,在子進(jìn)程內(nèi)echo不會打印屏幕,而是寫入管道,讀鍵盤輸入可以重定向為管道讀取數(shù)據(jù)
- 配合Event模塊,創(chuàng)建的PHP子進(jìn)程可以異步的事件驅(qū)動模式
- 提供了exec接口,創(chuàng)建的進(jìn)程可以執(zhí)行其他程序,與原PHP父進(jìn)程之間可以方便的通信
協(xié)程模式:Swoole2/4版本支持了協(xié)程,使用協(xié)程后事件回調(diào)函數(shù)將會并發(fā)地執(zhí)行。協(xié)程是一種用戶態(tài)線程實現(xiàn),沒有額外的調(diào)度消耗,僅占用內(nèi)存。使用協(xié)程模式,可以理解為“每次事件回調(diào)函數(shù)都會創(chuàng)建一個新的線程去執(zhí)行,事件回調(diào)函數(shù)執(zhí)行完成后,線程退出”。
當(dāng)協(xié)程執(zhí)行完后,底層會恢復(fù)協(xié)程上下文,代碼邏輯繼續(xù)從切換點開始恢復(fù)執(zhí)行。開發(fā)者整個過程不需要關(guān)心整個切換過程??刹榭?a target="_blank">實現(xiàn)原理和Coroutine進(jìn)行調(diào)用并發(fā)HashTable:swoole_table一個基于共享內(nèi)存和鎖實現(xiàn)的超高性能,并發(fā)數(shù)據(jù)結(jié)構(gòu)。用于解決多進(jìn)程/多線程數(shù)據(jù)共享和同步加鎖問題。詳細(xì)可參考swoole_table
- 性能強悍,單線程每秒可讀寫200萬次
- 應(yīng)用代碼無需加鎖,swoole_table內(nèi)置行鎖自旋鎖,所有操作均是多線程/多進(jìn)程安全。用戶層完全不需要考慮數(shù)據(jù)同步問題。
- 支持多進(jìn)程,swoole_table可以用于多進(jìn)程之間共享數(shù)據(jù)
- 使用行鎖,而不是全局鎖,僅當(dāng)2個進(jìn)程在同一CPU時間,并發(fā)讀取同一條數(shù)據(jù)才會進(jìn)行發(fā)生搶鎖
進(jìn)程間通信IPC:此函數(shù)可以向任意Worker進(jìn)程或者Task進(jìn)程發(fā)送消息。在非主進(jìn)程和管理進(jìn)程中可調(diào)用。收到消息的進(jìn)程會觸發(fā)onPipeMessage事件??刹榭?a target="_blank">Server->sendMessage進(jìn)行調(diào)用
Swoole\Server配置參數(shù)
示例: $serv->set(array( 'reactor_num' => 2, //reactor thread num 'worker_num' => 4, //worker process num 'backlog' => 128, //listen backlog 'max_request' => 50, 'dispatch_mode' => 1, ));最大連接:max_conn => 10000, 此參數(shù)用來設(shè)置Server最大允許維持多少個tcp連接。超過此數(shù)量后,新進(jìn)入的連接將被拒絕
守護進(jìn)程化:daemonize => 1,加入此參數(shù)后,執(zhí)行php server.php將轉(zhuǎn)入后臺作為守護進(jìn)程運行
reactor線程數(shù):reactor_num => 2,通過此參數(shù)來調(diào)節(jié)Reactor線程的數(shù)量,以充分利用多核,reactor_num和默認(rèn)設(shè)置為CPU核數(shù)
worker進(jìn)程數(shù):worker_num => 4,設(shè)置啟動的Worker進(jìn)程數(shù)量。Swoole采用固定Worker進(jìn)程的模式。
全異步非阻塞服務(wù)器 worker_num配置為CPU核數(shù)的1-4倍即可。
同步阻塞服務(wù)器,worker_num配置為100或者更高,具體要看每次請求處理的耗時和操作系統(tǒng)負(fù)載狀況
設(shè)定的Worker進(jìn)程數(shù)小于reactor線程數(shù)時,會自動調(diào)低reactor線程的數(shù)量
max_request:max_request => 2000,此參數(shù)表示worker進(jìn)程在處理完n次請求后結(jié)束運行。manager會重新創(chuàng)建一個worker進(jìn)程。此選項用來防止worker進(jìn)程內(nèi)存溢出。
worker進(jìn)程數(shù)據(jù)包分配模式:dispatch_mode = 1 //1平均分配,2按FD取模固定分配,3搶占式分配,默認(rèn)為取模(dispatch=2)
更多配置可參考:Server->set配置信息Swoole\Server事件回調(diào)函數(shù)
Swoole\Server是事件驅(qū)動模式,所有的業(yè)務(wù)邏輯代碼必須寫在事件回調(diào)函數(shù)中。當(dāng)特定的網(wǎng)絡(luò)事件發(fā)生后,底層會主動回調(diào)指定的PHP函數(shù)。共支持13種事件。更詳細(xì)的內(nèi)容可參考:事件回調(diào)函數(shù)
事件執(zhí)行順序:
- 所有事件回調(diào)均在$server->start后發(fā)生
- 服務(wù)器關(guān)閉程序終止時最后一次事件是onShutdown
- 服務(wù)器啟動成功后,onStart/onManagerStart/onWorkerStart會在不同的進(jìn)程內(nèi)并發(fā)執(zhí)行
- onReceive/onConnect/onClose在Worker進(jìn)程中觸發(fā)
- Worker/Task進(jìn)程啟動/結(jié)束時會分別調(diào)用一次onWorkerStart/onWorkerStop
- onTask事件僅在task進(jìn)程中發(fā)生
- onFinish事件僅在worker進(jìn)程中發(fā)生
- onStart/onManagerStart/onWorkerStart 3個事件的執(zhí)行順序是不確定的
參考文章
四、easyswoole框架
easyswoole框架生命周期
具體可參考核心代碼文件:vendor/easyswoole/easyswoole/src/Core.php
框架生命周期服務(wù)類型
- EASYSWOOLE_SERVER (對應(yīng)Swoole\Server)
- EASYSWOOLE_WEB_SERVER (對應(yīng)Swoole\Http\Server)
- EASYSWOOLE_WEB_SOCKET_SERVER (對應(yīng)Swoole\WebSocket\Server)
easyswoole案例演示
tcp請求:
EasySwoole Tcp服務(wù) / tcp demo
#在CliExample目錄下執(zhí)行 php TCPClient3.php 服務(wù)端回復(fù): 1557998515 服務(wù)端回復(fù): your args is:{"name":"\u4ed9\u58eb\u53ef"} #服務(wù)端內(nèi)容響應(yīng): 已連接 tcp服務(wù)3 fd:3 發(fā)送消息:M{"controller":"Index","action":"index","param":{"name":"\u4ed9\u58eb\u53ef"}} tcp服務(wù)3 fd:3 發(fā)送消息:L{"controller":"Index","action":"args","param":{"name":"\u4ed9\u58eb\u53ef"}} tcp服務(wù)3 fd:3 已關(guān)閉服務(wù)端通過addListener開啟9504端口監(jiān)聽,客戶端通過$client->connect('127.0.0.1', 9504, 0.5)發(fā)起鏈接后發(fā)送數(shù)據(jù)。服務(wù)端通過\EasySwoole\Socket\Dispatcher->dispatch進(jìn)行路由解析,并執(zhí)行具體動作后將數(shù)據(jù)返回給客戶端。
http請求:
#按照demo案例添加App/HttpController/Test.php,并訪問以下函數(shù) function index() { $this->response()->write('test index'); } # 訪問 http://mytest.easyswoole.com/Test/index 輸出:test index
壓力測試:
#硬件信息 CPU:1核 Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz 內(nèi)存:1GB 操作系統(tǒng): Ubuntu / 16.04 LTS amd64 (64bit) # ab長連接壓測swoole頁面(ab -c 100 -n 100000 -k http://127.0.0.1:9501/Test/index)返回: Concurrency Level: 100 Time taken for tests: 18.085 seconds Complete requests: 100000 Failed requests: 0 Keep-Alive requests: 100000 Total transferred: 15500000 bytes HTML transferred: 1000000 bytes Requests per second: 5529.44 [#/sec] (mean) Time per request: 18.085 [ms] (mean) Time per request: 0.181 [ms] (mean, across all concurrent requests) Transfer rate: 836.98 [Kbytes/sec] receive #ab非長連接壓測swoole頁面(ab -c 100 -n 100000 http://127.0.0.1:9501/Test/index)返回: Concurrency Level: 100 Time taken for tests: 46.141 seconds Complete requests: 100000 Failed requests: 0 Total transferred: 15000000 bytes HTML transferred: 1000000 bytes Requests per second: 2167.29 [#/sec] (mean) Time per request: 46.141 [ms] (mean) Time per request: 0.461 [ms] (mean, across all concurrent requests) Transfer rate: 317.47 [Kbytes/sec] received #ab長連接壓測nginx頁面(ab -c 100 -n 100000 -k http://127.0.0.1/test.php)返回: Concurrency Level: 100 Time taken for tests: 36.257 seconds Complete requests: 100000 Failed requests: 0 Keep-Alive requests: 0 Total transferred: 14700000 bytes HTML transferred: 1000000 bytes Requests per second: 2758.12 [#/sec] (mean) Time per request: 36.257 [ms] (mean) Time per request: 0.363 [ms] (mean, across all concurrent requests) Transfer rate: 395.94 [Kbytes/sec] receivedswoole頁面的長連接比非長連接qps高出一半,nginx非長連接壓測的qps比swoole非長連接壓測的qps要高
自定義進(jìn)程
Process教程 / 多進(jìn)程 / swoole_process實現(xiàn)多進(jìn)程 / Process自定義進(jìn)程demo
#按照demo部署代碼,并稍微調(diào)整EasySwooleEvent.php下函數(shù)mainServerCreate代碼 public static function mainServerCreate(EventRegister $register) { /** * 除了進(jìn)程名,其余參數(shù)非必須 */ for($i=0;$i<3;$i++){ $myProcess = new ProcessOne("processName".$i,$i,false,2,true); ServerManager::getInstance()->getSwooleServer()->addProcess($myProcess->getProcess()); } } #稍微調(diào)整App/Process/ProcessOne.php代碼 public function run($arg) { Logger::getInstance()->console($this->getProcessName().'-'.$arg." start"); while (1){ \co::sleep(4-$arg); Logger::getInstance()->console($this->getProcessName().'-'.$arg." run"); } } #查詢執(zhí)行的進(jìn)程: root@instance-wjbdyzhe:/data/web/mytest.easyswoole.com# ps -aux|grep processName root 10288 0.0 0.0 14688 908 pts/0 S+ 20:53 0:00 grep --color=auto processName root 24261 0.0 1.2 232932 12308 pts/2 S+ 20:04 0:00 processName0 root 24262 0.0 1.2 232932 12308 pts/2 S+ 20:04 0:00 processName1 root 24263 0.0 1.2 232932 12308 pts/2 S+ 20:04 0:00 processName2 #三個進(jìn)程的執(zhí)行結(jié)果: [2019-04-28 08:56:37][default]processName0-0 start [2019-04-28 08:56:37][default]processName2-2 start [2019-04-28 08:56:37][default]processName1-1 start [2019-04-28 08:56:39][default]processName2-2 run [2019-04-28 08:56:40][default]processName1-1 run [2019-04-28 08:56:41][default]processName0-0 run [2019-04-28 08:56:41][default]processName2-2 run [2019-04-28 08:56:43][default]processName1-1 run [2019-04-28 08:56:43][default]processName2-2 run可以看到以上代碼及執(zhí)行結(jié)果:執(zhí)行程序后,創(chuàng)建了三個自定義進(jìn)程,并且進(jìn)程是并行執(zhí)行的
異步任務(wù):
#按照demo部署代碼,并調(diào)整以下函數(shù) function multiTaskConcurrency(){ // 多任務(wù)并發(fā) $tasks[] = function () { sleep(1);return 'this is 1'; }; // 任務(wù)1 $tasks[] = function () { sleep(5);return 'this is 2'; }; // 任務(wù)2 $tasks[] = function () { sleep(3);return 'this is 3'; }; // 任務(wù)3 $results = \EasySwoole\EasySwoole\Swoole\Task\TaskManager::barrier($tasks, 6); var_dump($results); $this->response()->write('執(zhí)行并發(fā)任務(wù)成功'); } # 訪問 http://mytest.easyswoole.com/index/multiTaskConcurrency 輸出: array(3) { [0]=> string(9) "this is 1" [2]=> string(9) "this is 3" [1]=> string(9) "this is 2" }由代碼和打印來看,雖然任務(wù)2排在第二位,但由于執(zhí)行時間長,執(zhí)行結(jié)果排在最后,由此可知三個任務(wù)并非串行執(zhí)行,而是并行執(zhí)行
連接池
Mysql協(xié)程連接池 / Redis協(xié)程連接池 / 連接池demo
#最大進(jìn)程配置數(shù)8個 'SETTING' => [ 'worker_num' => 8, 'max_request' => 5000, 'task_worker_num' => 8, 'task_max_request' => 1000, ], #每個進(jìn)程連接池最大連接個數(shù)5個 'REDIS' => [ 'host' => '127.0.0.1', 'port' => '6379', 'auth' => 'test', 'POOL_MAX_NUM' => '5', 'POOL_TIME_OUT' => 'utf8mb4', ], #jmeter壓測配置 線程數(shù):100;ramp-up時間:10;循環(huán)次數(shù):30 #通過redis客戶端查看鏈接信息: 127.0.0.1:6379> info clients # Clients connected_clients:41 client_recent_max_input_buffer:4 client_recent_max_output_buffer:0 blocked_clients:0理論最大redis鏈接數(shù) = worker_num*POOL_MAX_NUM = 8*5 = 40
并發(fā)壓測客戶端顯示 = 41-1 = 40(多一個1實際為手動鏈接)
只要不終止swoole/server服務(wù)客戶端連接,顯示一直為41,表示鏈接一直處于鏈接可用狀態(tài)。#最大進(jìn)程配置數(shù)8個 'SETTING' => [ 'worker_num' => 8, 'max_request' => 5000, 'task_worker_num' => 8, 'task_max_request' => 1000, ], #匿名連接池最大配置10個鏈接 'MYSQL3' => [ 'host' => '127.0.0.1',//防止報錯,就不切換數(shù)據(jù)庫了 'port' => '3306', 'user' => 'root', 'timeout' => '5', 'charset' => 'utf8mb4', 'password' => '123456', 'database' => 'test',//防止報錯,就不切換數(shù)據(jù)庫了 'POOL_MAX_NUM' => '10', 'POOL_TIME_OUT' => '0.1' ], #jmeter壓測配置 線程數(shù):100;ramp-up時間:10;循環(huán)次數(shù):30 #通過mysql查看鏈接個數(shù) mysql> show processlist; …… | 306155 | root | 172.17.0.1:48704 | test | Sleep | 30 | | NULL | | 306156 | root | 172.17.0.1:48840 | test | Sleep | 36 | | NULL | | 306157 | root | 172.17.0.1:48846 | test | Sleep | 29 | | NULL | +--------+------+------------------+------+---------+------+----------+------------------+ 81 rows in set (0.00 sec)理論最大mysql鏈接數(shù) = worker_num*POOL_MAX_NUM = 8*10 = 80
并發(fā)壓測客戶端顯示 = 81-1 = 80(多一個1實際為手動鏈接)
過一段再show processlist發(fā)現(xiàn)已無鏈接,可能mysql主動超時中斷,有興趣的同學(xué)可主動排查
更多參考



