最近去南京一家公司面試時被問到的問題:如何設(shè)計一個高并發(fā)的http服務(wù)器?
從接觸這個問題開始,到現(xiàn)在為止,我個人的腦海里有若干種方案,很遺憾的是在面試的時候我只說出其中兩種。
但是我并不能確認(rèn)我的這些方案是否正確與可行,是否可以高并發(fā)或者高效率。放在這里,希望大家多提意見。
為了使描述能夠更加清晰,我假設(shè)了一個高速公路收費站的模型:
1:一個用戶的請求就比作一輛汽車。
2:服務(wù)端的響應(yīng)行為就比作收費站放閘、收錢、開閘等一系列動作(同步)。(最終目的是收錢)
方案一:
1:一個請求到來,開一個線程處理請求,處理完成,線程退出。
這里一個線程可以看做一個收費窗口,就好比收費站上來一輛汽車我開一個收費窗口,放行之后就關(guān)閉這個窗
口。如果所有窗口都在工作,那剩下的汽車就得處于等待狀態(tài)了。顯然,這樣工作的話,收費站的員工要累壞
了,一會兒開門進來一會兒關(guān)門出去,手酸。收費站的員工意見都很大。服務(wù)器中也一樣,一個服務(wù)端有一個
線程數(shù)的上限,不可能無限制創(chuàng)建線程。而且,雖然線程的創(chuàng)建和銷毀開銷不如進程來得大,但是頻繁創(chuàng)建和
銷毀的代價卻可能很巨大。
方案二:
1:開一個線程池。
2:一個請求來了之后,尋找一個“空閑”線程將其“喚醒”,處理請求之后,再讓其“休眠”。
這里的“喚醒”和“休眠”就好比開窗和關(guān)窗的動作,這比頻繁地開關(guān)門好多了。這對服務(wù)器來說,休眠狀態(tài)下
的線程不占用cpu時間片,這肯定是有利于其他線程的及時調(diào)度的。ok,現(xiàn)在收費員意見不大了,收費站外面排長
隊的汽車司機們的意見卻依舊:這么長一條高速路,俺們每次路過交那么多錢,結(jié)果個收費站才這么幾個窗口,害
得俺們每次都要排長隊等待。雖然現(xiàn)在這些收費的家伙時刻坐在里面,不用走出來走出去了,可是放閘、交錢、開閘
等動作太繁瑣和費時間了。
方案三:
1:開一個線程池。
2:一個請求來了,尋找一個“空閑”線程將其“喚醒”,處理請求之后,再讓其“休眠”。同時處理請求的過程采
用異步io的方式,大大縮短處理時間,可以讓線程及時“空閑”出來。
前面講過的方案中的處理請求的過程都是以同步io的方式來實現(xiàn)的。也就是收費站員工要放閘、收錢、開閘等一系列
動作。他必須要等到汽車司機把錢交了才能進入空閑狀態(tài),可以處理下一個汽車的收費問題。這樣問題的關(guān)鍵就在于
收費員和司機的動作是否都足夠快(服務(wù)端和客戶端的溝通效率),即使兩者都很快,大部分時間還是被這一個過程
占用。其實收費站的主要目的就是收錢,而司機們就想快點達(dá)到終點。異步io的使用就類似于快速反應(yīng)的實現(xiàn),一個
收費窗口可以讓一輛車迅速通過,并且在后面安排其駛進一個帶有專業(yè)快速收費系統(tǒng)的地下車道,使其能夠照樣交錢
通過,并且使得后面等待的車可以繼續(xù)快速跟進。然后新的問題總會出現(xiàn),收費站的窗口有限,而汽車在“春運”的
時候總會很多很多,即便使用了地下專業(yè)快速收費系統(tǒng),收費窗口前的汽車依舊排起長龍。司機兄弟們脾氣都很火爆,
等久了,就會下車打架啦。
方案四:
1:開一個線程池。
2:將一個請求的處理過程細(xì)分成若干個細(xì)小的狀態(tài)。
3:一個請求到來之后迅速找到一個“空閑”線程,如果沒有,則找一個“工作”線程,使其當(dāng)前正在處理的請求在
當(dāng)前狀態(tài)下掛起,迅速響應(yīng)新進來的請求,稍后再將最早掛起的狀態(tài)的請求恢復(fù),如此循環(huán),使得客戶端感覺仿佛沒有
等待的感覺。
收費站的領(lǐng)導(dǎo)目前正在為這個方案的實施大傷腦筋,這似乎是一個真正考驗他們管理能力的難題了,但是上峰的命令就
是汽車來了不能讓人家等,趕緊為司機“服務(wù)”。
說到這里,仿佛這四個方案都有其優(yōu)缺點。同時我也想起以前聽到同事們將erlang,說其在分布式開發(fā)上很有優(yōu)勢,但是
他們同時又強調(diào)高并發(fā),不一定代表高性能。以前總不明白為什么既然要高并發(fā)了,卻沒有高性能呢?現(xiàn)在總算明白,
實際的需求總是在變化的,我們總要根據(jù)需求和實際情況做出最恰當(dāng)?shù)脑O(shè)計,必要的時候需要做出一些折中,我想這才是
一個優(yōu)秀的程序員所必須具備的素質(zhì)。