摘要:所以我們要時(shí)刻留意,在使用時(shí),一定要根據(jù)緩存命中率作出調(diào)整,在不發(fā)生緩存錯(cuò)亂的情況之下,盡可能的提高資源的緩存命中率。
寫(xiě)在前面
最近抽空參加了幾場(chǎng)大廠的面試,突然發(fā)現(xiàn)一個(gè)現(xiàn)象,就是不論面試偏服務(wù)端的職位還是偏客戶端的職位,不論面試的 5 年以上的高級(jí)職位,還是 3 年左右的中級(jí)職位,面試官開(kāi)頭所問(wèn)問(wèn)題必然是關(guān)于 HTTP 的。
我記得之前找工作的時(shí)候,似乎都是先考察一些職位所需技能領(lǐng)域的基礎(chǔ)知識(shí),之后再考察關(guān)于 HTTP 的東西,現(xiàn)在大家都將 HTTP 的問(wèn)題放到面試的開(kāi)頭來(lái)問(wèn),我覺(jué)的應(yīng)該是越來(lái)越多的招聘者意識(shí)到,作為一個(gè) Web 開(kāi)發(fā)者,HTTP 真的是太重要了,必須要先考察。
回想起來(lái),這幾年我自己對(duì)于 HTTP 的學(xué)習(xí)大多是碎片化的,很多東西無(wú)法系統(tǒng)地在腦海中組織起來(lái)。雖然感覺(jué) HTTP 整體的學(xué)習(xí)難度是比較低的,但是各個(gè)知識(shí)點(diǎn)交雜在一起又變得很復(fù)雜很難,相信大家都會(huì)有同感。同時(shí)有些知識(shí)點(diǎn),如果在實(shí)際工作中沒(méi)有采坑或者刻意深挖的話,很自然地就被忽略了。
由于在之前一次面試中,被狠狠地問(wèn)了若干關(guān)于 Vary 的問(wèn)題,所以想抽一些時(shí)間整理一下那些比較容易讓人忽略的知識(shí)點(diǎn),算是查漏補(bǔ)缺吧。
內(nèi)容協(xié)商首先需要了解的是內(nèi)容協(xié)商這個(gè)術(shù)語(yǔ)。當(dāng)我們通過(guò)某個(gè) URI 來(lái)訪問(wèn)其指向的資源時(shí),HTTP 協(xié)議可以通過(guò)內(nèi)容協(xié)商機(jī)制提供資源的不同的展示形式。
如果缺少服務(wù)端開(kāi)發(fā)經(jīng)驗(yàn)話,對(duì)于這個(gè)概念可能會(huì)感到陌生,但其實(shí)我們?cè)诠ぷ髦袔缀醵紩?huì)遇到它,比如在調(diào)用接口時(shí),經(jīng)常會(huì)用到 Accept: application/json 這個(gè)頭部,有時(shí)可能會(huì)用到 Accept: application/xml,這就是內(nèi)容協(xié)商,前者期望接口返回 json 格式的數(shù)據(jù),而后者期望返回 xml 格式的數(shù)據(jù)。
一般客戶端涉及的常見(jiàn)頭部有以下幾個(gè):
Accept: 聲明客戶端可以處理的資源格式
Accept-Charset: 聲明客戶端可以處理的字符集類型
Accept-Language: 聲明客戶端可以理解的自然語(yǔ)言
Accept-Encoding: 聲明客戶端支持的編碼格式
而服務(wù)端涉及的常見(jiàn)頭部包括:
Content-Type: 指示資源的 MIME 類型
Content-Language: 指示該資源所期望的自然語(yǔ)言
Content-Encoding: 指示資源使用該編碼格式進(jìn)行內(nèi)容轉(zhuǎn)換
仔細(xì)觀察的話,會(huì)發(fā)現(xiàn)它們其實(shí)存在著一定程度的對(duì)應(yīng)關(guān)系。原因也很簡(jiǎn)單,既然是協(xié)商,那必然就會(huì)和兩個(gè)人在進(jìn)行說(shuō)話一樣,如果兩者之間的對(duì)話內(nèi)容沒(méi)有關(guān)聯(lián),他們還怎么溝通呢?客戶端和服務(wù)端進(jìn)行溝通同理。
如果想詳細(xì)了解該機(jī)制,可以參考MDN的文檔,很詳細(xì),這里就不多說(shuō)了。
這里順帶說(shuō)明一下,對(duì)于內(nèi)容協(xié)商機(jī)制中涉及的頭部,從 web 發(fā)展歷史上來(lái)看已經(jīng)沒(méi)有什么實(shí)質(zhì)的用途了,原因如下(有興趣的話可以閱讀這篇wiki):
Accept-Charset: 由于 utf-8 成為主流的字符集類型,所以使用其他字符集類型的服務(wù)可以將其轉(zhuǎn)換為 utf-8 類型
Accept-Language: 大體包含以下幾點(diǎn)
提供多種語(yǔ)言服務(wù)的網(wǎng)站往往是基于某種特定語(yǔ)言構(gòu)建,再提供其他語(yǔ)言支持的,這樣每種語(yǔ)言類型的內(nèi)容在質(zhì)量上層次不齊,而訪問(wèn)者可能會(huì)更傾向于內(nèi)容質(zhì)量更高的那一種語(yǔ)言,而內(nèi)容協(xié)商機(jī)制無(wú)法替代用戶的主觀判斷
實(shí)踐中,對(duì)于切換網(wǎng)站語(yǔ)言的功能,切換方式往往更傾向于主動(dòng)切換(比如提供一個(gè)切換的按鈕)而非自動(dòng)切換
瀏覽器在用戶不提供語(yǔ)言相關(guān)配置的情況下,很難猜測(cè)用戶的自然語(yǔ)言傾向(一般可能會(huì)根據(jù)地理定位、ip等因素猜測(cè)),打個(gè)比方,比如我會(huì)經(jīng)常出差去日本,但這不代表我會(huì)說(shuō)日語(yǔ),同時(shí)雖然我掛了加拿大的 vps,但是提供中文內(nèi)容的網(wǎng)站,我還是傾向于看中文
Accept: 與 Accept-Language 類似,同樣因?yàn)閮?nèi)容的格式會(huì)因用戶的主觀意識(shí)而不同,還有諸多其他因素制約內(nèi)容協(xié)商機(jī)制,所以最終失敗了。
唯一有些用途的是 Accept-Encoding,但鑒于如今大部分現(xiàn)代瀏覽器都已支持多種壓縮方式(常見(jiàn)的如 gzip、br),因此一定程度上已經(jīng)不需要額外聲明這個(gè)頭部了,雖然大部分瀏覽器都會(huì)自動(dòng)發(fā)送這個(gè)頭部,但其實(shí)這會(huì)造成額外 23 字節(jié)的浪費(fèi)。
Vary 頭部在理解(或者鞏固)了內(nèi)容協(xié)商的概念后,就可以介紹 Vary 這個(gè)頭部了。直接引用 MDN 對(duì)于它的描述:
The Vary HTTP response header determines how to match future request headers to decide whether a cached response can be used rather than requesting a fresh one from the origin server.Vary 是一個(gè)HTTP響應(yīng)頭部信息,它決定了對(duì)于未來(lái)的一個(gè)請(qǐng)求頭,應(yīng)該使用一個(gè)緩存作為響應(yīng)還是向源服務(wù)器請(qǐng)求一個(gè)新的響應(yīng)。
單純靠文檔對(duì)于 Vary 的描述來(lái)理解它其實(shí)是有些困難的,最起碼我會(huì)有這種感覺(jué)。
這個(gè)頭部的語(yǔ)法和其他的 HTTP 頭部類似,如下:
Vary:, , ...
不同的頭部之間使用逗號(hào)進(jìn)行分割,同時(shí)可以指定 * 為它的值,這樣等價(jià)于將資源視為唯一,并不進(jìn)行緩存,但這并不是最佳實(shí)踐,因此不建議這么做。
Vary 的工作原理一句話概括它的工作原理就是,就是它表示某個(gè)響應(yīng)因某個(gè)響應(yīng)頭部而不同。舉個(gè)例子,比如 Vary: Accept 的意思即為,響應(yīng)因請(qǐng)求資源格式頭部而不同,那么通過(guò)相同 URI 訪問(wèn)的資源就可以根據(jù)這個(gè)頭上知道其內(nèi)容格式不同。
但我們已經(jīng)知道,對(duì)于大部分內(nèi)容協(xié)商機(jī)制中涉及的頭部,已經(jīng)被看作是失敗的,那么 Vary 和這些頭部搭配使用還有什么意義呢?話雖如此,但 Vary 還可以與 HTTP 中其他的頭部來(lái)搭配使用,從而滿足很多應(yīng)用場(chǎng)景下的特殊需求,比如動(dòng)態(tài)服務(wù)、防止緩存錯(cuò)亂等。
Vary 的應(yīng)用場(chǎng)景以下簡(jiǎn)單羅列一些常用的應(yīng)用場(chǎng)景以及采坑指南。
Vary 與 動(dòng)態(tài)服務(wù)關(guān)于動(dòng)態(tài)服務(wù),最常見(jiàn)的莫過(guò)于 Vary: User-Agent。眾所周知,UA 是一段特征字符串,通常包含區(qū)分客戶端類型、操作系統(tǒng)、版本號(hào)等信息,隨著移動(dòng) web 應(yīng)用變得越流行,一個(gè)應(yīng)用網(wǎng)站同時(shí)提供桌面和移動(dòng)兩種版本的應(yīng)用是很常見(jiàn)的事情。通過(guò)設(shè)置 Vary: User-Agent 頭部,對(duì)于搜索引擎,對(duì)于關(guān)鍵字的搜索結(jié)果可以提供更加準(zhǔn)確的應(yīng)用版本,對(duì)于客戶端,可以使其從緩存服務(wù)器獲取到相應(yīng)應(yīng)用類型的緩存版本,而不是錯(cuò)誤地將桌面版緩存?zhèn)鬟f給移動(dòng)版應(yīng)用。
web 應(yīng)用的性能在加載速度這一指標(biāo)上,很大程度上取決于加載資源的大小,而圖片資源是所占比例最大的一塊。為了減少圖片的大小,除了對(duì)常見(jiàn)的圖片格式進(jìn)行壓縮以外,chrome 推出的 WebP 格式也是不錯(cuò)的選擇。但是這里的問(wèn)題是,不是所有的瀏覽器都支持 WebP 圖片格式的,所以這里使用 Vary: Accept 來(lái)針對(duì)瀏覽器的支持情況返回相應(yīng)的緩存副本,支持則返回 WebP 格式,不支持則返回縮略圖或者原圖。
還有其他關(guān)于動(dòng)態(tài)服務(wù)的場(chǎng)景,比如要針對(duì)不同分辨率的屏幕加載不同質(zhì)量的圖片(Client Hints 相關(guān)的頭部)、針對(duì)不同用戶身份提供不同的資源(Cookie頭部)等等。
Vary 與 緩存錯(cuò)亂有時(shí)候我們會(huì)發(fā)現(xiàn)響應(yīng)中存在 Vary: Accept-Encoding 頭部信息,我原先按照內(nèi)容協(xié)商機(jī)制中所描述的內(nèi)容來(lái)理解,但到后來(lái)才發(fā)現(xiàn),其實(shí)很大程度上是為了防止緩存錯(cuò)亂的問(wèn)題。
設(shè)想一下,如果沒(méi)有這個(gè)頭部,當(dāng)兩個(gè)分別支持 gzip 和 不支持 gzip 的客戶端對(duì)同一份資源進(jìn)行獲取時(shí),結(jié)果會(huì)變得十分微妙。如果不支持 gzip 的客戶端先訪問(wèn),緩存代理會(huì)緩存未壓縮的版本,那么當(dāng)支持 gzip 的客戶端再訪問(wèn)時(shí),由于命中緩存,雖然它支持 gzip 但也只能加載未壓縮的資源。反過(guò)來(lái)同樣如此,支持 gzip 客戶端先訪問(wèn),則緩存代理會(huì)緩存壓縮版本,當(dāng)不支持 gzip 的客戶端再訪問(wèn)時(shí),緩存同樣命中,但是由于它無(wú)法對(duì)壓縮資源解碼,所以會(huì)呈現(xiàn)亂碼。
通過(guò) Vary: Accept-Encoding 我們可以防止這種情況的發(fā)生,因?yàn)?Vary 在這里其實(shí)是扮演著校驗(yàn)器的角色,它會(huì)進(jìn)一步對(duì)命中緩存的資源進(jìn)行再校驗(yàn),如果發(fā)現(xiàn)頭部信息不同,則會(huì)將緩存資源視為無(wú)效,從而將請(qǐng)求繼續(xù)轉(zhuǎn)發(fā)至源服務(wù)器。這對(duì)于緩存代理服務(wù)器也有一定的益處,因?yàn)榭梢杂杏幸罁?jù)地針對(duì)不同的 Accept-Encoding 緩存不同的資源副本。
Vary 與 緩存命中率Vary 雖然可以防止緩存錯(cuò)亂,但并不代表可以濫用,盲目的使用會(huì)適得其反,比如之前提及的 Vary: *,這樣等價(jià)于將每個(gè)請(qǐng)求視為唯一,并且不緩存其響應(yīng)資源,除非有意為之,不然沒(méi)有人會(huì)犧牲緩存帶來(lái)的性能提升。
同時(shí)對(duì)于一些 Header 的值是開(kāi)放性的,比如之前提及的 User-Agent,如果單純從字面量來(lái)匹配的話,眾多桌面瀏覽器的值會(huì)因各種因素而不同的,如果僅是簡(jiǎn)單地將 UA 作為區(qū)分桌面端和移動(dòng)端的依據(jù),那么緩存命中率會(huì)達(dá)到一個(gè)很低的水平。如何解決這個(gè)問(wèn)題呢?可以將這些 UA 頭部的值進(jìn)行標(biāo)準(zhǔn)化,比如可以通過(guò)正則匹配所有桌面瀏覽器的 UA 并重新更改為 Desktop,之后再轉(zhuǎn)發(fā)至緩存代理和源服務(wù)器,這樣有利于提高緩存命中率,關(guān)于這部分的內(nèi)容,可以參考這篇文章,其中有很細(xì)致的講解。
所以我們要時(shí)刻留意,在使用 Vary 時(shí),一定要根據(jù)緩存命中率作出調(diào)整,在不發(fā)生緩存錯(cuò)亂的情況之下,盡可能的提高資源的緩存命中率。
Vary 與 CORS對(duì)于跨域的有情況,Vary 也包含一些內(nèi)容。HTTP 協(xié)議規(guī)定,當(dāng)服務(wù)端響應(yīng)包含 Access-Control-Allow-Origin 頭部,且它的值是一個(gè)具體的域名而不是通配符 *,那么這時(shí)必須要包含 Vary: Origin 這個(gè)頭部。
為什么要包含這個(gè)頭部,因?yàn)檎?qǐng)求頭中的 Origin 頭部代表了該請(qǐng)求來(lái)源的具體域名信息,那么對(duì)于不同域名網(wǎng)站所發(fā)起的請(qǐng)求,會(huì)使用僅屬于它本身的緩存。一般而言,我們很少會(huì)遇到這種問(wèn)題,因?yàn)橐话愣紝?Access-Control-Allow-Origin 設(shè)置為了 *,至少我自己是這樣的。如果想進(jìn)一步了解 Vary 和 CORS 的內(nèi)容,可以參考這篇文章。
最后差不多就這么多內(nèi)容了,如有錯(cuò)誤,還望指正。
參考鏈接內(nèi)容協(xié)商
Best Practices for Using the Vary Header
IE 與 Vary
CORS
Vary
Response with Vary Header
Understanding Vary Header
Getting the most out of Vary with Fastly
Why not conneg
條件型 CORS 響應(yīng)下因缺失 Vary: Origin 導(dǎo)致的緩存錯(cuò)亂問(wèn)題
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/62052.html
摘要:醞釀許久之后,筆者準(zhǔn)備接下來(lái)撰寫(xiě)前端面試題系列文章,內(nèi)容涵蓋瀏覽器框架分鐘搞定常用基礎(chǔ)知識(shí)前端掘金基礎(chǔ)智商劃重點(diǎn)在實(shí)際開(kāi)發(fā)中,已經(jīng)非常普及了。 這道題--致敬各位10年阿里的前端開(kāi)發(fā) - 掘金很巧合,我在認(rèn)識(shí)了兩位同是10年工作經(jīng)驗(yàn)的阿里前端開(kāi)發(fā)小伙伴,不但要向前輩學(xué)習(xí),我有時(shí)候還會(huì)選擇另一種方法逗逗他們,拿了網(wǎng)上一道經(jīng)典面試題,可能我連去阿里面試的機(jī)會(huì)都沒(méi)有,但是我感受到了一次面試1...
摘要:規(guī)定了標(biāo)記語(yǔ)言的規(guī)則,這樣瀏覽器才能正確地呈現(xiàn)內(nèi)容不基于,所以不需要引用標(biāo)簽都用來(lái)做什么的提供有關(guān)頁(yè)面的元信息,常用于定義頁(yè)面的說(shuō)明,關(guān)鍵字,最后修改日期,和其它的元數(shù)據(jù)。這些元數(shù)據(jù)將服務(wù)于瀏覽器如何布局或重載頁(yè)面,搜索引擎和其它網(wǎng)絡(luò)服務(wù)。 HTML規(guī)范 HTML規(guī)范文檔 H4時(shí)代被規(guī)定為錯(cuò)誤的行為,在H5時(shí)代全都被合理化了,比如標(biāo)簽不區(qū)分大小寫(xiě)、只有開(kāi)始標(biāo)簽沒(méi)有結(jié)束標(biāo)簽、屬性值不...
摘要:因?yàn)樵陧?yè)面加載完成后,引擎維護(hù)著兩個(gè)隊(duì)列,一個(gè)是按頁(yè)面順序加載的執(zhí)行隊(duì)列,還有一個(gè)空閑隊(duì)列,使用定時(shí)函數(shù)就是將回調(diào)函數(shù)加入到空閑隊(duì)列中,故和其他定時(shí)器是并發(fā)執(zhí)行的。 1.window.onload和$(document).ready()的區(qū)別: ①執(zhí)行時(shí)間:window.onload會(huì)在所有元素,包括圖片,引用文件加載完成之后執(zhí)行,而$(document).ready()則會(huì)在HTML...
摘要:同源策略是什么跨域通信同源兩個(gè)文檔同源需滿足協(xié)議相同域名相同端口相同跨域通信進(jìn)行操作通信時(shí)如果目標(biāo)與當(dāng)前窗口不滿足同源條件,瀏覽器為了安全會(huì)阻止跨域操作。 同源策略是什么? javascript跨域通信 同源:兩個(gè)文檔同源需滿足 協(xié)議相同 域名相同 端口相同 跨域通信:js進(jìn)行DOM操作、通信時(shí)如果目標(biāo)與當(dāng)前窗口不滿足同源條件,瀏覽器為了安全會(huì)阻止跨域操作??缬蛲ㄐ磐ǔS幸韵路椒?...
閱讀 808·2021-10-09 09:44
閱讀 2105·2021-09-22 15:54
閱讀 5204·2021-09-22 10:55
閱讀 1504·2019-08-29 18:41
閱讀 825·2019-08-29 11:24
閱讀 2167·2019-08-28 18:20
閱讀 1098·2019-08-26 11:51
閱讀 3114·2019-08-26 11:00