開(kāi)頭
這篇文章通過(guò) FCC 上海線(xiàn)下和成都微信的分享,整理成文字稿順便湊一下更新,考慮到吃瓜讀者們不知道都了解到啥程度,以及我科普作者的身份(自己定的),我決定從入門(mén)到放棄的介紹一下,大致涉及:
- 什么是 CDN
- 為什么我們要用 CDN
- 訪(fǎng)問(wèn)原理
- 架構(gòu)
- 應(yīng)用與踩坑
- 現(xiàn)實(shí)世界的 CDN
由于每個(gè)地方都事無(wú)巨細(xì)講起來(lái)非常費(fèi)勁,費(fèi)勁就容易跳票,而且會(huì)導(dǎo)致篇幅過(guò)長(zhǎng),所以其實(shí)都是科普向的,如果想要深入,在每個(gè)地方都會(huì)給出鏈接,可以進(jìn)行針對(duì)性的深入閱讀。
如果有說(shuō)的不對(duì)的地方請(qǐng)各位大佬指摘,粗體部分為超鏈接。
什么是 CDN
從一個(gè)簡(jiǎn)單的栗子說(shuō)起:

「非洲農(nóng)業(yè)不發(fā)達(dá),人人都要金坷垃」——相信大家基本上都看過(guò)來(lái)自美國(guó)圣地亞哥的視頻,美國(guó)人、非洲人和日本人在一起搶來(lái)?yè)屓?。如果金坷垃只在一個(gè)地方生產(chǎn),那么非洲的運(yùn)輸成本和生產(chǎn)者的產(chǎn)能壓力都很大。
那么很簡(jiǎn)單,我們?cè)谑澜绲拿總€(gè)需要金坷垃的國(guó)家都開(kāi)代工廠(chǎng),都生產(chǎn)金坷垃——我們的 CDN 就是生產(chǎn)金坷垃的公司,而一個(gè)個(gè)「節(jié)點(diǎn)」就是代工廠(chǎng)。
CDN,中文名叫做「內(nèi)容分發(fā)網(wǎng)絡(luò)」,它的作用是減少傳播時(shí)延,找最近的節(jié)點(diǎn),實(shí)際上,盡管互聯(lián)網(wǎng)幫助我們實(shí)現(xiàn)了地球村,但是從中國(guó)到日本和從中國(guó)到臺(tái)灣的時(shí)延仍舊是不一樣的,這一點(diǎn)可以從?ping?和?traceroute?中看出。
CDN 的優(yōu)點(diǎn)
訪(fǎng)問(wèn)加速
CDN 作為前端性能經(jīng)典手段,相信大家已經(jīng)無(wú)腦使用了,正如前面所說(shuō)的,減少了時(shí)延,從很大程度上就能作為加速手段了。實(shí)際上,真正的 CDN 并不是前面舉例的一個(gè)國(guó)家一個(gè)節(jié)點(diǎn),甚至是一個(gè)運(yùn)營(yíng)商,一個(gè)省份乃至地區(qū)都會(huì)有節(jié)點(diǎn)。

