• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            isware

            驚群?jiǎn)栴}的思考

            “據(jù)說(shuō)”驚群?jiǎn)栴}已經(jīng)是一個(gè)很古老的問(wèn)題了,并且在大多數(shù)系統(tǒng)中已經(jīng)得到有效解決,但對(duì)我來(lái)說(shuō),仍舊是一個(gè)比較新的概念,因此有必要記錄一下。

            什么是驚群

                    舉一個(gè)很簡(jiǎn)單的例子,當(dāng)你往一群鴿子中間扔一塊食物,雖然最終只有一個(gè)鴿子搶到食物,但所有鴿子都會(huì)被驚動(dòng)來(lái)爭(zhēng)奪,沒(méi)有搶到食物的鴿子只好回去繼續(xù)睡覺(jué),等待下一塊食物到來(lái)。這樣,每扔一塊食物,都會(huì)驚動(dòng)所有的鴿子,即為驚群。對(duì)于操作系統(tǒng)來(lái)說(shuō),多個(gè)進(jìn)程/線程在等待同一資源是,也會(huì)產(chǎn)生類似的效果,其結(jié)果就是每當(dāng)資源可用,所有的進(jìn)程/線程都來(lái)競(jìng)爭(zhēng)資源,造成的后果:
            1)系統(tǒng)對(duì)用戶進(jìn)程/線程頻繁的做無(wú)效的調(diào)度、上下文切換,系統(tǒng)系能大打折扣。
            2)為了確保只有一個(gè)線程得到資源,用戶必須對(duì)資源操作進(jìn)行加鎖保護(hù),進(jìn)一步加大了系統(tǒng)開(kāi)銷。

                    最常見(jiàn)的例子就是對(duì)于socket描述符的accept操作,當(dāng)多個(gè)用戶進(jìn)程/線程監(jiān)聽(tīng)在同一個(gè)端口上時(shí),由于實(shí)際只可能accept一次,因此就會(huì)產(chǎn)生驚群現(xiàn)象,當(dāng)然前面已經(jīng)說(shuō)過(guò)了,這個(gè)問(wèn)題是一個(gè)古老的問(wèn)題,新的操作系統(tǒng)內(nèi)核已經(jīng)解決了這一問(wèn)題。

            linux內(nèi)核解決驚群?jiǎn)栴}的方法

                    對(duì)于一些已知的驚群?jiǎn)栴},內(nèi)核開(kāi)發(fā)者增加了一個(gè)“互斥等待”選項(xiàng)。一個(gè)互斥等待的行為與睡眠基本類似,主要的不同點(diǎn)在于:
                    1)當(dāng)一個(gè)等待隊(duì)列入口有 WQ_FLAG_EXCLUSEVE 標(biāo)志置位, 它被添加到等待隊(duì)列的尾部. 沒(méi)有這個(gè)標(biāo)志的入口項(xiàng), 相反, 添加到開(kāi)始.
                    2)當(dāng) wake_up 被在一個(gè)等待隊(duì)列上調(diào)用時(shí), 它在喚醒第一個(gè)有 WQ_FLAG_EXCLUSIVE 標(biāo)志的進(jìn)程后停止。
                    也就是說(shuō),對(duì)于互斥等待的行為,比如如對(duì)一個(gè)listen后的socket描述符,多線程阻塞accept時(shí),系統(tǒng)內(nèi)核只會(huì)喚醒所有正在等待此時(shí)間的隊(duì)列的第一個(gè),隊(duì)列中的其他人則繼續(xù)等待下一次事件的發(fā)生,這樣就避免的多個(gè)線程同時(shí)監(jiān)聽(tīng)同一個(gè)socket描述符時(shí)的驚群?jiǎn)栴}。

            根據(jù)以上背景信息,我們來(lái)比較一下常見(jiàn)的Server端設(shè)計(jì)方案。
            方案1:listen后,啟動(dòng)多個(gè)線程(進(jìn)程),對(duì)此socket進(jìn)行監(jiān)聽(tīng)(僅阻塞accept方式不驚群)。
            方案2:主線程負(fù)責(zé)監(jiān)聽(tīng),通過(guò)線程池方式處理連接。(通常的方法)
            方案3:主線程負(fù)責(zé)監(jiān)聽(tīng),客戶端連接上來(lái)后由主線程分配實(shí)際的端口,客戶端根據(jù)此端口重新連接,然后處理數(shù)據(jù)。

            先考慮客戶端單連接的情況
            方案1:每當(dāng)有新的連接到來(lái)時(shí),系統(tǒng)內(nèi)核會(huì)從隊(duì)列中以FIFO的方式選擇一個(gè)監(jiān)聽(tīng)線程來(lái)服務(wù)此連接,因此可以充分發(fā)揮系統(tǒng)的系能并且多線程負(fù)載均衡。對(duì)于單連接的場(chǎng)景,這種方案無(wú)疑是非常優(yōu)越的。遺憾的是,對(duì)于select、epoll,內(nèi)核目前無(wú)法解決驚群?jiǎn)栴}。(nginx對(duì)于驚群?jiǎn)栴}的解決方法)
            方案2:由于只有一個(gè)線程在監(jiān)聽(tīng),其瞬時(shí)的并發(fā)處理連接請(qǐng)求的能力必然不如多線程。同時(shí),需要對(duì)線程池做調(diào)度管理,必然涉及資源共享訪問(wèn),相對(duì)于方案一來(lái)說(shuō)管理成本要增加不少,代碼復(fù)雜度提高,性能也有所下降。
            方案3:與方案2有不少類似的地方,其優(yōu)勢(shì)是不需要做線程調(diào)度。缺點(diǎn)是增加了主線程的負(fù)擔(dān),除了接收連接外還需要發(fā)送數(shù)據(jù),而且需要兩次連接,孰優(yōu)孰劣,有待測(cè)試。

            再考慮客戶端多連接的情況:
            對(duì)于數(shù)據(jù)傳輸類的應(yīng)用,為了充分利用帶寬,往往會(huì)開(kāi)啟多個(gè)連接來(lái)傳輸數(shù)據(jù),連接之間的數(shù)據(jù)有相互依賴性,因此Server端要想很好的維護(hù)這種依賴性,把同一個(gè)客戶端的所有連接放在一個(gè)線程中處理是非常有必要的。
            A、同一客戶端在一個(gè)線程中處理
            方案1:如果沒(méi)有更底層的解決方案的話,Server則需要維護(hù)一個(gè)全局列表,來(lái)記錄當(dāng)前連接請(qǐng)求該由哪個(gè)線程處理。多線程需要同時(shí)競(jìng)爭(zhēng)一個(gè)全局資源,似乎有些不妙。
            方案2:主線程負(fù)責(zé)監(jiān)聽(tīng)并分發(fā),因此與單連接相比沒(méi)有帶來(lái)額外的性能開(kāi)銷。僅僅會(huì)造成主線程忙于更多的連接請(qǐng)求。
            方案3:較單線程來(lái)說(shuō),主線程工作量沒(méi)有任何增加,由于多連接而造成的額外開(kāi)銷由實(shí)際工作線程分擔(dān),因此對(duì)于這種場(chǎng)景,方案3似乎是最佳選擇。

            B、同一客戶端在不同線程中處理
            方案1:同樣需要競(jìng)爭(zhēng)資源。
            方案2:沒(méi)理由。
            方案3:不可能。

            另外:
            (《UNIX網(wǎng)絡(luò)編程》第三版是在第30章)
            讀《UNIX網(wǎng)絡(luò)編程》第二版的第一卷時(shí),發(fā)現(xiàn)作者在第27章“客戶-服務(wù)器程序其它設(shè)計(jì)方法”中的27.6節(jié)“TCP預(yù)先派生子進(jìn)程服務(wù)器程序,accept無(wú)上鎖保護(hù)”中提到了一種由子進(jìn)程去競(jìng)爭(zhēng)客戶端連接的設(shè)計(jì)方法,用偽碼描述如下:

            服務(wù)器主進(jìn)程:

            listen_fd = socket(...);
            bind(listen_fd, ...);
            listen(listen_fd, ...);
            pre_fork_children(...);
            close(listen_fd);
            wait_children_die(...);


            服務(wù)器服務(wù)子進(jìn)程:

            while (1) {
            conn_fd = accept(listen_fd, ...);
            do_service(conn_fd, ...);
            }


            初 識(shí)上述代碼,真有眼前一亮的感覺(jué),也正如作者所說(shuō),以上代碼確實(shí)很少見(jiàn)(反正我讀此書(shū)之前是確實(shí)沒(méi)見(jiàn)過(guò))。作者真是構(gòu)思精巧,巧妙地繞過(guò)了常見(jiàn)的預(yù)先創(chuàng)建 子進(jìn)程的多進(jìn)程服務(wù)器當(dāng)主服務(wù)進(jìn)程接收到新的連接必須想辦法將這個(gè)連接傳遞給服務(wù)子進(jìn)程的“陷阱”,上述代碼通過(guò)共享的傾聽(tīng)套接字,由子進(jìn)程主動(dòng)地去向內(nèi) 核“索要”連接套接字,從而避免了用UNIX域套接字傳遞文件描述符的“淫技”。

            不過(guò),當(dāng)接著往下讀的時(shí)候,作者談到了“驚群” (Thundering herd)問(wèn)題。所謂的“驚群”就是,當(dāng)很多進(jìn)程都阻塞在accept系統(tǒng)調(diào)用的時(shí)候,即使只有一個(gè)新的連接達(dá)到,內(nèi)核也會(huì)喚醒所有阻塞在accept上 的進(jìn)程,這將給系統(tǒng)帶來(lái)非常大的“震顫”,降低系統(tǒng)性能。

            除了這個(gè)問(wèn)題,accept還必須是原子操作。為此,作者在接下來(lái)的27.7節(jié)講述了加了互斥鎖的版本:

            while (1) {
            lock(...);
            conn_fd = accept(listen_fd, ...);
            unlock(...);
            do_service(conn_fd, ...);
            }


            原 子操作的問(wèn)題算是解決了,那么“驚群”呢?文中只是提到在Solaris系統(tǒng)上當(dāng)子進(jìn)程數(shù)由75變成90后,CPU時(shí)間顯著增加,并且作者認(rèn)為這是因?yàn)檫M(jìn) 程過(guò)多,導(dǎo)致內(nèi)存互換。對(duì)“驚群”問(wèn)題回答地十分含糊。通過(guò)比較書(shū)中圖27.2的第4列和第7列的內(nèi)容,我們可以肯定“真兇”絕對(duì)不是“內(nèi)存對(duì)換”。

            “元兇”到底是誰(shuí)?

            仔 細(xì)分析一下,加鎖真的有助于“驚群”問(wèn)題么?不錯(cuò),確實(shí)在同一時(shí)間只有一個(gè)子進(jìn)程在調(diào)用accept,其它子進(jìn)程都阻塞在了lock語(yǔ)句,但是,當(dāng) accept返回并unlock之后呢?unlock肯定是要喚醒阻塞在這個(gè)鎖上的進(jìn)程的,不過(guò)誰(shuí)都沒(méi)有規(guī)定是喚醒一個(gè)還是喚醒多個(gè)。所以,潛在的“驚 群”問(wèn)題還是存在,只不過(guò)換了個(gè)地方,換了個(gè)形式。而造成Solaris性能驟降的“罪魁禍?zhǔn)?#8221;很有可能就是“驚群”問(wèn)題。

            崩潰了!這么說(shuō)所有的鎖都有可能產(chǎn)生驚群?jiǎn)栴}了?

            似乎真的是這樣,所以減少鎖的使用很重要。特別是在競(jìng)爭(zhēng)比較激烈的地方。

            作者在27.9節(jié)所實(shí)現(xiàn)的“傳遞文件描述符”版本的服務(wù)器就有效地克服了“驚群”問(wèn)題,在現(xiàn)實(shí)的服務(wù)器實(shí)現(xiàn)中,最常用的也是此節(jié)所提到的基于“分配”形式。

            把“競(jìng)爭(zhēng)”換成“分配”是避免“驚群”問(wèn)題的有效方法,但是也不要忽視“分配”的“均衡”問(wèn)題,不然后果可能更加嚴(yán)重哦!

            posted on 2011-07-20 12:44 艾斯維亞 閱讀(6441) 評(píng)論(2)  編輯 收藏 引用

            Feedback

            # re: 驚群?jiǎn)栴}的思考 2013-01-22 22:20 longzhiri

            多個(gè)進(jìn)程都accept同一個(gè)socket,這和你之前說(shuō)的多線程accept同一個(gè)socket一樣嗎?怎么被你說(shuō)的新奇了。  回復(fù)  更多評(píng)論   

            # re: 驚群?jiǎn)栴}的思考 2013-09-09 20:13 sunf

            我想問(wèn)一下,UNP中提到的方法,和你說(shuō)的第一種方案好像沒(méi)差別?  回復(fù)  更多評(píng)論   


            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            久久精品国产亚洲av日韩| 综合久久一区二区三区 | 久久久九九有精品国产| 色综合久久88色综合天天 | 久久久久高潮毛片免费全部播放| 久久久久久国产精品免费无码| 亚洲成人精品久久| 99久久精品毛片免费播放| 无码专区久久综合久中文字幕| 伊人久久无码精品中文字幕| 人妻无码αv中文字幕久久琪琪布| 久久亚洲国产精品成人AV秋霞| 国产农村妇女毛片精品久久| 久久精品这里热有精品| 亚洲精品国精品久久99热| 国产一级做a爰片久久毛片| 精品久久久久久无码中文字幕一区| 亚洲伊人久久大香线蕉综合图片| 色欲综合久久躁天天躁| 国产精品久久成人影院| 欧美精品国产综合久久| 久久久久99精品成人片牛牛影视| 狠狠精品干练久久久无码中文字幕 | 亚洲AV无码久久精品蜜桃| 国産精品久久久久久久| 久久久精品一区二区三区| 精品久久久无码人妻中文字幕| 久久精品国产乱子伦| 久久av高潮av无码av喷吹| 国产成人久久精品区一区二区| 久久精品国产精品亚洲精品 | aaa级精品久久久国产片| 狠狠色婷婷久久综合频道日韩| 久久人人爽人人精品视频| 久久综合九色综合网站| 久久丝袜精品中文字幕| 久久久综合香蕉尹人综合网| A级毛片无码久久精品免费| 色综合久久天天综合| 爱做久久久久久| 精品国产青草久久久久福利 |