分享CDN內容分發網路實戰技巧
給大家分享下關於 CDN 的東西,總共分為 2個大部分:原理、詳解。

首先說一下 CDN 的基本原理部分,主要分 4 塊來描述:CDN 的由來、排程是怎麼做的、快取是什麼、關於安全。
什麼是CDN?
這是一個做過 CDN 之後的拓撲圖,裡面有幾個概念需要明確一下:
Origin Server: 源站,也就是做 CDN 之前的客戶真正的伺服器;
User: 訪問者,也就是要訪問網站的網民;
Edge Server: CDN 的伺服器,不單隻“邊緣伺服器”,這個之後細說;s/(單)只/指/;
Last Mile: 最後一公里,也就是網民到他所訪問到的 CDN 伺服器之間的路徑。
我們平時所使用的DNS伺服器,一般稱之為LDNS,在解析一個域名的時候,一般有兩個情況,一種是域名在DNS上有記錄,另一種情況是沒有記錄,兩種情況的處理流程不一樣。
當你訪問163這個域名時,如果LDNS上有快取記錄,那它會直接將IP地址直接給你。如果沒有快取記錄,它將會一步步向後面的伺服器做請求,然後將所有資料進行彙總交給最終的客戶。
當你訪問163這個地址時,實際上如果本身沒有內容的話,它要去後面拿資料,這個過程術語叫遞迴,它首先會向全球13個根域伺服器請求,問com域名在哪,然後根域伺服器作出回答,一步步往下,這個過程較複雜,如果大家感興趣可去查相關資料,在這就不一一贅述。
DNS排程

肯定很多人好奇是如何進行排程和進行定位的?
其實也是通過LDNS的具體地址來進行的,比如,看圖,假設你是一個廣東電信客戶,那你所使用的DNS伺服器去做遞迴的時會訪問到某一個CDN廠商的GRB,全球的一個排程系統,他就能看到來自於哪個LDNS。假設如果使用者和LDNS使用同一個區域的伺服器,他就會間接認為使用者也是廣東電信的。
再舉個例子,比如說北京聯通的使用者,它使用DNS地址,一般自動給它分配的是北京聯通的伺服器,這個伺服器去做遞迴的時候,排程伺服器就會看到這個請求是來自北京聯通的LDNS伺服器,就會給它分配一個北京聯通的伺服器地址,然後讓來自北京聯通的使用者直接訪問北京聯通的伺服器地址,這樣來實現精準的區域性排程。
從這個排程理論上看,我們可以發現一個問題,就是假設使用者所使用的LDNS地址和你是同一個區域,那麼這個時候我們的排程才有可能是正確的。但是舉個例子來說,如果你是北京聯通的使用者,可是使用的是廣東電信的LDNS的話,就會讓GRB系統誤以為你是廣東電信的客戶,這樣就會錯誤的排程過去。
之前有一次我在小區裡上網,由於我的路由器有問題,我設了202.106.0.20的北京聯通的DNS伺服器地址,後來出差去深圳,訪問比較大的網站發現比較慢,經過分析,才發現原來我設的DNS地址是北京聯通的,而我在廣東和深圳使用的網路都是電信接入的,但是分配給我的是北京聯通的地址,那我用電信的線路訪問北京聯通的地址,勢必就會很慢。
因為剛才講到的DNS排程機制存在一定問題,所以在某些場合下我們會使用第二種排程機制,叫HTTP的排程。
瞭解http協議的人知道,在http協議中有一個叫302跳轉的功能,它的實現並不是說你訪問一個URL,然後馬上吐給你想要的資料,而是吐給你一個302返回信令,這個信令頭部會告訴你,有一個location目標,這個location就是告訴你下一步將要怎麼做,而具體排程是通過location來實現的。
即便我所使用的DNS和我不在一個區域,但當我訪問http server的時,這個server是由CDN公司提供的。客戶訪問server的時,雖說通過DNS方式無法拿到客戶的真正IP地址,但是如果你訪問的是http server,他一定能直接看到客戶的真實IP,利用這種方法可以進行排程的糾偏,可以直接返回給你一個302,然後location裡面攜帶一個真正離你最近的CDN server。
這種排程方式,優勢是準確,但是也存在弊端,它需要有一次TCP的三次握手建連,他不像DNS那樣直接請求一個數據包過去給一個反饋就OK了,他需要一次TCP的三次握手建連。
第二個是你如何訪問到http的伺服器?如果你之前是通過DNS排程過去的,實際上前邊的那個DNS也是省不了,在國內是沒有辦法做anycast的,也就是沒有辦法來直接訪問一個眾所周知的大的IP來進行,所以,一般情況下都是通過DNS來進行第一次排程,然後用http來進行第二次糾偏。這種情況下大家可以想象,如果你下載一個大檔案,比如說電影,但你訪問的是一個頁面小元素,比如說這個圖片只有幾k,那麼,實際上你排程的時間就已佔用了很大的成分。實際上,這種302排程是一種磨刀不誤砍柴工的方案,如果你後面有很多工作要做,比如要下載一個電影時間會很長,那你排程準確,即使花一點時間排程也是值得的。但是如果你後續訪問一下就完了,那麼你這樣排程就沒有太大意義。
除了DNS排程和http的302排程以外,其實還有一種排程方式,叫http DNS排程,它的原理是通過一個正常的http請求,發一個get的請求,然後再請求裡面以引數的形式攜帶一個我要解析的域名,然後伺服器那邊去通過資料庫查詢,查詢之後又通過http的正常響應,把這個你要請求的IP通過http協議給你,這種協議有一個特點就是必須雙端都支援,因為這種模式是非標準的。沒有任何一個RFC文件說,你的客戶端或者你的作業系統天生就支援這種機制。這有點類似是一種API的這種方式,那如果要實現的話就必須雙端都支援。
一般,第三種排程的應用場景是在手機的APP端,在APP軟體裡面,你要訪問某些東西很有可能被運營商劫持等問題,這個劫持問題後面還有很大的篇幅去講。那為了避免這種劫持,可能會用到這種http DNS的排程方式。既然APP的程式都是你自己寫的,所以說實現這麼簡單一個API的藉口是很容易的。
CDN的接入