?
減輕源站(服務(wù)器)負(fù)載
一個(gè)非常簡(jiǎn)單就能想明白的問(wèn)題,如果 CDN 已經(jīng)能幫我返回?cái)?shù)據(jù)了,那么請(qǐng)求就不會(huì)到達(dá)源站,源站(服務(wù)器)的負(fù)載就減輕了。
抗住攻擊
既然源站的負(fù)載被減輕了,那么在受到 DDOS 攻擊的時(shí)候,也能談笑風(fēng)生。
當(dāng)年阮老師被 DDOS 鬧的滿(mǎn)城風(fēng)雨,后來(lái)阮老師就把內(nèi)容開(kāi)始遷移到 GayHub……
然后本來(lái)我不用更新內(nèi)容,就在最近,阮老師發(fā)布了一篇 DDOS 防御指南,然后接著被攻擊,又癱瘓了,防御指南中說(shuō)自己受到了 CC,然后遷移到了騰訊云,啪啪啪打了我的臉……當(dāng)然,其實(shí) CC 并沒(méi)有那么難防御,但是不在分享主題內(nèi)容中,感興趣的可以之后聊……
受到阮老師啟發(fā),于是我糊了一個(gè)架構(gòu)圖,一個(gè)博客系統(tǒng)的思路圖,基本上和市面上的 Jekyll 和 Hexo 一樣,當(dāng)時(shí)我的設(shè)想是,把評(píng)論之類(lèi)的全部抽出來(lái),這樣打的時(shí)候最多打掛評(píng)論之類(lèi)的,對(duì)于博文本身不會(huì)有任何影響。后來(lái)我回去查了一下,發(fā)現(xiàn)這不就和市面上的一樣嘛——不錯(cuò)!
那么既然靜態(tài)資源能上 CDN,我們的 API,我們的 MVC 頁(yè)面能不能也上 CDN?答案是可以的,關(guān)鍵就是這個(gè)叫全站加速的東西。
啥玩樣兒?其實(shí) CDN 就是一個(gè)緩存,區(qū)別只是這個(gè)緩存是放在網(wǎng)絡(luò)服務(wù)提供商節(jié)點(diǎn)的。
最簡(jiǎn)單的模型像這張圖,這張圖來(lái)自《大型網(wǎng)站技術(shù)架構(gòu) 核心原理與案例分析》一書(shū)。
訪(fǎng)問(wèn)原理
從我們發(fā)起請(qǐng)求,到到達(dá) CDN 節(jié)點(diǎn),到底經(jīng)過(guò)了哪些東西,CDN 是怎么加速我們的請(qǐng)求的呢?

?
這張圖也是網(wǎng)上找來(lái)的。
首先我們?cè)诘刂窓阪I入一個(gè)網(wǎng)址,瀏覽器發(fā)現(xiàn)本地沒(méi)有關(guān)于這個(gè)網(wǎng)址的 DNS 緩存,所以向網(wǎng)站的 DNS 服務(wù)器發(fā)起請(qǐng)求。
網(wǎng)站的 DNS 服務(wù)器設(shè)置了 CNAME,指向了某個(gè) CDN 服務(wù)器,也就是我們常見(jiàn)的阿里云、騰訊云、Cloudflare 之類(lèi)的,去請(qǐng)求 CDN 中的智能 DNS 均衡負(fù)載系統(tǒng)。
均衡負(fù)載系統(tǒng)解析域名,把對(duì)用戶(hù)響應(yīng)最快的節(jié)點(diǎn)返回給用戶(hù),然后用戶(hù)向該節(jié)點(diǎn)發(fā)出請(qǐng)求。
如果是第一次訪(fǎng)問(wèn)該內(nèi)容,CDN 服務(wù)器會(huì)向源站請(qǐng)求數(shù)據(jù)并緩存,否則的話(huà),直接在緩存節(jié)點(diǎn)中找到該數(shù)據(jù),將請(qǐng)求結(jié)果發(fā)給用戶(hù)。
對(duì)于最簡(jiǎn)單的 CDN 系統(tǒng)而言,只要一臺(tái) DNS 調(diào)度服務(wù)器和一個(gè)節(jié)點(diǎn)服務(wù)器即可,但在復(fù)雜的應(yīng)用中,會(huì)存在多級(jí)緩存,多臺(tái) Cache 來(lái)協(xié)同工作。
這里我之前在博客里寫(xiě)過(guò)類(lèi)似的內(nèi)容(實(shí)際上就是摘抄的)
架設(shè)原理
如果你要架設(shè)一個(gè) CDN,大概需要怎么做?記住我們剛才介紹的內(nèi)容,重點(diǎn)是,CDN 仍然是一個(gè)緩存。

