一、介紹
Tail Loss Probe (TLP)是同樣是一個發送端算法,主要目的是使用快速重傳取代RTO超時重傳來處理尾包丟失場景。在一些WEB業務中,如果TCP尾包丟失,如果依靠RTO超時進行重傳會帶來比較大的延遲,進而影響用戶體驗。如果一個TCP連接沒有在一段時間內沒有收到ACK報文,TLP會強制傳輸還沒有收到ACK確認的報文里面的最后一個報文或者未發送的新報文(傳輸的這個報文就叫做loss probe)。這里強制傳輸是指loss probe的發送不受到擁塞控制的限制但是同樣收到對方通告的接收窗口的限制。這個loss probe報文如果順利到達對端就會有效的觸發一個基於SACK的快速重傳,實際上TLP結合增強ER和FACK功能可以完全使用快速重傳取代尾包的第一次RTO超時,從而有效的提升了TCP性能。TLP只能使用在支持SACK的TCP連接上並且需要連接處於Open狀態(Open狀態是linux擁塞控制中的一種狀態,表示一個發送端順序接收到了沒有SACK選項的ACK報文同時沒有發現任何丟包的痕跡,例如發現了RTO超時的丟包痕跡就不會觸發TLP)。
RTO超時重傳除了會帶來比較大的延遲外,還會對擁塞控制產生嚴重的影響,產生RTO超時重傳的情況有很多種,比如:
尾包連續丟失,比如傳輸了4個報文,最后的三個報文都發生了丟失,那么只能依靠RTO超時來進行重傳
整個發送窗口的報文丟失,比如發送窗口只允許發送2個tcp報文,tcp需要發送4個報文的時候,如果前2個報文發生丟失,那么也只是依靠RTO超時來觸發重傳
沒有足夠的dup ACK來觸發快速重傳,ER和thin stream對於這種場景有一些改善
鏈路時延突然增大,這種也會導致RTO超時,產生無效重傳。
TLP的主要目的就是解決前兩種場景,即尾包連續丟失和整個發送窗口的報文都丟失。在這兩種情況下,如果一個TCP連接在一段時間里面沒有收到ACK確認包,又不能發出新數據,則大概率會發生RTO超時重傳,這時候就可以通過發送loss probe來觸發SACK反饋進行快速重傳,等待的這段超時時間則稱呼為probe timeout (PTO)。PTO應該是限制在小於等於RTO的。
二、TLP算法流程
目前TLP算法還沒有正式的RFC協議,只有協議草案,下面介紹的算法流程為協議草案中的流程
1、在每次傳出新數據的時候,如果滿足下面的條件,則啟動PTO定時器
連接處於Open狀態
連接因為擁塞窗口大小受限而不能發出新的數據或者緩存中沒有新的待發送數據(擁塞窗口我們后面介紹)
連續的PTO<=CON_PTO_SH,連續的PTO是指PTO超時觸發loss probe報文的發送時候如果這個loss probe報文重啟了PTO定時器那么就叫做連續PTO,其中協議草案中給出的CON_PTO_SH為2,但是協議草案指出實現上也可以使用CON_PTO_SH=1進行判斷,linux使用的是CON_PTO_SH=1進行判斷,也就是說linux中在發送loss probe報文的時候雖然不會重設PTO定時器,但是在收到ACK報文的時候仍然可以重啟PTO定時器,最終結果就是可能會連續發送新數據包作為loss probe報文,但是如果loss probe報文為尾包的重傳報文,那么只能發送一次而不能連續發送,后面介紹TLP與擁塞控制的時候會給出示例。
連接支持SACK功能
在滿足上面這些條件后,判斷如果當前發出去的還沒有收到ACK確認的報文的個數(稱呼為FlightSize)大於1,則設置PTO = max(2*SRTT, 10ms),如果FlightSize =1,則設置PTO =max(2*SRTT, 1.5*SRTT+WCDelAckT)。如果之前存在RTO定時器,則使用PTO定時器取代它並設置 PTO = min(RTO,PTO)。其中WCDelAckT代表延遲Ack定時器的最大值,之前我們說過TCP並不會對每個數據包都回復ACK,有可能會延遲等待一段時間和后面的數據包一起回復ACK報文,WCDelAckT就是延遲等待的最大時間。協議草案對WCDelAckT的建議值為200ms。
2、當收到ACK報文的時候
如果上面列出的條件仍然滿足,那么在當前時間(即收到ACK報文)的基礎上重新設置PTO定時器。如果上面條件不滿足則取消PTO定時器。
3、當PTO定時器超時的時候
如果一個新的未發送的報文存在,發送新的報文。這個報文可能之前受到擁塞控制的限制而沒有發送出去,linux在PTO超時發送這個報文的時候不會受到擁塞控制和Nagle算法的限制(后面文章會介紹擁塞控制和Nagle算法)。
如果沒有新的未發送過的報文存在,則重傳最后一個發送的報文。注意這里是發送最后一個已發送報文。可以參考后面的wireshark示例。
如果1中列出的條件仍然滿足則重新啟動PTO定時器,否則重新啟動RTO定時器。linux中連續PTO<=1,因此此處總是會重啟RTO定時器。
三、丟包探測(Loss Detection)
在擁塞控制里面,TCP發送端需要檢測評估丟包情況來調整擁塞窗口,進而限制發送窗口的大小(后面內容會詳細介紹,暫時只要知道擁塞控制需要檢測評估丟包情況就行了)。在TLP算法流程里面我們介紹了當PTO超時時候可能會發送最后一個發出的數據包作為loss probe,但是如果這個數據包的初傳丟失了,而loss probe的這次重傳成功的時候,就需要一種探測算法來探測丟包。后面內容我們會介紹兩種分別基於DSACK和FRTO的虛假重傳檢測,但是目前還有一部分主機並不支持虛假重傳的檢測,因此協議草案中給出的檢測方式只是依賴RFC2018中的SACK,但是目前linux實現上已經支持使用DSACK來處理TLP的ACK報文了。假設連續發生了N次PTO超時而重傳了N次尾包(對於linux因為不能連續PTO超時重傳,N=1),假如接收端能收到N個TLP dupack(指ack number正好為TLP重傳的recovery point,且不攜帶新的SACK塊),則說明這N個尾包都到達了接收端,如果接收到的TLP dupack小於N,則說明可能發生了丟包,應該進行擁塞控制的處理。注意上面FlightSize=1時候,PTO的設置添加了WCDelAckT限制就是考慮到了延遲ACK的情況。對於接收端回復的ACK包丟失的問題,這個算法是保守的也是可接受的。
四、wireshark示例
1、tcp_retrans_collapse=0、tcp_sack=1、tcp_early_retrans=4、tcp_fack=1
在同時打開FACK和TLP的情況下,當loss probe觸發的SACK塊信息與ack number之間相隔3個以上(包括3個)TCP報文的時候就可以觸發基於FACK的快速重傳,如下圖所示
server端傳輸No6-No10共5個tcp報文,在傳輸完No10報文的時候啟動PTO定時器
client正常接收No6報文並回復不帶有SACK信息的ACK報文No11,對於No7-No10直接丟棄做模擬丟包。
server端在接收到No11報文的時候,重啟PPTO定時器為2倍RTT,從server端程序可以獲取此時的RTT為500.503ms,因此PTO定時器大約為1s
PTO定時器超時,server端重傳No10報文的數據,對應No12報文
client回復一個ACK確認包,同時含有SACK信息通知server端自己收到了序列號范圍為(33-40)的數據
此時server端通過ack number得知client已經收到了No6報文,通過SACK信息得知client收到了No10報文,中間間隔3個TCP報文,達到了dup ACK門限,因此立即觸發基於FACK的快速重傳
2、tcp_retrans_collapse=0、tcp_sack=1、tcp_early_retrans=4、tcp_fack=0
接下來的這個示例No13之前(包括No13)的報文交互與上一個示例類似,但是因為這里關閉了FACK功能,在收到No13的時候並不能觸發快速重傳。但是因為PTO超時重傳No12報文的時候初始化了RTO定時器,因此最終會RTO超時進行重傳(No14-No16)。這里也可以看到linux中CON_PTO_SH這個參數為1。
如果CON_PTO_SH為2的話會在No12重傳之后再次重啟PTO定時器,接着PTO超時再次進行loss probe,此時連續2次loss probe,達到CON_PTO_SH門限重啟RTO定時器。
3、tcp_retrans_collapse=0、tcp_sack=1、tcp_early_retrans=3、tcp_fack=0
在同時打開FACK和TLP的情況下,當loss probe觸發的SACK塊信息與ack number之間相隔3個以下(不包括3個)TCP報文的時候就可以觸發基於增強ER的快速重傳,如下圖所示
server端發送No6-No9四個tcp報文,並在發出No9報文的時候啟動PTO定時器
client正常接收No6報文,並回復不帶有SACK信息的ACK確認包No10,No7-No9三個報文模擬報文丟失,client不回復ACK確認包
server端在收到No10的ACK確認包的時候,重啟PTO定時器為2倍的RTT,PTO定時器超時的時候,server端進行loss probe發出No11數據包來重傳No9數據包的內容
client收到No11數據包后,回復ACK確認包,並包含SACK信息通知server端收到了序列號為(25-32)的數據包。
server端收到No11的dup ACK后,判斷滿足ER重傳的條件,啟動ER定時器,定時時間為RTT/4,ER定時器超時之后server端發出No13和No14兩個數據包,接着client分別回復兩個ACK確認包。可以看到這里ER重傳時候能夠重傳兩個TCP報文,而上面FACK重傳的時候只能重傳一個TCP報文,這些都是擁塞控制的結果,我們后面內容會簡單介紹的。
4、TLP與延遲ACK交互
TLP與延遲ACK的交互示例放到后續SWS的介紹文章中,作為與SWS綜合示例的對照。
補充說明:
1、https://www.ietf.org/proceedings/84/slides/slides-84-tcpm-14.pdf
2、https://www.ietf.org/archive/id/draft-dukkipati-tcpm-tcp-loss-probe-01.txt 這個是linux實現的參考文檔,里面有一些谷歌的TCP統計信息,有助於了解為什么我們會需要TLP功能。
3、linux對tlpack的處理可以參考tcp_process_tlp_ack,除了使用本文介紹的機制外還會使用DSACK來檢測,后面文章會介紹DSACK相關內容。
4、TLP兩個patch的log里面有對TLP代碼的解釋,可以參考