HTTP 的特點(diǎn)
所以接下來(lái)先是聊聊 HTTP 協(xié)議的特點(diǎn)趴俘、優(yōu)點(diǎn)和缺點(diǎn)。既要看到它好的一面胰蝠,也要正視它不好的一面,只有全方位、多角度了解 HTTP玲献,才能實(shí)現(xiàn)“揚(yáng)長(zhǎng)避短”廓啊,更好地利用 HTTP。
靈活可擴(kuò)展
首先乾胶, HTTP 協(xié)議是一個(gè)“靈活可擴(kuò)展”的傳輸協(xié)議抖剿。
HTTP 協(xié)議最初誕生的時(shí)候就比較簡(jiǎn)單,本著開(kāi)放的精神只規(guī)定了報(bào)文的基本格式识窿,比如用空格分隔單詞斩郎,用換行分隔字段,“header+body”等喻频,報(bào)文里的各個(gè)組成部分都沒(méi)有做嚴(yán)格的語(yǔ)法語(yǔ)義限制缩宜,可以由開(kāi)發(fā)者任意定制。
所以,HTTP 協(xié)議就隨著互聯(lián)網(wǎng)的發(fā)展一同成長(zhǎng)起來(lái)了锻煌。在這個(gè)過(guò)程中妓布,HTTP 協(xié)議逐漸增加了請(qǐng)求方法、版本號(hào)宋梧、狀態(tài)碼匣沼、頭字段等特性。而 body 也不再限于文本形式的 TXT 或 HTML乃秀,而是能夠傳輸圖片肛著、音頻視頻等任意數(shù)據(jù),這些都是源于它的“靈活可擴(kuò)展”的特點(diǎn)跺讯。
而那些 RFC 文檔枢贿,實(shí)際上也可以理解為是對(duì)已有擴(kuò)展的“承認(rèn)和標(biāo)準(zhǔn)化”,實(shí)現(xiàn)了“從實(shí)踐中來(lái)刀脏,到實(shí)踐中去”的良性循環(huán)局荚。
也正是因?yàn)檫@個(gè)特點(diǎn),HTTP 才能在三十年的歷史長(zhǎng)河中“屹立不倒”愈污,從最初的低速實(shí)驗(yàn)網(wǎng)絡(luò)發(fā)展到現(xiàn)在的遍布全球的高速互聯(lián)網(wǎng)耀态,始終保持著旺盛的生命力。
可靠傳輸
第二個(gè)特點(diǎn)暂雹, HTTP 協(xié)議是一個(gè)“可靠”的傳輸協(xié)議首装。
這個(gè)特點(diǎn)顯而易見(jiàn),因?yàn)?HTTP 協(xié)議是基于 TCP/IP 的杭跪,而 TCP 本身是一個(gè)“可靠”的傳輸協(xié)議仙逻,所以 HTTP 自然也就繼承了這個(gè)特性,能夠在請(qǐng)求方和應(yīng)答方之間“可靠”地傳輸數(shù)據(jù)涧尿。
它的具體做法與 TCP/UDP 差不多系奉,都是對(duì)實(shí)際傳輸?shù)臄?shù)據(jù)(entity)做了一層包裝,加上一個(gè)頭姑廉,然后調(diào)用 Socket API缺亮,通過(guò) TCP/IP 協(xié)議棧發(fā)送或者接收。
不過(guò)我們必須正確地理解“可靠”的含義桥言,HTTP 并不能 100% 保證數(shù)據(jù)一定能夠發(fā)送到另一端萌踱,在網(wǎng)絡(luò)繁忙、連接質(zhì)量差等惡劣的環(huán)境下号阿,也有可能收發(fā)失敗虫蝶。“可靠”只是向使用者提供了一個(gè)“承諾”倦西,會(huì)在下層用多種手段“盡量”保證數(shù)據(jù)的完整送達(dá)能真。
當(dāng)然,如果遇到光纖被意外挖斷這樣的極端情況,即使是神仙也不能發(fā)送成功粉铐。所以疼约,“可靠”傳輸是指在網(wǎng)絡(luò)基本正常的情況下數(shù)據(jù)收發(fā)必定成功,借用運(yùn)維里的術(shù)語(yǔ)蝙泼,大概就是“3 個(gè) 9”或者“4 個(gè) 9”的程度吧程剥。
應(yīng)用層協(xié)議
第三個(gè)特點(diǎn),HTTP 協(xié)議是一個(gè)應(yīng)用層的協(xié)議汤踏。
這個(gè)特點(diǎn)也是不言自明的织鲸,但卻很重要。
在 TCP/IP 誕生后的幾十年里溪胶,雖然出現(xiàn)了許多的應(yīng)用層協(xié)議搂擦,但它們都僅關(guān)注很小的應(yīng)用領(lǐng)域,局限在很少的應(yīng)用場(chǎng)景哗脖。例如 FTP 只能傳輸文件瀑踢、SMTP 只能發(fā)送郵件、SSH 只能遠(yuǎn)程登錄等才避,在通用的數(shù)據(jù)傳輸方面“完全不能打”橱夭。
所以 HTTP 憑借著可攜帶任意頭字段和實(shí)體數(shù)據(jù)的報(bào)文結(jié)構(gòu),以及連接控制桑逝、緩存代理等方便易用的特性棘劣,一出現(xiàn)就“技?jí)喝盒邸保杆俪蔀榱藨?yīng)用層里的“明星”協(xié)議楞遏。只要不太苛求性能茬暇,HTTP 幾乎可以傳遞一切東西,滿足各種需求橱健,稱得上是一個(gè)“萬(wàn)能”的協(xié)議。
套用一個(gè)網(wǎng)上流行的段子沙廉,HTTP 完全可以用開(kāi)玩笑的口吻說(shuō):“不要誤會(huì)拘荡,我不是針對(duì) FTP,我是說(shuō)在座的應(yīng)用層各位撬陵,都是垃圾珊皿。”
請(qǐng)求 - 應(yīng)答
第四個(gè)特點(diǎn)巨税,HTTP 協(xié)議使用的是請(qǐng)求 - 應(yīng)答通信模式蟋定。
這個(gè)請(qǐng)求 - 應(yīng)答模式是 HTTP 協(xié)議最根本的通信模型,通俗來(lái)講就是“一發(fā)一收”“有來(lái)有去”草添,就像是寫(xiě)代碼時(shí)的函數(shù)調(diào)用驶兜,只要填好請(qǐng)求頭里的字段,“調(diào)用”后就會(huì)收到答復(fù)。
請(qǐng)求 - 應(yīng)答模式也明確了 HTTP 協(xié)議里通信雙方的定位抄淑,永遠(yuǎn)是請(qǐng)求方先發(fā)起連接和請(qǐng)求屠凶,是主動(dòng)的,而應(yīng)答方只有在收到請(qǐng)求后才能答復(fù)肆资,是被動(dòng)的矗愧,如果沒(méi)有請(qǐng)求時(shí)不會(huì)有任何動(dòng)作。
當(dāng)然郑原,請(qǐng)求方和應(yīng)答方的角色也不是絕對(duì)的唉韭,在瀏覽器 - 服務(wù)器的場(chǎng)景里,通常服務(wù)器都是應(yīng)答方犯犁,但如果將它用作代理連接后端服務(wù)器属愤,那么它就可能同時(shí)扮演請(qǐng)求方和應(yīng)答方的角色。
HTTP 的請(qǐng)求 - 應(yīng)答模式也恰好契合了傳統(tǒng)的 C/S(Client/Server)系統(tǒng)架構(gòu)栖秕,請(qǐng)求方作為客戶端春塌、應(yīng)答方作為服務(wù)器。所以簇捍,隨著互聯(lián)網(wǎng)的發(fā)展就出現(xiàn)了 B/S(Browser/Server)架構(gòu)只壳,用輕量級(jí)的瀏覽器代替笨重的客戶端應(yīng)用,實(shí)現(xiàn)零維護(hù)的“瘦”客戶端暑塑,而服務(wù)器則擯棄私有通信協(xié)議轉(zhuǎn)而使用 HTTP 協(xié)議吼句。
此外,請(qǐng)求 - 應(yīng)答模式也完全符合 RPC(Remote Procedure Call)的工作模式事格,可以把 HTTP 請(qǐng)求處理封裝成遠(yuǎn)程函數(shù)調(diào)用惕艳,導(dǎo)致了 WebService、RESTful 和 gPRC 等的出現(xiàn)驹愚。
無(wú)狀態(tài)
第五個(gè)特點(diǎn)远搪,HTTP 協(xié)議是無(wú)狀態(tài)的。
這個(gè)所謂的“狀態(tài)”應(yīng)該怎么理解呢逢捺?
“狀態(tài)”其實(shí)就是客戶端或者服務(wù)器里保存的一些數(shù)據(jù)或者標(biāo)志谁鳍,記錄了通信過(guò)程中的一些變化信息。
你一定知道劫瞳,TCP 協(xié)議是有狀態(tài)的倘潜,一開(kāi)始處于 CLOSED 狀態(tài),連接成功后是 ESTABLISHED 狀態(tài)志于,斷開(kāi)連接后是 FIN-WAIT 狀態(tài)涮因,最后又是 CLOSED 狀態(tài)。
這些“狀態(tài)”就需要 TCP 在內(nèi)部用一些數(shù)據(jù)結(jié)構(gòu)去維護(hù)伺绽,可以簡(jiǎn)單地想象成是個(gè)標(biāo)志量养泡,標(biāo)記當(dāng)前所處的狀態(tài)嗜湃,例如 0 是 CLOSED,2 是 ESTABLISHED 等等瓤荔。
再來(lái)看 HTTP净蚤,那么對(duì)比一下 TCP 就看出來(lái)了,在整個(gè)協(xié)議里沒(méi)有規(guī)定任何的“狀態(tài)”输硝,客戶端和服務(wù)器永遠(yuǎn)是處在一種“無(wú)知”的狀態(tài)今瀑。建立連接前兩者互不知情,每次收發(fā)的報(bào)文也都是互相獨(dú)立的点把,沒(méi)有任何的聯(lián)系橘荠。收發(fā)報(bào)文也不會(huì)對(duì)客戶端或服務(wù)器產(chǎn)生任何影響,連接后也不會(huì)要求保存任何信息郎逃。
“無(wú)狀態(tài)”形象地來(lái)說(shuō)就是“沒(méi)有記憶能力”哥童。比如,瀏覽器發(fā)了一個(gè)請(qǐng)求褒翰,說(shuō)“我是小明贮懈,請(qǐng)給我 A 文件∮叛担”朵你,服務(wù)器收到報(bào)文后就會(huì)檢查一下權(quán)限,看小明確實(shí)可以訪問(wèn) A 文件揣非,于是把文件發(fā)回給瀏覽器抡医。接著瀏覽器還想要 B 文件,但服務(wù)器不會(huì)記錄剛才的請(qǐng)求狀態(tài)早敬,不知道第二個(gè)請(qǐng)求和第一個(gè)請(qǐng)求是同一個(gè)瀏覽器發(fā)來(lái)的忌傻,所以瀏覽器必須還得重復(fù)一次自己的身份才行:“我是剛才的小明,請(qǐng)?jiān)俳o我 B 文件搞监∷ⅲ”
我們可以再對(duì)比一下 UDP 協(xié)議,不過(guò)它是無(wú)連接也無(wú)狀態(tài)的琐驴,順序發(fā)包亂序收包俘种,數(shù)據(jù)包發(fā)出去后就不管了,收到后也不會(huì)順序整理棍矛。而 HTTP 是有連接無(wú)狀態(tài)安疗,順序發(fā)包順序收包抛杨,按照收發(fā)的順序管理報(bào)文够委。
但不要忘了 HTTP 是“靈活可擴(kuò)展”的,雖然標(biāo)準(zhǔn)里沒(méi)有規(guī)定“狀態(tài)”怖现,但完全能夠在協(xié)議的框架里給它“打個(gè)補(bǔ)丁”茁帽,增加這個(gè)特性玉罐。
其他特點(diǎn)
除了以上的五大特點(diǎn),其實(shí) HTTP 協(xié)議還可以列出非常多的特點(diǎn)潘拨,例如傳輸?shù)膶?shí)體數(shù)據(jù)可緩存可壓縮吊输、可分段獲取數(shù)據(jù)、支持身份認(rèn)證铁追、支持國(guó)際化語(yǔ)言等季蚂。但這些并不能算是 HTTP 的基本特點(diǎn),因?yàn)檫@都是由第一個(gè)“靈活可擴(kuò)展”的特點(diǎn)所衍生出來(lái)的琅束。
小結(jié)
HTTP 是靈活可擴(kuò)展的扭屁,可以任意添加頭字段實(shí)現(xiàn)任意功能;
HTTP 是可靠傳輸協(xié)議涩禀,基于 TCP/IP 協(xié)議“盡量”保證數(shù)據(jù)的送達(dá)料滥;
HTTP 是應(yīng)用層協(xié)議,比 FTP艾船、SSH 等更通用功能更多葵腹,能夠傳輸任意數(shù)據(jù);
HTTP 使用了請(qǐng)求 - 應(yīng)答模式屿岂,客戶端主動(dòng)發(fā)起請(qǐng)求践宴,服務(wù)器被動(dòng)回復(fù)請(qǐng)求;
HTTP 本質(zhì)上是無(wú)狀態(tài)的雁社,每個(gè)請(qǐng)求都是互相獨(dú)立浴井、毫無(wú)關(guān)聯(lián)的,協(xié)議不要求客戶端或服務(wù)器記錄請(qǐng)求相關(guān)的信息霉撵。
HTTP的連接管理
HTTP 的連接管理也算得上是個(gè)“老生常談”的話題了磺浙,你一定曾經(jīng)聽(tīng)說(shuō)過(guò)“短連接”“長(zhǎng)連接”之類的名詞,今天讓我們一起來(lái)把它們弄清楚徒坡。
短連接
HTTP 協(xié)議最初(0.9/1.0)是個(gè)非常簡(jiǎn)單的協(xié)議撕氧,通信過(guò)程也采用了簡(jiǎn)單的“請(qǐng)求 - 應(yīng)答”方式。
它底層的數(shù)據(jù)傳輸基于 TCP/IP喇完,每次發(fā)送請(qǐng)求前需要先與服務(wù)器建立連接伦泥,收到響應(yīng)報(bào)文后會(huì)立即關(guān)閉連接。
因?yàn)榭蛻舳伺c服務(wù)器的整個(gè)連接過(guò)程很短暫锦溪,不會(huì)與服務(wù)器保持長(zhǎng)時(shí)間的連接狀態(tài)不脯,所以就被稱為“短連接”(short-lived connections)。早期的 HTTP 協(xié)議也被稱為是“無(wú)連接”的協(xié)議刻诊。
短連接的缺點(diǎn)相當(dāng)嚴(yán)重防楷,因?yàn)樵?TCP 協(xié)議里,建立連接和關(guān)閉連接都是非吃蜓模“昂貴”的操作复局。TCP 建立連接要有“三次握手”冲簿,發(fā)送 3 個(gè)數(shù)據(jù)包,需要 1 個(gè) RTT亿昏;關(guān)閉連接是“四次揮手”峦剔,4 個(gè)數(shù)據(jù)包需要 2 個(gè) RTT。
而 HTTP 的一次簡(jiǎn)單“請(qǐng)求 - 響應(yīng)”通常只需要 4 個(gè)包角钩,如果不算服務(wù)器內(nèi)部的處理時(shí)間吝沫,最多是 2 個(gè) RTT。這么算下來(lái)递礼,浪費(fèi)的時(shí)間就是“3÷5=60%”野舶,有三分之二的時(shí)間被浪費(fèi)掉了,傳輸效率低得驚人宰衙。
單純地從理論上講平道,TCP 協(xié)議你可能還不太好理解,我就拿打卡考勤機(jī)來(lái)做個(gè)形象的比喻吧供炼。
假設(shè)你的公司買了一臺(tái)打卡機(jī)一屋,放在前臺(tái),因?yàn)檫@臺(tái)機(jī)器比較貴袋哼,所以專門做了一個(gè)保護(hù)罩蓋著它冀墨,公司要求每次上下班打卡時(shí)都要先打開(kāi)蓋子,打卡后再蓋上蓋子涛贯。
可是偏偏這個(gè)蓋子非常牢固诽嘉,打開(kāi)關(guān)閉要費(fèi)很大力氣,打卡可能只要 1 秒鐘弟翘,而開(kāi)關(guān)蓋子卻需要四五秒鐘虫腋,大部分時(shí)間都浪費(fèi)在了毫無(wú)意義的開(kāi)關(guān)蓋子操作上了。
可想而知稀余,平常還好說(shuō)悦冀,一到上下班的點(diǎn)在打卡機(jī)前就會(huì)排起長(zhǎng)隊(duì),每個(gè)人都要重復(fù)“開(kāi)蓋 - 打卡 - 關(guān)蓋”的三個(gè)步驟睛琳,你說(shuō)著急不著急盒蟆。
在這個(gè)比喻里,打卡機(jī)就相當(dāng)于服務(wù)器师骗,蓋子的開(kāi)關(guān)就是 TCP 的連接與關(guān)閉历等,而每個(gè)打卡的人就是 HTTP 請(qǐng)求,很顯然辟癌,短連接的缺點(diǎn)嚴(yán)重制約了服務(wù)器的服務(wù)能力寒屯,導(dǎo)致它無(wú)法處理更多的請(qǐng)求。
長(zhǎng)連接
針對(duì)短連接暴露出的缺點(diǎn)愿待,HTTP 協(xié)議就提出了“長(zhǎng)連接”的通信方式浩螺,也叫“持久連接”(persistent connections)、“連接比越模活”(keep alive)要出、“連接復(fù)用”(connection reuse)。
其實(shí)解決辦法也很簡(jiǎn)單农渊,用的就是“成本均攤”的思路患蹂,既然 TCP 的連接和關(guān)閉非常耗時(shí)間,那么就把這個(gè)時(shí)間成本由原來(lái)的一個(gè)“請(qǐng)求 - 應(yīng)答”均攤到多個(gè)“請(qǐng)求 - 應(yīng)答”上砸紊。
這樣雖然不能改善 TCP 的連接效率传于,但基于“分母效應(yīng)”,每個(gè)“請(qǐng)求 - 應(yīng)答”的無(wú)效時(shí)間就會(huì)降低不少醉顽,整體傳輸效率也就提高了沼溜。
這里我畫(huà)了一個(gè)短連接與長(zhǎng)連接的對(duì)比示意圖。
在短連接里發(fā)送了三次 HTTP“請(qǐng)求 - 應(yīng)答”游添,每次都會(huì)浪費(fèi) 60% 的 RTT 時(shí)間系草。而在長(zhǎng)連接的情況下,同樣發(fā)送三次請(qǐng)求唆涝,因?yàn)橹辉诘谝淮螘r(shí)建立連接找都,在最后一次時(shí)關(guān)閉連接,所以浪費(fèi)率就是“3÷9≈33%”廊酣,降低了差不多一半的時(shí)間損耗能耻。顯然,如果在這個(gè)長(zhǎng)連接上發(fā)送的請(qǐng)求越多亡驰,分母就越大晓猛,利用率也就越高。
繼續(xù)用剛才的打卡機(jī)的比喻凡辱,公司也覺(jué)得這種反復(fù)“開(kāi)蓋 - 打卡 - 關(guān)蓋”的操作太“反人類”了鞍帝,于是頒布了新規(guī)定,早上打開(kāi)蓋子后就不用關(guān)上了煞茫,可以自由打卡帕涌,到下班后再關(guān)上蓋子。
這樣打卡的效率(即服務(wù)能力)就大幅度提升了续徽,原來(lái)一次打卡需要五六秒鐘蚓曼,現(xiàn)在只要一秒就可以了,上下班時(shí)排長(zhǎng)隊(duì)的景象一去不返钦扭,大家都開(kāi)心纫版。
連接相關(guān)的頭字段
由于長(zhǎng)連接對(duì)性能的改善效果非常顯著,所以在 HTTP/1.1 中的連接都會(huì)默認(rèn)啟用長(zhǎng)連接客情。不需要用什么特殊的頭字段指定其弊,只要向服務(wù)器發(fā)送了第一次請(qǐng)求癞己,后續(xù)的請(qǐng)求都會(huì)重復(fù)利用第一次打開(kāi)的 TCP 連接,也就是長(zhǎng)連接梭伐,在這個(gè)連接上收發(fā)數(shù)據(jù)痹雅。
當(dāng)然,我們也可以在請(qǐng)求頭里明確地要求使用長(zhǎng)連接機(jī)制糊识,使用的字段是 Connection绩社,值是 “keep-alive”。
不過(guò)不管客戶端是否顯式要求長(zhǎng)連接赂苗,如果服務(wù)器支持長(zhǎng)連接愉耙,它總會(huì)在響應(yīng)報(bào)文里放一個(gè) “Connection: keep-alive” 字段,告訴客戶端:“我是支持長(zhǎng)連接的拌滋,接下來(lái)就用這個(gè) TCP 一直收發(fā)數(shù)據(jù)吧”朴沿。
不過(guò)長(zhǎng)連接也有一些小缺點(diǎn),問(wèn)題就出在它的“長(zhǎng)”字上败砂。
因?yàn)?TCP 連接長(zhǎng)時(shí)間不關(guān)閉悯仙,服務(wù)器必須在內(nèi)存里保存它的狀態(tài),這就占用了服務(wù)器的資源吠卷。如果有大量的空閑長(zhǎng)連接只連不發(fā)锡垄,就會(huì)很快耗盡服務(wù)器的資源,導(dǎo)致服務(wù)器無(wú)法為真正有需要的用戶提供服務(wù)祭隔。
所以货岭,長(zhǎng)連接也需要在恰當(dāng)?shù)臅r(shí)間關(guān)閉,不能永遠(yuǎn)保持與服務(wù)器的連接疾渴,這在客戶端或者服務(wù)器都可以做到千贯。
在客戶端,可以在請(qǐng)求頭里加上“Connection: close”字段搞坝,告訴服務(wù)器:“這次通信后就關(guān)閉連接”搔谴。服務(wù)器看到這個(gè)字段,就知道客戶端要主動(dòng)關(guān)閉連接桩撮,于是在響應(yīng)報(bào)文里也加上這個(gè)字段敦第,發(fā)送之后就調(diào)用 Socket API 關(guān)閉 TCP 連接。
服務(wù)器端通常不會(huì)主動(dòng)關(guān)閉連接店量,但也可以使用一些策略芜果。拿 Nginx 來(lái)舉例,它有兩種方式:
使用“keepalive_timeout”指令融师,設(shè)置長(zhǎng)連接的超時(shí)時(shí)間右钾,如果在一段時(shí)間內(nèi)連接上沒(méi)有任何數(shù)據(jù)收發(fā)就主動(dòng)斷開(kāi)連接,避免空閑連接占用系統(tǒng)資源。
使用“keepalive_requests”指令舀射,設(shè)置長(zhǎng)連接上可發(fā)送的最大請(qǐng)求次數(shù)窘茁。比如設(shè)置成 1000,那么當(dāng) Nginx 在這個(gè)連接上處理了 1000 個(gè)請(qǐng)求后脆烟,也會(huì)主動(dòng)斷開(kāi)連接山林。
另外,客戶端和服務(wù)器都可以在報(bào)文里附加通用頭字段“Keep-Alive: timeout=value”浩淘,限定長(zhǎng)連接的超時(shí)時(shí)間。但這個(gè)字段的約束力并不強(qiáng)吴攒,通信的雙方可能并不會(huì)遵守张抄,所以不太常見(jiàn)。
隊(duì)頭阻塞
看完了短連接和長(zhǎng)連接洼怔,接下來(lái)就要說(shuō)到著名的“隊(duì)頭阻塞”(Head-of-line blocking署惯,也叫“隊(duì)首阻塞”)了。
“隊(duì)頭阻塞”與短連接和長(zhǎng)連接無(wú)關(guān)镣隶,而是由 HTTP 基本的“請(qǐng)求 - 應(yīng)答”模型所導(dǎo)致的极谊。
因?yàn)?HTTP 規(guī)定報(bào)文必須是“一發(fā)一收”,這就形成了一個(gè)先進(jìn)先出的“串行”隊(duì)列安岂。隊(duì)列里的請(qǐng)求沒(méi)有輕重緩急的優(yōu)先級(jí)轻猖,只有入隊(duì)的先后順序,排在最前面的請(qǐng)求被最優(yōu)先處理域那。
如果隊(duì)首的請(qǐng)求因?yàn)樘幚淼奶⒄`了時(shí)間咙边,那么隊(duì)列里后面的所有請(qǐng)求也不得不跟著一起等待,結(jié)果就是其他的請(qǐng)求承擔(dān)了不應(yīng)有的時(shí)間成本次员。
還是用打卡機(jī)做個(gè)比喻败许。
上班的時(shí)間點(diǎn)上,大家都在排隊(duì)打卡淑蔚,可這個(gè)時(shí)候偏偏最前面的那個(gè)人遇到了打卡機(jī)故障市殷,怎么也不能打卡成功,急得滿頭大汗刹衫。等找人把打卡機(jī)修好醋寝,后面排隊(duì)的所有人全遲到了。
性能優(yōu)化
因?yàn)椤罢?qǐng)求 - 應(yīng)答”模型不能變带迟,所以“隊(duì)頭阻塞”問(wèn)題在 HTTP/1.1 里無(wú)法解決甥桂,只能緩解,有什么辦法呢邮旷?
公司里可以再多買幾臺(tái)打卡機(jī)放在前臺(tái)黄选,這樣大家可以不用擠在一個(gè)隊(duì)伍里,分散打卡,一個(gè)隊(duì)伍偶爾阻塞也不要緊办陷,可以改換到其他不阻塞的隊(duì)伍貌夕。
這在 HTTP 里就是“并發(fā)連接”(concurrent connections),也就是同時(shí)對(duì)一個(gè)域名發(fā)起多個(gè)長(zhǎng)連接民镜,用數(shù)量來(lái)解決質(zhì)量的問(wèn)題啡专。
但這種方式也存在缺陷。如果每個(gè)客戶端都想自己快制圈,建立很多個(gè)連接们童,用戶數(shù)×并發(fā)數(shù)就會(huì)是個(gè)天文數(shù)字。服務(wù)器的資源根本就扛不住鲸鹦,或者被服務(wù)器認(rèn)為是惡意攻擊慧库,反而會(huì)造成“拒絕服務(wù)”。
所以馋嗜,HTTP 協(xié)議建議客戶端使用并發(fā)齐板,但不能“濫用”并發(fā)。RFC2616 里明確限制每個(gè)客戶端最多并發(fā) 2 個(gè)連接葛菇。不過(guò)實(shí)踐證明這個(gè)數(shù)字實(shí)在是太小了甘磨,眾多瀏覽器都“無(wú)視”標(biāo)準(zhǔn),把這個(gè)上限提高到了 6~8眯停。后來(lái)修訂的 RFC7230 也就“順?biāo)浦邸奔糜撸∠诉@個(gè)“2”的限制。
但“并發(fā)連接”所壓榨出的性能也跟不上高速發(fā)展的互聯(lián)網(wǎng)無(wú)止境的需求莺债,還有什么別的辦法嗎吗冤?
公司發(fā)展的太快了,員工越來(lái)越多九府,上下班打卡成了迫在眉睫的大問(wèn)題椎瘟。前臺(tái)空間有限,放不下更多的打卡機(jī)了侄旬,怎么辦肺蔚?那就多開(kāi)幾個(gè)打卡的地方,每個(gè)樓層儡羔、辦公區(qū)的入口也放上三四臺(tái)打卡機(jī)宣羊,把人進(jìn)一步分流,不要都往前臺(tái)擠汰蜘。
這個(gè)就是“域名分片”(domain sharding)技術(shù)仇冯,還是用數(shù)量來(lái)解決質(zhì)量的思路。
HTTP 協(xié)議和瀏覽器不是限制并發(fā)連接數(shù)量嗎族操?好苛坚,那我就多開(kāi)幾個(gè)域名比被,比如shard1.chrono.com、shard2.chrono.com泼舱,而這些域名都指向同一臺(tái)服務(wù)器www.chrono.com等缀,這樣實(shí)際長(zhǎng)連接的數(shù)量就又上去了,真是“美滋滋”娇昙。不過(guò)實(shí)在是有點(diǎn)“上有政策尺迂,下有對(duì)策”的味道。
小結(jié)
這一講中我們學(xué)習(xí)了 HTTP 協(xié)議里的短連接和長(zhǎng)連接冒掌,簡(jiǎn)單小結(jié)一下今天的內(nèi)容:
早期的 HTTP 協(xié)議使用短連接噪裕,收到響應(yīng)后就立即關(guān)閉連接,效率很低股毫;
HTTP/1.1 默認(rèn)啟用長(zhǎng)連接膳音,在一個(gè)連接上收發(fā)多個(gè)請(qǐng)求響應(yīng),提高了傳輸效率皇拣;
服務(wù)器會(huì)發(fā)送“Connection: keep-alive”字段表示啟用了長(zhǎng)連接严蓖;
報(bào)文頭里如果有“Connection: close”就意味著長(zhǎng)連接即將關(guān)閉薄嫡;
過(guò)多的長(zhǎng)連接會(huì)占用服務(wù)器資源氧急,所以服務(wù)器會(huì)用一些策略有選擇地關(guān)閉長(zhǎng)連接;
“隊(duì)頭阻塞”問(wèn)題會(huì)導(dǎo)致性能下降毫深,可以用“并發(fā)連接”和“域名分片”技術(shù)緩解吩坝。
作者:易道云控
鏈接:http://www.reibang.com/p/58ac01ffda82
來(lái)源:簡(jiǎn)書(shū)
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)哑蔫,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處钉寝。