?
拿來(lái)阿里云 CDN 的架構(gòu)圖口胡一下,我也沒(méi)有搭建過(guò),如果解釋的不對(duì)請(qǐng)大家指出。
既然是緩存,那么很明顯,也就是均衡負(fù)載加上緩存調(diào)度的搭配,根據(jù)我們剛才所說(shuō)的訪(fǎng)問(wèn)原理,其實(shí)主要的重點(diǎn)除了均衡負(fù)載與緩存外,就是一個(gè)中央的 DNS 調(diào)度器。
實(shí)際上,和計(jì)算機(jī)的多級(jí)緩存設(shè)計(jì)以及后端的多級(jí)緩存設(shè)計(jì)一樣,每一層的 cache 一級(jí)比一級(jí)大,可以存儲(chǔ)更多資源,但是響應(yīng)一個(gè)比一個(gè)耗時(shí),如果在 L1 中無(wú)法命中,那么我們就會(huì)去 L2 找,L2 無(wú)法命中才會(huì)回到源站,這樣可以有效的避免回到源站過(guò)于頻繁的問(wèn)題。
?
接下來(lái)這張圖,我就有點(diǎn)編不下去了,在阿里,主要使用 LVS + Tengine 做負(fù)載均衡,然后用 Swift 做 HTTP 緩存。這是他們自己說(shuō)的,但是和我沒(méi)什么關(guān)系,我主要講的是左邊那個(gè)花括號(hào)的一致性 Hash。
一致性 Hash 就是把對(duì)象映射在 232 個(gè)桶空間里,像一個(gè)閉合的圓環(huán),當(dāng)然,實(shí)際上,我們將機(jī)器也映射到這個(gè)圓環(huán)中,比如利用別名或者 IP,順時(shí)針將對(duì)象將內(nèi)容存儲(chǔ)到離自己最近的機(jī)器中,刪除和添加節(jié)點(diǎn)也一樣,添加和刪除節(jié)點(diǎn)之后,根據(jù)順時(shí)針遷移,原來(lái)的對(duì)象會(huì)進(jìn)行重新計(jì)算。
關(guān)于這點(diǎn)需要詳細(xì)了解可以看這篇:https://blog.csdn.net/cywosp/article/details/23397179

?
然后說(shuō)完這個(gè)關(guān)鍵性算法,我們就差不多消化了——隔壁阿里云 CDN 是怎么搞出來(lái)的。
當(dāng)然這依舊和我們沒(méi)啥關(guān)系……
應(yīng)用與踩坑
最常見(jiàn)的應(yīng)用,就是用于前端靜態(tài)資源的加速,實(shí)際上,利用 CDN,我們甚至可以做出一套屬于自己的?jsDelivr。
不過(guò),使用 CDN 的時(shí)候,有一些基本法需要我們了解。
緩存設(shè)置
第一,緩存的設(shè)置,max-age 我們都用過(guò),在 Cache-Control 中經(jīng)常用于緩存的控制,可是 max-age 設(shè)置的緩存會(huì)應(yīng)用于一個(gè)請(qǐng)求經(jīng)過(guò)的每一級(jí),如果我們希望 CDN 不緩存那么久,要怎么辦呢?那就是 s-maxage,它用于設(shè)置代理服務(wù)器的緩存時(shí)間,會(huì)覆蓋 max-age 的設(shè)置,這樣我們可以把 max-age 用于本地緩存,把 s-maxage 用于 CDN 緩存時(shí)間,避免臟數(shù)據(jù)的產(chǎn)生。
緩存命中率
對(duì)于一個(gè)緩存而言,還有一點(diǎn)很重要,就是你的緩存到底有沒(méi)有用,衡量這個(gè)東西的就是緩存命中率。如果只是靜態(tài)資源,在刷新緩存之后,可能會(huì)導(dǎo)致命中率下降,因此 CDN 的資源不適合經(jīng)常刷新,換句話(huà)說(shuō),如果一個(gè)請(qǐng)求結(jié)果會(huì)經(jīng)常進(jìn)行變更,那么 CDN 基本就沒(méi)什么存在的意義。
判斷是否命中緩存
無(wú)論是我們自己在開(kāi)發(fā)過(guò)程中,還是幫客戶(hù) debug 的過(guò)程中,我們都會(huì)考慮一件事——這個(gè)資源是否命中了CDN,是否是因?yàn)镃DN導(dǎo)致的問(wèn)題,這個(gè)時(shí)候就要秀一波操作了。

