一央拖、涉及的計(jì)算機(jī)基礎(chǔ)知識
通信網(wǎng)絡(luò)
全雙工/單工/半雙工:
1祭阀、單工:數(shù)據(jù)只在一個方向上傳輸,不能實(shí)現(xiàn)雙方通信爬泥。如:閉路電視柬讨、廣播。
2袍啡、半雙工:允許數(shù)據(jù)在兩個方向上傳輸踩官,但是同一時間數(shù)據(jù)只能在一個方向上傳輸,其實(shí)際上是切換的單工境输。如:對講機(jī)
3蔗牡、全雙工:允許數(shù)據(jù)在兩個方向上同時傳輸。如:手機(jī)通話IP/TCP/UDP:
1嗅剖、ip協(xié)議(網(wǎng)絡(luò)層):IP的責(zé)任就是把數(shù)據(jù)從源傳送到目的地辩越。它不負(fù)責(zé)保證傳送可靠性,流控制信粮,包順序和其它對于主機(jī)到主機(jī)協(xié)議來說很普通的服務(wù)黔攒。
2、tcp(傳輸層):是一種面向連接的强缘、可靠的督惰、基于字節(jié)流的傳輸層通信協(xié)議,TCP為了保證不發(fā)生丟包旅掂,就給每個包一個序號赏胚,同時序號也保證了傳送到接收端實(shí)體的包的按序接收。然后接收端實(shí)體對已成功收到的包發(fā)回一個相應(yīng)的確認(rèn)(ACK)商虐;TCP用一個校驗(yàn)和函數(shù)來檢驗(yàn)數(shù)據(jù)是否有錯誤觉阅;在發(fā)送和接收時都要計(jì)算校驗(yàn)和崖疤。
3、udp(傳輸層):一種無連接的傳輸層協(xié)議典勇,提供面向事務(wù)的簡單不可靠信息傳送服務(wù)劫哼,UDP有不提供數(shù)據(jù)包分組、組裝和不能對數(shù)據(jù)包進(jìn)行排序的缺點(diǎn)割笙,也就是說沦偎,當(dāng)報(bào)文發(fā)送之后,是無法得知其是否安全完整到達(dá)的咳蔚。socket:
socket本質(zhì)是編程接口(API),對TCP/IP的封裝搔驼,TCP/IP也要提供可供程序員做網(wǎng)絡(luò)開發(fā)所用的接口谈火,這就是Socket編程接口。在設(shè)計(jì)模式中舌涨,Socket其實(shí)就是一個門面模式糯耍,它把復(fù)雜的TCP/IP協(xié)議族隱藏在Socket接口后面,對用戶來說囊嘉,一組簡單的接口就是全部温技,讓Socket去組織數(shù)據(jù),以符合指定的協(xié)議扭粱。websocket:
WebSocket協(xié)議是基于TCP的一種新的網(wǎng)絡(luò)協(xié)議舵鳞。它實(shí)現(xiàn)了瀏覽器與服務(wù)器全雙工(full-duplex)通信,它只需要一次http握手,就可以保持一個長連接,使得服務(wù)器可以主動發(fā)送消息給客戶端,大大減少了輪詢機(jī)制的消耗操作系統(tǒng)
進(jìn)程:進(jìn)程是一個具有一定獨(dú)立功能的程序關(guān)于某個數(shù)據(jù)集合的一次運(yùn)行活動琢蛤。它是操作系統(tǒng)動態(tài)執(zhí)行的基本單元蜓堕,在傳統(tǒng)的操作系統(tǒng)中,進(jìn)程既是基本的分配單元博其,也是基本的執(zhí)行單元套才。進(jìn)程是一個實(shí)體。每一個進(jìn)程都有它自己的地址空間慕淡,一般情況下背伴,包括文本區(qū)域(text region)、數(shù)據(jù)區(qū)域(data region)和堆棧(stack region)峰髓。
線程:通常在一個進(jìn)程中可以包含若干個線程傻寂,當(dāng)然一個進(jìn)程中至少有一個線程,不然沒有存在的意義儿普。線程可以利用進(jìn)程所擁有的資源崎逃,在引入線程的操作系統(tǒng)中,通常都是把進(jìn)程作為分配資源的基本單位,而把線程作為獨(dú)立運(yùn)行和獨(dú)立調(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)程或線程趣惠。上下文是指某一時間點(diǎn) CPU 寄存器和程序計(jì)數(shù)器的內(nèi)容。寄存器是 CPU 內(nèi)部的數(shù)量較少但是速度很快的內(nèi)存(與之對應(yīng)的是 CPU 外部相對較慢的 RAM 主內(nèi)存)身害。寄存器通過對常用值(通常是運(yùn)算的中間值)的快速訪問來提高計(jì)算機(jī)程序運(yùn)行的速度味悄。程序計(jì)數(shù)器是一個專用的寄存器,用于表明指令序列中 CPU 正在執(zhí)行的位置塌鸯,存的值為正在執(zhí)行的指令的位置或者下一個將要被執(zhí)行的指令的位置侍瑟,具體依賴于特定的系統(tǒng)。
二界赔、涉及的PHP基礎(chǔ)知識
五種常見運(yùn)行模式
- cgi 協(xié)議模式
- fast-cgi 協(xié)議模式丢习,一個常駐內(nèi)存型的cgi。
- 模塊模式淮悼,如php作為apache的模塊隨apache啟動而啟動
- php-cli模式咐低,屬于命令行模式
- 其他,如SwooleServer
fast-cgi
和cgi
都是一種協(xié)議,開啟的進(jìn)程是單獨(dú)實(shí)現(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 是運(yùn)行在CLI
模式下鲤屡, 所以可以常駐運(yùn)行和以守護(hù)進(jìn)程運(yùn)行, 但也正因?yàn)槿绱烁3蓿残枰_發(fā)者自行處理變量的銷毀及各種異常和超時的處理酒来。pcntl_fork多進(jìn)程使用
- pcntl是php官方的多進(jìn)程擴(kuò)展,只能在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è)軟件耳胎、云計(jì)算惯吕、網(wǎng)絡(luò)游戲、物聯(lián)網(wǎng)(IOT)怕午、車聯(lián)網(wǎng)废登、智能家居等領(lǐng)域。 使用 PHP + Swoole 作為網(wǎng)絡(luò)通信框架郁惜,可以使企業(yè) IT 研發(fā)團(tuán)隊(duì)的效率大大提升堡距,更加專注于開發(fā)創(chuàng)新產(chǎn)品。
Swoole高并發(fā)高性能保證:IO復(fù)用兆蕉、常駐內(nèi)存型羽戒、協(xié)程、異步虎韵、進(jìn)程池易稠、耗時任務(wù)處理、高性能數(shù)據(jù)結(jié)構(gòu)(hashTable)等包蓝。swoole服務(wù)運(yùn)行結(jié)構(gòu)圖
運(yùn)行流程圖
進(jìn)程/線程結(jié)構(gòu)圖1
進(jìn)程/線程結(jié)構(gòu)圖2Reactor驶社、Worker企量、TaskWorker的關(guān)系
三種角色分別的職責(zé)是:
Reactor線程
- 負(fù)責(zé)維護(hù)客戶端TCP連接、處理網(wǎng)絡(luò)IO衬吆、處理協(xié)議梁钾、收發(fā)數(shù)據(jù)
- 完全是異步非阻塞的模式
- 全部為C代碼,除Start/Shudown事件回調(diào)外逊抡,不執(zhí)行任何PHP代碼
- 將TCP客戶端發(fā)來的數(shù)據(jù)緩沖姆泻、拼接、拆分成完整的一個請求數(shù)據(jù)包
- Reactor以多線程的方式運(yùn)行
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)程的方式運(yù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)程的方式運(yù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等隊(duì)列中纲缓,并在后臺啟動一些php進(jìn)程異步地處理這些任務(wù)卷拘。Swoole提供的TaskWorker是一套更完整的方案,將任務(wù)的投遞祝高、隊(duì)列栗弟、php任務(wù)處理進(jìn)程管理合為一體。通過底層提供的API可以非常簡單地實(shí)現(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,消息隊(duì)列稍坯,毫秒定時器,異步文件讀寫搓劫,異步DNS查詢瞧哟。 Swoole內(nèi)置了Http/WebSocket服務(wù)器端/客戶端、Http2.0服務(wù)器端枪向∏诳基礎(chǔ)入門可參考快速起步
除了異步 IO 的支持之外,Swoole 為 PHP 多進(jìn)程的模式設(shè)計(jì)了多個并發(fā)數(shù)據(jù)結(jié)構(gòu)和IPC(進(jìn)程間通信)通信機(jī)制秘蛔,可以大大簡化多進(jìn)程并發(fā)編程的工作陨亡。其中包括了并發(fā)原子計(jì)數(shù)器,并發(fā) HashTable深员,Channel负蠕,Lock,進(jìn)程間通信IPC等豐富的功能特性倦畅。
Swoole2.0 支持了類似 Go 語言的協(xié)程遮糖,可以使用完全同步的代碼實(shí)現(xiàn)異步程序。PHP 代碼無需額外增加任何關(guān)鍵詞叠赐,底層自動進(jìn)行協(xié)程調(diào)度欲账,實(shí)現(xiàn)異步屡江。常見服務(wù)類型
Swoole\Server
創(chuàng)建一個異步服務(wù)器程序,支持TCP赛不、UDP惩嘉、UnixSocket 3種協(xié)議,支持IPv4和IPv6踢故,支持SSL/TLS單向雙向證書的隧道加密文黎。使用者無需關(guān)注底層實(shí)現(xiàn)細(xì)節(jié),僅需要設(shè)置網(wǎng)絡(luò)事件的回調(diào)函數(shù)即可畴椰。
請勿在使用Server創(chuàng)建之前調(diào)用其他異步IO的API臊诊,否則將會創(chuàng)建失敗⌒敝可以在Server啟動后onWorkerStart回調(diào)函數(shù)中使用抓艳。
Server只能用于php-cli環(huán)境,在其他環(huán)境下會拋出致命錯誤
Swoole\Http\Server
Http\Server繼承自Server帚戳,是一個的Http服務(wù)器實(shí)現(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消息隊(duì)列的進(jìn)程間通信鄙皇,只需調(diào)用write/read或者push/pop即可
- 支持重定向標(biāo)準(zhǔn)輸入和輸出芜赌,在子進(jìn)程內(nèi)echo不會打印屏幕,而是寫入管道伴逸,讀鍵盤輸入可以重定向?yàn)楣艿雷x取數(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)線程實(shí)現(xiàn),沒有額外的調(diào)度消耗禽篱,僅占用內(nèi)存畜伐。使用協(xié)程模式,可以理解為“每次事件回調(diào)函數(shù)都會創(chuàng)建一個新的線程去執(zhí)行躺率,事件回調(diào)函數(shù)執(zhí)行完成后玛界,線程退出”。
當(dāng)協(xié)程執(zhí)行完后悼吱,底層會恢復(fù)協(xié)程上下文慎框,代碼邏輯繼續(xù)從切換點(diǎn)開始恢復(fù)執(zhí)行。開發(fā)者整個過程不需要關(guān)心整個切換過程后添”靠荩可查看實(shí)現(xiàn)原理和Coroutine進(jìn)行調(diào)用并發(fā)HashTable:swoole_table一個基于共享內(nèi)存和鎖實(shí)現(xiàn)的超高性能,并發(fā)數(shù)據(jù)結(jié)構(gòu)遇西。用于解決多進(jìn)程/多線程數(shù)據(jù)共享和同步加鎖問題馅精。詳細(xì)可參考swoole_table
- 性能強(qiáng)悍,單線程每秒可讀寫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事件『馕矗可查看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)入的連接將被拒絕
守護(hù)進(jìn)程化:daemonize => 1缓醋,加入此參數(shù)后如失,執(zhí)行php server.php將轉(zhuǎn)入后臺作為守護(hù)進(jìn)程運(yù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é)束運(yùn)行歼培。manager會重新創(chuàng)建一個worker進(jìn)程震蒋。此選項(xiàng)用來防止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] received
swoole頁面的長連接比非長連接qps高出一半弯院,nginx非長連接壓測的qps比swoole非長連接壓測的qps要高
自定義進(jìn)程
Process教程 / 多進(jìn)程 / swoole_process實(shí)現(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實(shí)際為手動鏈接)
只要不終止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',//防止報(bào)錯,就不切換數(shù)據(jù)庫了 'port' => '3306', 'user' => 'root', 'timeout' => '5', 'charset' => 'utf8mb4', 'password' => '123456', 'database' => 'test',//防止報(bào)錯,就不切換數(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實(shí)際為手動鏈接)
過一段再show processlist發(fā)現(xiàn)已無鏈接,可能mysql主動超時中斷,有興趣的同學(xué)可主動排查
更多參考