加快網絡速度-TCP優化

如今, 時間就是金錢. 不像之前瀏覽一個網頁就是一個奢侈品. 現在, 網速愈來愈快, 下個2GB的東西, 1分鐘就行了.
那, 我如今網速很慢,應該怎麼提升的個人網速呢?
提高網速的不二法門就是... 買寬帶~
233333~ 固然,這只是, 給用戶的建議。 對於, 咱們程序員來講, 花錢就是最痛苦的事. 這裏, 咱們須要用咱們本身的雙手去提高網速.
這裏,咱們主要看看,在TCP這邊怎麼提高網絡速度.html

常見的TCP 延時事務

TCP是網絡通訊很重要的一個協議,程序員的基本功也就在這, 最出名的應該算是TCP的3次握手了. 不過,這裏3次握手不是我要闡述的, 有興趣的同窗,能夠看看TCP3次握手. 那還有其餘能形成TCP時延的事務嗎?
固然有啊. 常見可以形成的事務有如下幾種.node

  • 延遲確認linux

  • nagle算法程序員

  • slow-start算法

  • 端口耗盡數據庫

延遲確認

當你在進行網絡數據傳輸, 成功發送數據包時, 服務器會給你返回一個ACK進行確認。 可是爲了防止,網絡的堵塞,一般,服務器在接收該數據時,會對ACK進行延時, 若是在必定時間內(一般爲200ms), 有另外的數據來源時, 則會將2次ACK包一塊兒發送,減小寬帶.
以下圖所示:

總結一句話:segmentfault

ACK every second packet, or a single packet after the Delayed ACK timer expireswindows

即,有兩個包當即到來當即發送ACK, 沒有的話,等一會,實在沒有則將該次ACK 單獨發送.緩存

nagle算法延時

這是nagle 建立的一個算法,和延遲確認同樣也是用來解決網絡堵塞問題. 不過,他針對的是Sender一端. 衆所周知的TCP的大小有40 bytes. 若是你的數據包內容才1B的話, 這樣傳輸的價值就很是小了.因此,聰明的nagle想了想,這樣不行,得讓小數據包在緩存裏面待一段時間,等數據多了再一塊兒發送,若是是在沒有其餘數據了, 超過nagle算法設置的時延後,那就只能單獨發送了.安全

nagle 和 確認延遲共同做用

假設如今有這樣一個場景, 有兩個包, 一個已經發送,另一個因爲尺寸過小, 被放在緩存當中. 此時,你的nagle算法是開啓的, 那麼此時,你須要等到前面一個包被確認以後才能發送.那麼這樣算起來,在這個特殊狀況下,你偶數包的延遲時間是=== nagle+確認延遲. 雖然,這種狀況很特殊,可是在高併發的狀況下,任何東西都是有可能出現的.

nagle的缺陷

因爲nagel主要是針對小數據包, 因此對於小包的影響很大. 好比: 你使用HTTP只是發送GET請求,要求數據庫進行相關的取操做,而返回的數據很小. 那麼,這時候nagle會讓你欲哭無淚~

解決缺陷

針對於確認時延, 這裏因爲是系統本身設置的.
在windows 裏,確認延遲通常爲200ms.(如今誰還用windows)
而在*nx裏,延時被調整到15-40ms.
不過在linux裏面可使用tcp_delack_min進行修改. 不過對於15-40ms, 俺認爲這個應該沒有太大的影響. 不必大動干戈進行改動。 不過,對於nagle算法延時,這個問題就比較嚴肅了. 在nodeJS裏面咱們通常使用socket.setNoDelay([noDelay])來進行設置.

slow start爲什麼物

slow-start 是爲了解決網絡延遲而被設計出來的一個算法. 用來逐步增長數據包的發送量,直到發送端和接收端可以承載的最大值爲止.
在正式介紹slow-start 工做流程以前,咱們須要掌握幾個基本的概念。

TCP 裏面的window

