摘要:原文地址在初探先從一個(gè)簡(jiǎn)單的服務(wù)器開(kāi)始中依次講解了三個(gè)逐漸進(jìn)步的服務(wù)器只能服務(wù)于一個(gè)客戶(hù)端的服務(wù)器利用可以服務(wù)于多個(gè)客戶(hù)端的額服務(wù)器利用預(yù)派生進(jìn)程服務(wù)于多個(gè)客戶(hù)端的服務(wù)器最后一種服務(wù)器的進(jìn)程模型基本上的大概原理其實(shí)跟我們常用的是非常
[原文地址:https://blog.ti-node.com/blog...]
在<PHP socket初探 --- 先從一個(gè)簡(jiǎn)單的socket服務(wù)器開(kāi)始>中依次講解了三個(gè)逐漸進(jìn)步的服務(wù)器:
只能服務(wù)于一個(gè)客戶(hù)端的服務(wù)器
利用fork可以服務(wù)于多個(gè)客戶(hù)端的額服務(wù)器
利用預(yù)fork派生進(jìn)程服務(wù)于多個(gè)客戶(hù)端的服務(wù)器
最后一種服務(wù)器的進(jìn)程模型基本上的大概原理其實(shí)跟我們常用的apache是非常相似的.
其實(shí)這種模型最大的問(wèn)題在于需要根據(jù)實(shí)際業(yè)務(wù)預(yù)估進(jìn)程數(shù)量,依舊是需要大量進(jìn)程來(lái)解決問(wèn)題,可能會(huì)出現(xiàn)CPU浪費(fèi)在進(jìn)程間切換上,還有可能會(huì)出現(xiàn)驚群現(xiàn)象(簡(jiǎn)單理解就是100個(gè)進(jìn)程在等帶客戶(hù)端連接,來(lái)了一個(gè)客戶(hù)端但是所有進(jìn)程都被喚醒了,但最終只有一個(gè)進(jìn)程為這個(gè)客戶(hù)端服務(wù),其余99個(gè)白白折騰),那么,有沒(méi)有一種解決方案可以使得少量進(jìn)程服務(wù)于多個(gè)客戶(hù)端呢?
答案就是在<PHP socket初探 --- 關(guān)于IO的一些枯燥理論>中提到的"IO多路復(fù)用".多路是指多個(gè)客戶(hù)端連接socket,復(fù)用就是指復(fù)用少數(shù)幾個(gè)進(jìn)程,多路復(fù)用本身依然隸屬于同步通信方式,只是表現(xiàn)出的結(jié)果看起來(lái)像異步,這點(diǎn)值得注意.目前多路復(fù)用有三種常用的方案,依次是:
select,最早的解決方案
poll,算是select的升級(jí)版
epoll,目前的最終解決版,解決c10k問(wèn)題的功臣
今天說(shuō)的是select,這個(gè)東西本身是個(gè)Linux系統(tǒng)調(diào)用.在Linux中一切皆為文件,socket也不例外,每當(dāng)Linux打開(kāi)一個(gè)文件系統(tǒng)都會(huì)返回一個(gè)對(duì)應(yīng)該文件的標(biāo)記叫做文件描述符.文件描述符是一個(gè)非負(fù)整數(shù),當(dāng)文件描述數(shù)達(dá)到最大的時(shí)候,會(huì)重新回到小數(shù)重新開(kāi)始(題外話:按照傳統(tǒng),一般情況下標(biāo)準(zhǔn)輸入是0,標(biāo)準(zhǔn)輸出是1,標(biāo)準(zhǔn)錯(cuò)誤是2).對(duì)文件的讀寫(xiě)操作就是利用對(duì)文件描述符的讀寫(xiě)操作.一個(gè)進(jìn)程可以操作的文件描述符的數(shù)量是有限制的,不同系統(tǒng)有不同的數(shù)量,在linux中,可以通過(guò)調(diào)整ulimit來(lái)調(diào)整控制.
先通過(guò)一個(gè)簡(jiǎn)單的例子說(shuō)明下select的作用和功能.雙11到了,你給少林足球隊(duì)買(mǎi)了很多很多球鞋,分別有10個(gè)快遞給你運(yùn)送,然后你就不斷地電話詢(xún)問(wèn)這10個(gè)快遞員,你覺(jué)得有點(diǎn)兒累.阿梅很心疼你,于是阿梅就說(shuō):"這事兒你不用管了,你去專(zhuān)心練大力金剛腿吧,等任何一個(gè)快遞到了,我告訴你".當(dāng)其中一個(gè)快遞來(lái)了后,阿梅就喊你:"下來(lái)啦,有快遞?。?,但是,這個(gè)阿梅比較缺心眼,她不告訴你是具體哪雙鞋子的快遞,只告訴你有快遞到了.所以,你只能依次查詢(xún)一遍所有快遞單的狀態(tài)才能確認(rèn)是哪個(gè)簽收了.
上面這個(gè)例子通過(guò)結(jié)合術(shù)語(yǔ)演繹一遍就是,你就是服務(wù)器軟件,阿梅就是select,10個(gè)快遞就是10個(gè)客戶(hù)端(也就是10個(gè)連接socket fd).阿梅負(fù)責(zé)替你管理著這10個(gè)連接socket fd,當(dāng)其中任何一個(gè)fd有反應(yīng)了也就是可以讀數(shù)據(jù)或可以發(fā)送數(shù)據(jù)了,阿梅(select)就會(huì)告訴你有可以讀寫(xiě)的fd了,但是阿梅(select)不會(huì)告訴你是哪個(gè)fd可讀寫(xiě),所以你必須輪循所有fd來(lái)看看是哪個(gè)fd,是可讀還是可寫(xiě).
是時(shí)候機(jī)械記憶一波兒了:
當(dāng)你啟動(dòng)select后,需要將三組不同的socket fd加入到作為select的參數(shù),傳統(tǒng)意義上這種fd的集合就叫做fd_set,三組fd_set依次是可讀集合,可寫(xiě)集合,異常集合.三組fd_set由系統(tǒng)內(nèi)核來(lái)維護(hù),每當(dāng)select監(jiān)控管理的三個(gè)fd_set中有可讀或者可寫(xiě)或者異常出現(xiàn)的時(shí)候,就會(huì)通知調(diào)用方.調(diào)用方調(diào)用select后,調(diào)用方就會(huì)被select阻塞,等待可讀可寫(xiě)等事件的發(fā)生.一旦有了可讀可寫(xiě)或者異常發(fā)生,需要將三個(gè)fd_set從內(nèi)核態(tài)全部copy到用戶(hù)態(tài)中,然后調(diào)用方通過(guò)輪詢(xún)的方式遍歷所有fd,從中取出可讀可寫(xiě)或者異常的fd并作出相應(yīng)操作.如果某次調(diào)用方?jīng)]有理會(huì)某個(gè)可操作的fd,那么下一次其余fd可操作時(shí),也會(huì)再次將上次調(diào)用方未處理的fd繼續(xù)返回給調(diào)用方,也就是說(shuō)去遍歷fd的時(shí)候,未理會(huì)的fd依然是可讀可寫(xiě)等狀態(tài),一直到調(diào)用方理會(huì).
上面都是我個(gè)人的理解和匯總,有錯(cuò)誤可以指出,希望不會(huì)誤人子弟.下面通過(guò)php代碼實(shí)例來(lái)操作一波兒select系統(tǒng)調(diào)用.在php中,你可以通過(guò)stream_select或者socket_select來(lái)操作select系統(tǒng)調(diào)用,下面演示socket_select進(jìn)行代碼演示:
0 ){ // 判斷l(xiāng)isten_socket有沒(méi)有發(fā)生變化,如果有就是有客戶(hù)端發(fā)生連接操作了 if( in_array( $listen_socket, $read ) ){ // 將客戶(hù)端socket加入到client數(shù)組中 $client_socket = socket_accept( $listen_socket ); $client[] = $client_socket; // 然后將listen_socket從read中去除掉 $key = array_search( $listen_socket, $read ); unset( $read[ $key ] ); } // 查看去除listen_socket中是否還有client_socket if( count( $read ) > 0 ){ $msg = "hello world"; foreach( $read as $socket_item ){ // 從可讀取的fd中讀取出來(lái)數(shù)據(jù)內(nèi)容,然后發(fā)送給其他客戶(hù)端 $content = socket_read( $socket_item, 2048 ); // 循環(huán)client數(shù)組,將內(nèi)容發(fā)送給其余所有客戶(hù)端 foreach( $client as $client_socket ){ // 因?yàn)閏lient數(shù)組中包含了 listen_socket 以及當(dāng)前發(fā)送者自己socket,所以需要排除二者 if( $client_socket != $listen_socket && $client_socket != $socket_item ){ socket_write( $client_socket, $content, strlen( $content ) ); } } } } } // 當(dāng)select沒(méi)有監(jiān)聽(tīng)到可操作fd的時(shí)候,直接continue進(jìn)入下一次循環(huán) else { continue; } }
將文件保存為server.php,然后執(zhí)行php server.php運(yùn)行服務(wù),同時(shí)再打開(kāi)三個(gè)終端,執(zhí)行telnet 127.0.0.1 9999,然后在任何一個(gè)telnet終端中輸入"I am DOG!",再看其他兩個(gè)telnet窗口,是不是感覺(jué)很屌?
不完全截圖圖下:
還沒(méi)意識(shí)到問(wèn)題嗎?如果我們看到有三個(gè)telnet客戶(hù)端連接服務(wù)器并且可以彼此之間發(fā)送消息,但是我們只用了一個(gè)進(jìn)程就可以服務(wù)三個(gè)客戶(hù)端,如果你愿意,可以開(kāi)更多的telnet,但是服務(wù)器只需要一個(gè)進(jìn)程就可以搞定,這就是IO多路復(fù)用diao的地方!
最后,我們重點(diǎn)解析一些socket_select函數(shù),我們看下這個(gè)函數(shù)的原型:
int socket_select ( array &$read , array &$write , array &$except , int $tv_sec [, int $tv_usec = 0 ] )
值得注意的是$read,$write,$except三個(gè)參數(shù)前面都有一個(gè)&,也就是說(shuō)這三個(gè)參數(shù)是引用類(lèi)型的,是可以被改寫(xiě)內(nèi)容的.在上面代碼案例中,服務(wù)器代碼第一次執(zhí)行的時(shí)候,我們要把需要監(jiān)聽(tīng)的所有fd全部放到了read數(shù)組中,然而在當(dāng)系統(tǒng)經(jīng)歷了select后,這個(gè)數(shù)組的內(nèi)容就會(huì)發(fā)生改變,由原來(lái)的全部read fds變成了只包含可讀的read fds,這也就是為什么聲明了一個(gè)client數(shù)組,然后又聲明了一個(gè)read數(shù)組,然后read = client.如果我們直接將client當(dāng)作socket_select的參數(shù),那么client數(shù)組內(nèi)容就被修改.假如有5個(gè)用戶(hù)保存在client數(shù)組中,只有1個(gè)可讀,在經(jīng)過(guò)socket_select后client中就只剩下那個(gè)可讀的fd了,其余4個(gè)客戶(hù)端將會(huì)丟失,此時(shí)客戶(hù)端的表現(xiàn)就是連接莫名其妙發(fā)生丟失了.
[原文地址:https://blog.ti-node.com/blog...]
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/29385.html
摘要:原文地址正如標(biāo)題所言,顫顫抖抖開(kāi)篇。于是只能是你自己,把單子上的個(gè)快遞逐次和收到的對(duì)比一遍,然后對(duì)比完畢后再把這個(gè)單子給了阿梅,然后阿梅繼續(xù)等。剃光頭前的阿梅,就是,不敢正眼看老板娘一眼。剃光頭后的阿梅,就是,可徒手接魔鬼隊(duì)的死亡之球。 [原文地址:https://blog.ti-node.com/blog...] 正如標(biāo)題所言,顫顫抖抖開(kāi)篇epoll。顫顫抖抖的原因大概也就是以前幾乎...
摘要:原文前面可以說(shuō)是弄了一系列的和多進(jìn)程的一大坨內(nèi)容,知識(shí)淺顯代碼粗暴風(fēng)格簡(jiǎn)陋,總的說(shuō)來(lái),還是差了一些細(xì)節(jié)。今天,就一些漏掉的細(xì)節(jié)補(bǔ)充一下。最后,我補(bǔ)充一句是同步的,而不是異步。 原文:https://t.ti-node.com/thread/... 前面可以說(shuō)是弄了一系列的php socket和多進(jìn)程的一大坨內(nèi)容,知識(shí)淺顯、代碼粗暴、風(fēng)格簡(jiǎn)陋,總的說(shuō)來(lái),還是差了一些細(xì)節(jié)。今天,就一些漏...
摘要:一閱前熱身為了更加形象的說(shuō)明同步異步阻塞非阻塞,我們以小明去買(mǎi)奶茶為例。等奶茶做好了,店員喊一聲小明,奶茶好了,然后小明去取奶茶。將響應(yīng)結(jié)果發(fā)給相應(yīng)的連接請(qǐng)求處理完成因?yàn)榛冢悦總€(gè)可以處理無(wú)數(shù)個(gè)連接請(qǐng)求。如此,就輕松的處理了高并發(fā)。 一、閱前熱身 為了更加形象的說(shuō)明同步異步、阻塞非阻塞,我們以小明去買(mǎi)奶茶為例。 1、同步與異步 ①同步與異步的理解 同步與異步的重點(diǎn)在消息通知的方式上...
摘要:一閱前熱身為了更加形象的說(shuō)明同步異步阻塞非阻塞,我們以小明去買(mǎi)奶茶為例。等奶茶做好了,店員喊一聲小明,奶茶好了,然后小明去取奶茶。將響應(yīng)結(jié)果發(fā)給相應(yīng)的連接請(qǐng)求處理完成因?yàn)榛冢悦總€(gè)可以處理無(wú)數(shù)個(gè)連接請(qǐng)求。如此,就輕松的處理了高并發(fā)。 一、閱前熱身 為了更加形象的說(shuō)明同步異步、阻塞非阻塞,我們以小明去買(mǎi)奶茶為例。 1、同步與異步 ①同步與異步的理解 同步與異步的重點(diǎn)在消息通知的方式上...
閱讀 3059·2021-11-25 09:43
閱讀 1035·2021-11-24 10:22
閱讀 1364·2021-09-22 15:26
閱讀 690·2019-08-30 15:44
閱讀 2469·2019-08-29 16:33
閱讀 3705·2019-08-26 18:42
閱讀 918·2019-08-23 18:07
閱讀 1840·2019-08-23 17:55