摘要: 本文做者:白金,《CDN 之我見》是一個系列文章,共由三個篇章組成,分爲「原理篇」、「詳解篇」和「隕坑篇」。本篇章屬於「詳解篇」的第一部分:網絡優化。詳解篇適合那些接觸過 CDN,對 CDN 多少有些瞭解,但不少知識點知其然而不知其因此然,想深刻了解學習的同窗。算法
本文做者:白金
上篇回顧:《CDN 之我見》系列二:原理篇(緩存、安全)https://yq.aliyun.com/article...緩存
《CDN 之我見》是一個系列文章,共由三個篇章組成,分爲「原理篇」、「詳解篇」和「隕坑篇」。本篇章屬於「詳解篇」的第一部分:網絡優化。詳解篇適合那些接觸過 CDN,對 CDN 多少有些瞭解,但不少知識點知其然而不知其因此然,想深刻了解學習的同窗。安全
衆所周知,當前是互聯網時代,不管大大小小的公司,不管是互聯網企業仍是傳統企業,通通離不開一個「網」字,而相比之下,硬件服務器、操做系統、應用組件等,在沒有特殊必要需求的話(若是硬件不壞、軟件或系統無急需修復的 BUG、無急需實現的客戶需求),這些都是不用去主動改變。服務器
相比之下網絡則不一樣,最頻繁變化的,最不想變化而又最無奈被動跟隨變化的,就是網絡質量,而 CDN 的縮寫也是 Content Delivery Network,所以在詳解篇我會把重點放在網絡上。其它的部分相對都好把控,但網絡問題存在必定的隨機性和不可預測性。若是咱們能夠駕馭網絡,相信在這個互聯互通的互聯網時代,咱們將變得如虎添翼、叱吒風雲!網絡
爲此,這裏提出我對 CDN 的另外一個解讀:CDN - Can Do sth. on Network(咱們能夠在網絡層面搞些事情)併發
提到優化,其實從 OSI 七層模型來說(準確說是 TCP/IP 模型,兩者是不一樣的,具體能夠 google 一下),從物理層到應用層其實都是能夠優化的,優化手段各異。ssh
L1 物理層:硬件優化(升級硬件設備,承載更多業務)
L2 鏈路層:資源好壞(尋找更快的網絡節點、確保 Lastmile 儘可能短)
L3 路由層:路徑優化(尋找 A 到 B 的最短路徑)
L4 傳輸層:協議棧優化(TCP Optimization)
L7 應用層:能作的事情太多了(主要面向 HTTP 面向業務)tcp
針對 CDN 而言,經常使用的是 L4 和 L7 的優化,例如上圖所述。分佈式
分佈式就近部署:確保網民訪問到的 Cache Server 離他是最近的。
策略性緩存:針對明確已知的圖片、CSS、JS 等,若源站更新並不快,但卻沒有明確高值緩存多長時間,CDN Cache Server 能夠自定義一個緩存時間(例如 60s),這樣能夠確保在 60s 以內的一樣請求會快速給出數據而不用穿越整個 Internet 從源站獲取。
傳輸路徑優化:尋找從邊緣 CDN Cache Server 經 upper CDN Cache Server 到源站的最優傳輸路徑,確保動態傳輸的數據能夠走端到端的最優路徑。
鏈接加速:經過修改協議棧的 Handshake Timer 來實現快速重試,以彌補因爲丟包致使的重試超時。
傳輸層優化:這裏主要是指 TCP 協議,針對 TCP 協議能夠作不少優化策略,因爲篇幅問題後面再講。
內容預取:解析 WEB 內容,對於裏面的 Object,在網民請求以前,優先由 CDN Cache Server 主動獲取並緩存下來,以縮短數據交互時間,提高網民體驗感。
合併回源:當有多我的前後下載同一個還未緩存住的內容時(例如一個 mp4 視頻文件),CDN Cache Server 作到合併鏈接從 upstream 拿數據,而不是幾個請求,幾個 to uptream connection,既作到了帶寬成本優化,又作到了給 upstream 降載,後請求的人還能享受以前 CDN Cache Server 已經下載過的部分文件,這部份內容直接 HIT,只有當追上第一個 downloader 的時候才一塊兒等待 MISS 數據。
持久鏈接池:在 Middlemile 之間預先創建好 TCP Connection,並一直保持不斷開,當網民有新請求過來時,邊緣 CDN Cache Server 直接藉助與 upper 創建好鏈接,直接發送 HTTP 的 GET/POST 報文到 upper CDN Cache Server,進行 TCP 「去握手化」,減小因爲 TCP 鏈接創建而形成的時間損耗(多適用於高併發小文件請求)。
主動壓縮:不少源站因爲規劃設計問題或擔憂負載太高問題,頁面中的 HTML、CSS、JS 文件(這種文件具備高度可壓縮性)並未壓縮傳輸,此時 CDN Cache Server 能夠主動對其進行壓縮後傳輸並緩存,以減小傳輸量、下降交互時間、提高用戶體驗感。
Offline:當源站掛了怎麼辦?網民訪問時,會拿不到數據。那麼 CDN 此時能夠策略性發送最新緩存的一份舊數據給網民,而不是生硬的告知用戶不可訪問,以提高用戶體驗感。
總之,優化策略很是多,下面將抽取其中最具通用性的網絡部分進行詳細闡述。高併發
如前文所述,所謂路徑優化就是找到兩點間的最優路徑。
對於網絡而言,A 到 B 最快 ≠ A 距離 B 最近,從廣東聯通訪問福建聯通,可能不如廣東聯通經北京聯通再到福建聯通更快,所以要對節點作實時探測,計算最優路徑。
計算最優路徑時,還要考慮帶寬飽和度、成本、客戶敏感度問題綜合計算,所以不是看上去那麼簡單的。
帶寬飽和度:做爲中轉節點(例如上例所說的北京),若是帶寬自己已經沒有多少剩餘,那麼穿越北京的路徑優化可能會做爲壓死大象的最後一根稻草,使本來還 OK 的北京節點變得不堪重負。
成本:還以北京爲例,北京資源的帶寬成本確定遠高於其它省市,例如比河北聯通、天津聯通可能要貴不少,但可能只比河北天津慢幾個毫秒(運動員起跑時最快的反應時間是 150 毫秒),那麼爲了這幾毫秒要多支付不少帶寬費用顯然是不值當的,那麼利用北京進行中轉顯然就是不值得的(固然,有的時候就是爲了和對手 PK,那也沒辦法)。
客戶敏感度:有了中轉路徑,提速效果固然是好的,但如前文所述也是有代價的,那麼是全部業務流量都走最優路徑呢?仍是隻讓個別業務走最優路徑呢?這個就要看客戶敏感度了。例如重點大客戶,例如對質量要求較高的高價優質客戶,這些客戶可能就是首選。
如前文所述,所謂傳輸層優化主要是指 TCP 優化,這是全部互聯網行業的通用技術,是重中之重的領域,TCP 優化若是作的好,可彌補節點質量低下而形成的響應時間過大的損失。
賽馬比賽時,有好馬固然跑的快。若是馬通常(不是太差),騎手的騎術精湛,或許一樣也能夠得第一,就是這個道理!
另一點 TCP 優化重要的緣由在於,TCP 是互聯網尤爲是 CDN 的基礎協議,基本上全部業務都是 over TCP 來進行傳輸的,若是將 TCP 優化作好,受益面很是廣,能夠說全局收益(不只是提高客戶體驗感,也能提高內部支撐系統的使用體驗感,例如日誌傳輸、配置下發等)。
談到 TCP 優化,須要先將 TCP 協議基礎知識。須要首先明確一些名詞屬於的概念。
CWND:Congestion Window,擁塞窗口,負責控制單位時間內,數據發送端的報文發送量。TCP 協議規定,一個 RTT(Round-Trip Time,往返時延,你們常說的 ping 值)時間內,數據發送端只能發送 CWND 個數據包(注意不是字節數)。TCP 協議利用 CWND/RTT 來控制速度。
SS:Slow Start,慢啓動階段。TCP 剛開始傳輸的時候,速度是慢慢漲起來的,除非遇到丟包,不然速度會一直指數性增加(標準 TCP 協議的擁塞控制算法,例如 cubic 就是如此。不少其它擁塞控制算法或其它廠商可能修改過慢啓動增加特性,未必符合指數特性)。
CA:Congestion Avoid,擁塞避免階段。當 TCP 數據發送方感知到有丟包後,會下降 CWND,此時速度會降低,CWND 再次增加時,再也不像 SS 那樣指數增,而是線性增(同理,標準 TCP 協議的擁塞控制算法,例如 cubic 是這樣,不少其它擁塞控制算法或其它廠商可能修改過慢啓動增加特性,未必符合這個特性)。
ssthresh:Slow Start Threshold,慢啓動閾值。當數據發送方感知到丟包時,會記錄此時的 CWND,並計算合理的 ssthresh 值(ssthresh <= 丟包時的 CWND),當 CWND 從新由小至大增加,直到 sshtresh 時,再也不 SS 而是 CA。但由於數據確認超時(數據發送端始終收不到對端的接收確認報文),發送端會驟降 CWND 到最初始的狀態。
瞭解了 TCP 的一些名詞屬於,其實大體也就瞭解了 TCP 的運做機制,可是有幾點要注意。
1.不一樣的操做系統、內核版本,initcwnd(初始 CWND)不一樣
以 Linux 爲例,kernel-2.6.18 的 initcwnd 是 2,而 kernel-2.6.32 變成了 10,經過 iproute 軟件包中的 ip 命令能夠修改 Linux 的 CWND 和 RWND。
具體能夠參考一篇 Google 寫的 Paper,是他們提出了原始 initcwnd = 2 過小了,須要擴大的理念。
原文請參考:
https://developers.google.com...
2.單位時間內(一個 RTT)發送量不是 CWND,而是 min(CWND, RWND)
除了數據發送端有個 CWND 之外,數據接收端還有個 RWND(Receive Window,接收窗口),決定還能接收多少數據,並經過接收確認報文顯性告知數據發送端,發送端要參考 CWND 和 RWND 信息決定最終發送的數據量(packet 個數)。管道中究竟能放多少數據,取決於 CWND 和 RWND。
另外一個問題:TCP 怎麼了?TCP 有什麼問題嗎?
若是能問出這個問題,證實同窗們的關注點是正確的。
TCP 是在上個世紀六七十年代設計的,當時面向的是短距離傳輸、窄帶(可能仍是半雙工通訊)的鏈路環境。鏈路自己不太可能丟包,丟包基本都是由於鏈路擁塞形成的。根據早起的 TCP 擁塞控制算法,丟包 -> 降速,再丟 -> 再降,算法自己的目的是但願經過降速來規避擁塞,進而規避丟包狀況。
這個算法自己的理念是正確的,可是隨着時代的發展,有了 4G,有了 WiFi,有了長距傳輸,有了策略性丟包邏輯,致使 TCP 傳輸過程當中,丟包不必定就是擁塞形成的,若是按照傳統的「丟包就降速」策略來考慮問題,可能不但不能緩解問題,甚至可能會致使問題更加惡化。
舉個例子來講,在一個平均隨機丟包 50% 的鏈路上(平均發出去的包,每 2 個就必然丟 1 個),在這種環境下,發現丟包了,降速發送有用嗎?答案毋庸置疑,降速只會讓對端收到的有效數據更少。這種環境如何優化呢?很簡單,每一個包發 2 遍不就能夠了?這樣對端就會 100% 收到數據了,但代價就是發送端的出口流量是以前的 2 倍。
固然,真正在作 TCP 優化時不是這麼簡單的,要考慮不少細節,例如如何區分丟包緣由,例如該如何控制 CWND,例如如何更早的發現接收端沒收到數據,例如當對端無響應時如何快速感知等等等等……
TCP 優化其實是在用帶寬換用戶體驗感,低成本低質量網絡雖然能夠經過 TCP 優化提高體驗感,但綜合成本甚至可能比直接採購優質高價節點更高,效果還未必比優質節點直接服務好。
TCP 之因此叫優化不叫加速,是由於它可讓那些本來應當傳輸很快,因爲算法不合理致使的傳輸變慢的傳輸變得快起來,但卻不能讓那些鏈路質量本來沒有問題的變得更快。
除此以外還有一個優化點,就是 TCP Pacing。
前文咱們已經講過,TCP 是經過 CWND/RTT 來控制傳輸速率的,在一個 RTT 中,最多隻能發 min(CWND, RWND) 這麼多數據。可是 TCP 協議棧在發送數據時,是在 RTT 週期之初一股腦將數據發送出去的,這樣宏觀看沒有任何問題,但若是微觀看,數據發送波形就像上圖那樣,一個又一個的凸起。雖然在 RTT 單位時間內發送量恆定,但某微觀時間線上的發送速率確實超級猛烈的,這樣有可能形成瞬間鏈路擁塞(尤爲是窄帶線路)。
原文請參考:https://homes.cs.washington.e...
經過 TCP 三次握手建連能夠測得 RTT,在已知 CWND 的狀況下,經過控制發包的時間間隔能夠實現 Pacing 效果,使數據包在圍觀看是均衡發送充滿整個 RTT 空間的效果,這樣能夠避免瞬時擁塞,對窄帶鏈路後須要勻速恆定傳輸的業務很是有效。
除了 Pacing 之外,還有不少不一樣的優化算法或策略:
建連優化:TCP 在創建鏈接時,若是丟包,會進入重試,重試時間是 1s、2s、4s、8s 的指數遞增間隔,縮短定時器可讓 TCP 在丟包環境建連時間更快,很是適用於高併發短鏈接的業務場景。
首包優化:此優化其實沒什麼實質意義,若要說必定會有意義的話,可能就是知足一些評測標準的須要吧,例若有些客戶以首包時間做爲性能評判的一個依據。所謂首包時間,簡單解釋就是從 HTTP Client 發出 GET 請求開始計時,到收到 HTTP 響應的時間。爲此,Server 端能夠經過 TCP_NODELAY 讓服務器先吐出 HTTP 頭,再吐出實際內容(分包發送,本來是粘到一塊兒的),來進行提速和優化。聽說更有甚者先讓服務器無條件返回 "HTTP/" 這幾個字符,而後再去 upstream 拿數據。這種作法在真實場景中沒有任何幫助,只能欺騙一下探測者罷了,所以還沒見過有直接發 "HTTP/" 的,實際上是一種做弊行爲。
平滑發包:如前文所述,在 RTT 內均勻發包,規避微分時間內的流量突發,儘可能避免瞬間擁塞,此處再也不贅述。
丟包預判:有些網絡的丟包是有規律性的,例如每隔一段時間出現一次丟包,例如每次丟包都連續丟幾個等,若是程序能自動發現這個規律(有些不明顯),就能夠針對性提早多發數據,減小重傳時間、提升有效發包率。
RTO 探測:如前文講 TCP 基礎時說過的,若始終收不到 ACK 報文,則須要觸發 RTO 定時器。RTO 定時器通常都時間很是長,會浪費不少等待時間,並且一旦 RTO,CWND 就會驟降(標準 TCP),所以利用 Probe 提早與 RTO 去試探,能夠規避因爲 ACK 報文丟失而致使的速度降低問題。
帶寬評估:經過單位時間內收到的 ACK 或 SACK 信息能夠得知客戶端有效接收速率,經過這個速率能夠更合理的控制發包速度。
帶寬爭搶:有些場景(例如合租)是你們互相擠佔帶寬的,假如你和室友各 1Mbps 的速度看電影,會把 2Mbps 出口佔滿,而若是一共有 3 我的看,則沒人只能分到 1/3。若此時你的流量流量達到 2Mbps,而他倆還都是 1Mbps,則你至少仍能夠分到 2/(2+1+1) * 2Mbps = 1Mbps 的 50% 的帶寬,甚至更多,代價就是服務器側的出口流量加大,增長成本。(TCP 優化的本質就是用帶寬換用戶體驗感)
鏈路質量記憶:若是一個 Client IP 或一個 C 段 Network,若已經得知了網絡質量規律(例如 CWND 多大合適,丟包規律是怎樣的等),就能夠在下次鏈接時,優先使用歷史經驗值,取消慢啓動環節直接進入告訴發包狀態,以提高客戶端接收數據速率。
以前講的 TCP 優化都是須要去修改代碼的,這裏有一個不用修改代碼的方法,僅修改參數就能夠。
內核協議棧參數 net.ipv4.tcp_slow_start_after_idle 默認是開啓的,這個參數的用途,是爲了規避 CWND 無休止增加,所以在鏈接不斷開,但一段時間不傳輸數據的話,就將 CWND 收斂到 initcwnd,kernel-2.6.32 是 10,kernel-2.6.18 是 2。所以在 HTTP Connection: keep-alive 的環境下,若連續兩個 GET 請求之間存在必定時間間隔,則此時服務器端會下降 CWND 到初始值,當 Client 再次發起 GET 後,服務器會從新進入慢啓動流程。
這種友善的保護機制,對於 CDN 來講是幫倒忙,所以咱們能夠經過命令將此功能關閉,以提升 HTTP Connection: keep-alive 環境下的用戶體驗感。
# sysctl net.ipv4.tcp_slow_start_after_idle=0