可能會有人問,你講了這麼多DNS和具體CDN的排程有什麼關係呢?
因為在講你獲得一個具體的DNS域名地址的時,他給你的就是一個IP地址。那在沒有CDN之前,他給你的IP地址就是在原來沒做CDN時的原始伺服器地址。但如果你做過CDN的話,你會發現最終拿到的這個IP地址是CDN的節點,而並不是真正的原始伺服器。
我們通常說的拿到一個IP地址,這實際上是DNS的A記錄。DNS裡面有很多不同的記錄,比如像A記錄負責給你一個IP地址;比如像CNAME記錄給你的是一個域名的別名。當然還有很多其他記錄,比如TXT的記錄、MX記錄等等。這個跟CDN無關,這裡就不細說了,有興趣去查一下DNS相關的文件。
上圖就是一個很明顯的CDN介入後的效果圖。linux裡有一個命令叫dig,它可直接把要訪問域名的具體的解析情況列出來。那麼,通過這個圖可看出,當你要訪問www.163.com時,他最終雖給出的是一個IP地址,但實際上,它經過了兩次CNAME記錄。第一次CNAEM記錄就是我們之前說得CDN的GRB,他拿到了這個資料,就可以間接知道你的這個LOCODNS是從哪裡來的,然後間接給你進行一個定位。以這個圖為例,他實際上第一跳是跳到網速地址,第二跳是分配了網速的一個平臺,這個平臺又分開其他的IP給最終的客戶。
Cache系統——快取系統

