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

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

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

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

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

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

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

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