?
在各大廠(chǎng)商的 CDN 返回的數(shù)據(jù)中都會(huì)有一個(gè) X-Cache,上面內(nèi)容是 Hit 或者 Miss,還會(huì)加上諸如 Memory 或者 Disk 的縮寫(xiě)表示內(nèi)存還是磁盤(pán),如果出現(xiàn) Upstream 或者 Miss 則表示沒(méi)有命中。
資源預(yù)熱
緩存設(shè)計(jì)中,預(yù)熱是很重要的環(huán)節(jié),在最初剛開(kāi)始啟動(dòng) CDN 的時(shí)候,CDN 上并沒(méi)有緩存數(shù)據(jù),此刻大量的請(qǐng)求全部打向源站,肯定會(huì)把源站打掛,預(yù)熱就是實(shí)現(xiàn)緩存好熱門(mén)數(shù)據(jù),這樣在業(yè)務(wù)上線(xiàn)時(shí),CDN 上已經(jīng)有所需的數(shù)據(jù)了。
Vary
此外很多 CDN 都不支持 Vary 頭,這樣 CORS 需要的 Vary: Origin 就沒(méi)法保證了,遇到這種情況,比如你發(fā)現(xiàn) Origin 頭被緩存了,就只能把跨域頭改為 * 去匹配。
Range
另外如果是很大的文件,往往是用 RANGE 頭分片載入的,但如果 CDN 沒(méi)有進(jìn)行分片,就會(huì)重復(fù)向源站請(qǐng)求完整資源,CDN 就白搞了,啟用 RANGE 回源,就可以減少流量的損耗,正確的設(shè)置 RANGE 回源,就能夠正確的命中 CDN 緩存。
無(wú)私鑰 HTTPS
另外重點(diǎn)說(shuō)一下:這年頭上 HTTPS 已經(jīng)是常規(guī)基本法了,而不上 HTTPS 才是個(gè)噴點(diǎn),CDN 為了避免篡改和劫持,當(dāng)然也得上 HTTPS,但這樣就導(dǎo)致我們必須要將證書(shū)和私鑰傳輸?shù)?CDN 的平臺(tái)中去,對(duì)于安全性是一個(gè)隱患。
所以就有了無(wú)私鑰解決方案,用戶(hù)搭建私鑰服務(wù)器,由 CDN 方去請(qǐng)求簽名。圖為阿里云的實(shí)現(xiàn),只要在自己的服務(wù)器上部署 KeyServer 和配置就可以用了。
?
這里我在上海 FCC 分享的時(shí)候有同學(xué)想要具體了解一下,具體來(lái)說(shuō)的話(huà)涉及到很多 HTTPS 加密的事情,暫時(shí)不做展開(kāi),可以看一下 CloudFlare 發(fā)的一篇文章,我正好找到了翻譯版:https://www.zcfy.cc/article/keyless-ssl-the-nitty-gritty-technical-details-967.html
現(xiàn)實(shí)世界的 CDN

?比如,節(jié)點(diǎn)掛了,直接導(dǎo)致的是用戶(hù)的損失,尤其是體量大的公司依賴(lài) CDN 進(jìn)行靜態(tài)資源管理的時(shí)候,發(fā)生這樣的事情后果會(huì)非常嚴(yán)重。
其次,便宜沒(méi)好貨:本來(lái)在只有網(wǎng)宿而沒(méi)有阿里云的時(shí)代,CDN 是很昂貴的,阿里騰訊在拉低 CDN 價(jià)格的同時(shí),也拉低了 CDN 的質(zhì)量,部分節(jié)點(diǎn)的訪(fǎng)問(wèn)質(zhì)量不太高會(huì)導(dǎo)致有些用戶(hù)訪(fǎng)問(wèn)的網(wǎng)絡(luò)質(zhì)量非常差。
然后,一個(gè)微小的科普:什么是混合 CDN——混合 CDN 這個(gè)名詞看著很高端,實(shí)際上就是,我們用了多家廠(chǎng)商的 CDN,可能也包括自己建的,然后誰(shuí)好的選誰(shuí),但是有的時(shí)候反而會(huì)造成服務(wù)不可控,進(jìn)一步劣化 CDN 的質(zhì)量。
總結(jié)
CDN 這個(gè)東西本質(zhì)就是一個(gè)緩存,只是這個(gè)緩存離你特別特別的近,作為用戶(hù)還是開(kāi)發(fā)都能從中享受到一點(diǎn)福利,但作為一個(gè)服務(wù)于企業(yè)的開(kāi)發(fā)人員,不僅要考慮 CDN 的優(yōu)點(diǎn),也要知道 CDN 給我們帶來(lái)的坑,這樣才能靠譜的作為 CDN 的使用者。
源于知呼文章:https://zhuanlan.zhihu.com/p/39028766
作者:敖天羽