window是TCP報文裏面的一部分. 咱們看一下TCP協議內容.
此處輸入圖片的描述
就是裏面的window. 他主要就是用來描述 鏈接可以承受的包的大小, 由於一旦你的包過大,會形成包的廢棄,而致使重發。window 就是起到提升傳輸效率的做用.
那window 是怎麼肯定的呢? 首先咱們須要明白, 每一方的 congestion window 的最大值,都只有本身知道,那要猜想到對方的最大值話,就只能一步一步的逼近. 但第一次的值應該怎麼設置呢? 其實這是有標準的.
首先,咱們須要瞭解另一個名詞--MSS][4. 他是congestion window 的基礎值. MSS 一般爲1460B(1500-20 for IP - 20 for TCP). 而後,congest control 定義了一套公式:

If SMSS > 2190 bytes:

IW = 2 * SMSS bytes and MUST NOT be more than 2 segments
If (SMSS > 1095 bytes) and (SMSS <= 2190 bytes):
IW = 3 * SMSS bytes and MUST NOT be more than 3 segments
If SMSS <= 1095 bytes:
IW = 4 * SMSS bytes and MUST NOT be more than 4 segments

因此由上面算下來, window = MSS * 3 = 4380B. 這就是sender's congestion window 的初始值. 這裏的3還有另一個名詞--initcwnd(下文會有介紹).
ok~ 基本概念有了, 那 how does slow-start work?

slow-start 是怎麼工做的?

  • 在首次發送信息時, Sender 會初始化一個包, 而且該包裏面包含了一個比較小的 堵塞窗口(其實就是代表窗口的容載量--bytes). 他的大小是由 MSS 決定的.好比,咱們的MSS爲1460B, 那麼咱們初始化的congeston window就能夠爲2920B. 即,至關於發送了2個小分組. (不理解,見window size)

  • 接受者接受到包以後, 會返回一個ACK包,並附上 receiver's window size(一般,會比sender發送過來的大). 若是, receiver 沒有響應, 那麼sender 就知道本身 的包太大了, 而後會自行改小 而後再發送.

  • sender接受到receiver 的 ACK後,此時就在上一次發送的基礎上額外加上一倍的MSS. 即, 上一次,我發送了2個MSS大小的包, 那麼此時,會接受到兩次ACK確認,接收到以後,我就能夠增大window size, 向receiver額外再發送一倍---4個MSS大小(2920 + 1460 + 1460). 如此往復,直到兩邊的window size 到達上限以後,那麼slow start 就已經完成了.

其實,上面的傳輸過程,咱們可使用一個圖表來表示. 這是,在下載一個文件時抓包時的圖.(抓包工具你用什麼均可以,不過最經常使用的是fiddle和wireShark)

x-axis 是 時間, y-axis 是 發送的序列號(用來表示每一個包是整個資源的哪一部分). 注意!!! 裏面的一個點 就是一個包. 咱們能夠數一數, 第一個有2個dot, 第二個有4個dot, 第三個有8個dot... 後面確定會有一個上限值, 那麼,此時就已經達到最大傳輸速度了. ok~ slow-start要作的工做就已經完成了。 那麼該次的Connection 應該算是最優connection, 因此對於已經創建好的connection實現重用也是TCP優化很重要的一部分.

slow start 好處

  • 減小鏈接斷開次數: 由於包不會因爲網絡堵塞而丟失

  • 用戶可以享受更快的下載速度,由於此時slow-start已經找到了最大的鏈接速度了

  • 下降網絡堵塞狀況

可是,好處就這幾個,可是對於大量鏈接須要創建時, slow-start 對其影響 就比較呵呵了。 那應該若是優化slow-start呢?

解決slow-start時延

上面提到過,congestion control 有一個計算機制, 用來肯定初始化時的包的傳輸量.

If SMSS > 2190 bytes:

IW = 2 * SMSS bytes and MUST NOT be more than 2 segments
If (SMSS > 1095 bytes) and (SMSS <= 2190 bytes):
IW = 3 * SMSS bytes and MUST NOT be more than 3 segments
If SMSS <= 1095 bytes:
IW = 4 * SMSS bytes and MUST NOT be more than 4 segments

上面的數字,咱們就能夠稱做爲initcwnd(the initial congestion window parameter). 他用來規定你的sender 在初始化時,發送數據包的數量. 因爲一般規定MSS 通常爲1460B, 因此,initcwnd 的數量通常規定爲2.(由於,要比上限值小才行, 防止丟包)
可是,這個只是對於很老的機器來講, 如今你們的筆記本基本上都是00後, 配置已經遠遠超過之前的臺式了. 因此,通常而言,receiver window size 應該不會很低。 下圖是操做系統和對應的window size的最大值.

就算是XP 他也能夠接受 65 535B 大小的包. 若是你sender發送的仍是 2920B 的話,這就有點 太慢了. 因此,在配置服務器的時候(特別是*nx), 咱們須要對其initcwnd的值,更改一下. 減小他slow-start的時間.
在linux 下, 咱們可使用:

  • ip route show; //查看電腦設置的initcwnd初始值

  • sudo ip route change default via 192.168.1.1 dev eth0 initcwnd 10 //將initcwnd 設置爲10

  • ip route show; //檢查initcwnd 是否爲10

在windows 2008 Server 下的更改請參靠: initcwnd

提高了initcwnd,有什麼用?

那這樣作到底有什麼效果呢?咱們看圖說話:

能夠看到,隨着initcwnd的提升, 下載的時間逐漸變慢. 可是,initcwnd 也不能無限制提升, 能夠看到從10到20, 時間就基本上沒什麼變化了. 因此,通常推薦提高到10就能夠了.

另外一方面,initcwnd對於CDN 的優化,也是很是重要的。 要知道CDN 原本就是以快著名. 咱們先來看看CDN 是如何工做的.

HOW CDN works

CDN憑藉他特有的機制,高速通道,CDN Cache,域名分片等特性. 成爲了雲平臺上必不可少的一個特性.
但,CDN究竟是怎麼工做的呢?
CDN是雲平臺的產物,因此他必須依賴的就是衆多的服務器. 一般來講, CDN 有不少 Points of Presence (PoPs) 分佈在全國或者說全世界各地,用來加速文件傳輸的.
若是,咱們的文件沒有放在CDN上,那麼,他的傳輸過程是怎樣的呢?

像這裏,從Russia 到 America中間通過無數的節點進行傳輸,可想而知,隨隨便便丟個包,那麼, 你傳輸就得重頭再來~ 形成的網絡堵塞我就不說了,關鍵用戶等不了~ 對於這種狀況,考慮直接上CDN。 咱們來看一下,帶上CDN的時候.

當你的文件放在CDN上時, 用戶首次使用該文件時, 這時候最近的CDN Server 會向 Origin Server 請求文件,而後該文件就會保存在該CDN Server 以備用戶下一次讀取. 因此, 通常而言,第一個吃螃蟹的人,貢獻是最大的.
另外,若是你請求的資源是動態生成的,那麼CDN Cache你的Content也是沒有什麼卵用的. 因此CDN 還有另一個機制-- Super Highway. 不一樣於前面的without CDN 時候的傳輸方式, 通過獨立的ISP, 用戶等到花兒都謝了資源纔到, 這裏, CDN 會創建一個高速通道, 將動態資源高速傳遞.
此處輸入圖片的描述
雲平臺內部會自建算法,計算兩點位置傳輸的最短路徑, 而後找到對應服務器進行傳輸. 因此,遠距離傳輸也是CDN的一大亮點。 不過因爲距離長,穩定性和安全性的要求也會增長. CDN服務器就必須保證 本身 自己可以過濾掉一些DDOS attackers 以及 可以承受部分的DDOS攻擊.

ok~ CDN 基本內容算是說完了, 經過上面的介紹,爲了達到'高速'這個蜜汁weapon. 提高initcwnd 也是必不可少的一部分。 國外的CDN,一般會到10+. 這是linux kernel 內置的(3.0版本以上). 能夠說, 你的initcwnd 越高, 對機器的性能要求也越高。 因此,對於本身的雲平臺有蜜汁自信的,他的initcwnd確定會高。 這裏,我放一份,國外的CDN 服務商的 initcwnd數.

能夠看到. Cachefy 像是開掛似的, initcwnd數都到70了. 不過,initcwnd只是做爲一個參考,服務商性能的優劣並不只僅只有initcwnd這一個參數.

端口耗盡和TIME_WAIT

還記得4次揮手的時候,發生的故事麼?
在雙方發送一次FIN包以後,還須要通過TIME_WAIT的2MSL時延以後,才能夠正式宣告結束.(MSL是報文在網絡中存活的最大時間- Maximum Segment Lifetime)具體的4次回收流程以下:

只有當TIME_WAIT正式結束以後, 端口的利用纔有可能被釋放. 因此,TIME_WAIT的2MSL 也可算是一個時延.
這裏,咱們須要瞭解一下,在TCP通訊中,很重要的一個概念就是鏈接惟一性. 確認惟一性通常只要4個值便可.

  • client IP Address

  • client port

  • server IP Address

  • server port

這也是TCP報文和IP報文裏面必不可少的部分。
因此,因爲2MSL時延的關係,會形成client的端口堵塞。有可能會形成端口耗盡的結果。 那對於server 有什麼影響呢?
在普通HTTP 請求當中對於server的影響能夠說沒有,但若是你使用的是socket通訊的話,那麼影響就比較大了, 由於在斷開的時候,server 的socket 就會處於TIME_WAIT狀態, 有可能形成服務器的端口耗盡,在nodeJS裏面可使用setTimeout()進行 自動斷開等優化操做.
可是, 爲何客戶端必定要有2MSL的延時呢?
很簡單,就是保證數據的正確性.
這裏,咱們能夠這麼考慮, 若是沒有2MSL, TCP鏈接的數據有可能發生神馬狀況?
如圖:
point_1 向point_2發送信息時,有一個包,過分延時(在2MSL內). 但此時,point_2向point_1發送斷開請求,沒有時延的狀況下, 二者會當即斷開.而後接着, point_1又向point_2 創建3次握手鍊接。 完成以後,延遲的包又發送過來,因爲包的IP和port都是正確的,point_2固然會無條件處理,而結果就是,將現存的數據給改變--有可能形成數據bug.因此,2MSL的時間頗有必要.但,因爲2MSL的必要性,會對於某些大併發操做的鏈接產生巨大影響, 好比使用siege,ab進行基準測試, 大併發查詢數據庫等等. 因此, 對於此次,實現鏈接的reuse就很是必要了.

相關文章
相關標籤/搜索