除DNS排程以外,在CDN裡還有一個非常大的重頭戲就是Cache系統,也就是快取系統。它用於把那些可以快取住的東西,快取到CDN的邊緣節點,這樣當第二個人去訪問同一節點,同一具體電影或MP3時就不用再經過CDN鏈路回到真正的源站去拿資料,而是由邊緣節點直接給資料。
在Cache系統裡囊括了很多的技術,比如,用空間換時間的這種高效的資料結構和演算法,多級快取以熱度來區分,前端是SSD後面是機械硬碟等等。很多的細節就不說了,如感興趣的可之後交流。
對於Cache系統來說,有兩種不同的工作狀態。第一種工作狀態就是所謂的命中(hit),第二種就是沒有命中(miss)。如果命中了,直接通過檢索找到磁碟或記憶體上的資料,把這個資料直接吐給客戶,而不是從後面去拿資料。這樣的話就起到一個很完美的加速效果。
第二種是在miss時,其實,miss的時候跟hit唯一的區別就是,當我發現我的本機上沒有這個資源,我會去我的upstream(上游)去拿資料。拿完這個資料,除了第一時間給客戶,同時還會在硬碟上快取一份。如果這個硬碟空間滿了,會通過一系列置換方法,把最老的資料、最冷的資料替換出去。
提到了upstream,不是原始伺服器,原因是因為當客戶訪問到CDN節點的時,他發現上面沒有資料,並不是直接從原始伺服器上去拿,而是經過他的另一個CDN節點,然後通過middlemell的方式去進行一些資料傳輸。然後upstream這一層,從原始伺服器拿資料,通過一系列的加速手段,快速的把資料投遞給我們的邊緣節點,再把這個資料給最終客戶。在過程當中upstream和downstream這兩層都會把資料快取一份。通過這種樹形結構,比如說多個邊緣節點,然後彙總到一個或者幾個副層結點,這樣的話可以逐漸的實現流量的收斂。
提到Cache的具體技術,我相信這裡的很多朋友都是同行業的,有人會說其實這沒有什麼難的,你只要有網路、有運維人員就可以了。其實我並不這樣認為,因為你如果想把它做好的話其實很難,比如,我列出的很多技術你有沒有在考慮?
舉幾個例子來說,你有沒有做網絡卡的的多佇列和CPU的親和性繫結?你有沒有做磁碟的排程演算法改進?另外,你儲存的時候還是用還是?等等都是有講究的。包括核心的調優包括架構和CPU的繫結,CPU的多級快取的使用,然後你的處理你使用,還是用標準的的這種機制。再比如說編譯的程式時使用的去編譯還是用英特爾的,然後你再做很多的呼叫。比如說一個很簡單的字串拷貝,那你是用,你還是用匯編去寫,你還是用什麼方式等等很多細節。
關於高效能這一塊,還有很多的研究,如大家感興趣的話,可以之後跟我進行進一步的溝通。我想表達的一個觀點就是說,看上去做CDN很簡單,入門確實也簡單,但是要真正想做好很難。
安全問題
在沒有做CDN之前你的網站很有可能會遭受到各種各樣的攻擊。那麼攻擊一般分成兩種,第一種叫蠻力型攻擊,量大的讓你的頻寬無法抗住最後導致拒絕服務,另外一種是技巧性攻擊。
作為CDN來講,就已經將你的原始伺服器的IP進行了隱藏。這樣當一個攻擊者去訪問你的域名的時,實際上訪問的並不是你真正的伺服器。當他訪問的是CDN的節點,就沒有辦法把CDN的節點打倒,換句話說,即使有能力把CDN的比如10g的節點或者是40g的大節點全部打倒,但由於CDN天然的分散式的部署方式,他也很難在同一時間之內迅速的把全國所有CDN的邊緣節點全都打癱。
另外,還有一種攻擊是針對你的DNS地址的。如果你的GRB癱了的話,會導致整個排程系統失靈。如果調動系統失靈,即使你的CDN的Cache server還是能夠正常接受請求,但由於流量排程不了。因此,你需要在DNS層做很多防護機制,比如說用高效能的DNS或用分散式的部署方式等等。
技巧型攻擊不需要很大的流量,就可以把你的原針打倒或是讓你的網頁出現錯誤的情況。比如說,像注入、掛馬甚至說更嚴重的會直接拖走你的資料庫等等。那麼作為CDN來說,有很多廠商實際上已經開始具備這樣的技巧性的防護能力了,比如說WAF(Web Application Fierwall),就是應用層防火牆,他可以直接去解析你的請求內容,分析內容是否有惡意性,如有惡意性的話去進行過濾,報警等一系列措施來保證你的原始伺服器的安全。
第二部分主要是針對網路層的優化、架構的優化、Cache的選型還有效能分析等等幾個方面,對整個CDN的基礎原理作很深入地剖析。
原始的CDN其實是Content Delivery Network這三個詞的縮寫,也就是內容分發網路。但我認為應該是can do something on Network。CDN的理念是加速,所以,我們就盡一切可能去做各種優化,從一層到七層的優化來實現最終的優化效果。
為什麼說一層是優化,實際上也是硬體,你的伺服器選型就是一種優化。你是用ssd,還是用saker硬碟,你是該用pce卡,還是應該用ssd。你的CPU應該用至強還是應該用阿童木的等等,都是需要去斟酌。
至於二層,鏈路層的優化指的就是資源方面。比如機房如何去選擇。
三層路由層是指你在middlemell這塊真正選路的具體的細節,後面會有一個圖來具體講一下。
四層是指傳輸層的優化,我們一般的業務全都是TCP,所以說這裡面就可以明確的說這裡是指TCP的優化。還有一個就是七層也是可以優化的。比如說你強行對內容進行壓縮,甚至你改變壓縮級別去壓縮。
作為CDN來說,基本上我羅列了一下可能會用到的一些技術,大概10個。比如說就近分佈、策略性的快取、傳輸的優化、鏈路層的優化、包括內容的預取、合併回源。然後持久連線池、主動壓縮,還有當你原始伺服器掛了的話你怎麼樣能夠保證讓客戶看到資料等很多的細節。
路徑的優化,實際上,我們可以把它抽象成是一個求最短路徑最優解的思路去解決真實的問題。當你從a點到b點需要傳輸資料的時,往往會經過一個c點,比直接從a到b更快。在網際網路裡有個三角原理,和地理位置的原理有一定區別的。雖說有一定的相關性,但還是有區別的,有可能從a經過c到b會比a直接到b更快。
在資料傳輸的時,需要去考慮很多綜合因素,目前為止,包括阿克麥也很難做到完全系統自動化去做鏈路選擇和切換。在排程的時,很多公司都有專門的團隊管流量排程的。很多的系統可能只起到支撐和參考的作用,而真正需要決策的還是人。因為你需要考慮的元素太多了,比如說要考慮你的頻寬成本、頻寬節點冗餘量、伺服器承載能力,要考慮你的客戶敏感度哪些該切哪些不該切等很多細節。
傳輸層的優化剛才講到了是TCP優化,在現今的互聯網裡,TCP優化是可以帶來最直接客戶體驗感的一種實現方式。