Kontrola Przeciążenia TCP
Algorytmy slow start, unikanie przeciążenia i szybkie odzyskiwanie.
Internet jako System Autostrad: Analogia Przeciążenia
Wyobraźmy sobie internet jako ogromny, globalny system autostrad. Pakiety danych są jak samochody, a połączenia między miastami to autostrady. Kiedy chcesz pobrać plik z serwera w Berlinie, będąc w Warszawie, Twoje pakiety (samochody) podróżują przez tę rozległą sieć. W cichym okresie, na przykład o 3 nad ranem, autostrady są puste, a Twoje samochody mogą podróżować z maksymalną prędkością, docierając szybko i w odpowiedniej kolejności.
Jednak w godzinach szczytu tysiące innych osób również próbuje korzystać z tych samych autostrad. Skrzyżowania i kluczowe odcinki dróg, które w sieci odpowiadają , stają się wąskimi gardłami. Jeśli zbyt wiele samochodów próbuje wjechać na skrzyżowanie jednocześnie, powstaje korek. Ruch zwalnia do minimum, a w świecie cyfrowym routery, których bufory pamięci są pełne, po prostu odrzucają wszelkie nowe samochody (pakiety), które przybywają. To jest przeciążenie sieci (ang. network congestion).
W tym miejscu Kontrola Przeciążenia w TCP staje się kluczowa. Różni się ona fundamentalnie od Kontroli Przepływu. Kontrola Przepływu to rozmowa jeden na jednego między Twoim samochodem a garażem docelowym, zapewniająca, że nie dostarczasz samochodów szybciej, niż garaż jest w stanie je rozładować. W przeciwieństwie do tego, Kontrola Przeciążenia dotyczy bycia dobrym obywatelem na publicznej autostradzie przez Twój samochód. Jest to zestaw mechanizmów, które pozwalają TCP wyczuć poziom ruchu w sieci i odpowiednio dostosować swoją szybkość wysyłania, nie tylko po to, aby chronić odbiorcę, ale aby chronić całą współdzieloną sieć przed załamaniem się w korkach.
Jak TCP Wyczuwa Przeciążenie: Wnioskowanie z Utraty Pakietów
W idealnym świecie routery wysyłałyby jawne sygnały ostrzegawcze do nadawców, jak cyfrowy raport o ruchu drogowym mówiący: Autostrada A2 staje się zatłoczona, proszę zwolnić. Chociaż istnieją nowoczesne mechanizmy, takie jak ECN (Jawne Powiadamianie o Przeciążeniu), pierwotnym i wciąż fundamentalnym sposobem wykrywania przeciążenia przez TCP jest wnioskowanie. TCP zakłada, że internet jest czarną skrzynką i może dedukować o jego wewnętrznym stanie jedynie poprzez obserwację tego, co dzieje się z wysyłanymi pakietami.
Głównym sygnałem przeciążenia dla TCP jest utrata pakietów. Logika jest prosta: najczęstszym powodem zniknięcia pakietu we współczesnym internecie jest to, że dotarł on do routera, którego wewnętrzny bufor był już pełny. Router, nie mając więcej miejsca na zakolejkowanie pakietu, nie miał innego wyboru, jak go odrzucić. Dlatego TCP interpretuje utratę pakietu jako niejawny sygnał, że gdzieś na ścieżce znajduje się wąskie gardło. TCP ma dwa główne sposoby wykrywania, że pakiet został utracony:
- Przekroczenie Limitu Czasu Retransmisji (RTO)
Dla każdego wysłanego segmentu TCP uruchamia licznik czasu. Oczekuje, że otrzyma potwierdzenie (ACK) od odbiorcy, zanim ten licznik wygaśnie. Jeśli licznik się skończy, TCP zakłada, że segment został utracony w sieci. Jest to sygnał poważnego przeciążenia, ponieważ sugeruje, że żadne pakiety nie docierają, lub opóźnienie w sieci dramatycznie wzrosło.
- Trzy Zduplikowane Potwierdzenia ACK
Jest to bardziej subtelny i częstszy sygnał przeciążenia. Wyobraź sobie, że nadawca wysyła segmenty 1, 2, 3, 4 i 5. Segment 3 ginie, ale 4 i 5 docierają do odbiorcy. TCP wymaga, aby potwierdzenia były wysyłane w kolejności. Odbiorca, po otrzymaniu segmentu 4, zauważa, że wciąż brakuje mu segmentu 3. Natychmiast wysyła ACK dla ostatniego otrzymanego w kolejności bajtu (końca segmentu 2). Kiedy dotrze segment 5, ponownie wysyła ACK dla końca segmentu 2. Jeśli nadawca otrzyma trzy potwierdzenia ACK dla tych samych danych (oryginalne ACK plus dwa duplikaty), traktuje to jako mocną wskazówkę, że segment następujący bezpośrednio po tych danych został utracony, ale kolejne segmenty docierają. Jest to znak łagodnego przeciążenia, a nie całkowitej awarii sieci.
Zestaw Narzędzi Kontroli Przeciążenia: Kluczowe Zmienne
Aby zaimplementować swoją strategię kontroli przeciążenia, TCP utrzymuje po stronie nadawcy dwie krytyczne zmienne stanu dla każdego połączenia.
- Okno Przeciążeniowe (cwnd)
'cwnd' to własne ograniczenie nadawcy co do liczby bajtów, które może mieć w drodze (wysłanych, ale jeszcze niepotwierdzonych) w danym momencie. Jest to dynamiczna wartość, którą TCP dostosowuje na podstawie swojej percepcji przeciążenia sieci. Działa ona w połączeniu z oknem ogłaszanym przez odbiorcę ('rwnd'). Rzeczywista liczba bajtów, które nadawca może przesłać, to minimum z 'cwnd' i 'rwnd'. Zapewnia to, że nadawca szanuje zarówno pojemność sieci, jak i pojemność odbiorcy.
- Próg Powolnego Startu (ssthresh)
'ssthresh' to zmienna kontrolna używana do określenia, kiedy TCP powinien przełączyć się z agresywnej początkowej fazy wzrostu (Powolny Start) na bardziej konserwatywną fazę wzrostu liniowego (Unikanie Przeciążenia). Początkowo 'ssthresh' jest często ustawiony na bardzo dużą wartość. Jednak po wykryciu przeciążenia, jest on zazwyczaj ustawiany na połowę wartości 'cwnd' w momencie wystąpienia przeciążenia. Działa jako pamięć ostatniej znanej "bezpiecznej" przepustowości sieci.
Faza 1: Powolny Start (Slow Start) - W Poszukiwaniu Przepustowości
Gdy rozpoczyna się nowe połączenie TCP, nadawca nie ma pojęcia, jaka jest dostępna przepustowość na ścieżce sieciowej. Może to być szybkie łącze światłowodowe lub wolne połączenie satelitarne. Zbyt agresywne rozpoczęcie wysyłania danych mogłoby natychmiast spowodować przeciążenie. Zbyt ostrożne rozpoczęcie marnowałoby czas na szybkiej sieci. Rozwiązaniem TCP jest algorytm Powolnego Startu.
Nazwa jest myląca. Wzrost w tej fazie jest wszystkim, tylko nie powolny; jest wykładniczy. Nazywa się go "powolnym" tylko w porównaniu z alternatywą natychmiastowego wysłania pełnego okna danych.
Mechanizm Powolnego Startu
- Połączenie rozpoczyna się z małym początkowym Oknem Przeciążeniowym ('cwnd'), zazwyczaj między 2 a 4 .
- Za każde otrzymane potwierdzenie (ACK) potwierdzające nowe dane, nadawca zwiększa 'cwnd' o 1 MSS.
- Ponieważ nadawca może wysłać wiele segmentów w jednym obiegu, może otrzymać wiele potwierdzeń w odpowiedzi. W efekcie 'cwnd' w przybliżeniu podwaja się co Czas Obiegu (RTT).
Ten wykładniczy wzrost pozwala TCP szybko zbadać sieć, aby znaleźć jej przybliżoną dostępną przepustowość. Faza Powolnego Startu trwa, dopóki nie wydarzy się jedna z dwóch rzeczy: 'cwnd' osiągnie wartość 'ssthresh' lub wystąpi utrata pakietu. Po osiągnięciu 'ssthresh' TCP przechodzi do bardziej ostrożnej fazy Unikania Przeciążenia.
Faza 2: Unikanie Przeciążenia (Congestion Avoidance) - Delikatne Sondowanie
Po początkowym wykładniczym wzroście Powolnego Startu, TCP wchodzi w bardziej konserwatywną fazę zwaną Unikaniem Przeciążenia. Celem nie jest już gwałtowne odkrywanie przepustowości, ale delikatne sondowanie w poszukiwaniu dodatkowej pojemności bez powodowania przeciążenia.
To zachowanie jest częścią strategii zwanej Dodatkowym Wzrostem, Mnożeniowym Spadkiem (AIMD). Unikanie Przeciążenia to część algorytmu "Dodatkowego Wzrostu".
Mechanizm Unikania Przeciążenia
Zamiast podwajać swoje 'cwnd' co RTT, nadawca teraz zwiększa 'cwnd' w sposób liniowy, addytywny. Celem jest zwiększenie 'cwnd' o około 1 MSS na RTT.
Jest to osiągane poprzez zwiększanie 'cwnd' o mały ułamek za każde otrzymane ACK. Dokładny wzór to dla każdego ACK. Kiedy całe okno danych zostanie potwierdzone, całkowity wzrost wyniesie około 1 MSS. Ta znacznie wolniejsza, liniowa stopa wzrostu ostrożnie testuje granice sieci. Połączenie pozostaje w stanie Unikania Przeciążenia aż do wykrycia utraty pakietu.
Reakcja na Przeciążenie: Mnożeniowy Spadek
Połowa AIMD o nazwie Mnożeniowy Spadek dyktuje, jak TCP reaguje, gdy w końcu wykryje przeciążenie (utratę pakietów). Reakcja zależy od sposobu wykrycia straty.
Reakcja na Timeout (RTO)
Przekroczenie Limitu Czasu Retransmisji to sygnał poważnego przeciążenia. Reakcja TCP jest agresywna:
- 'ssthresh' jest ustawiany na połowę bieżącej wartości 'cwnd'. To zapisuje nowy, niższy "bezpieczny" limit.
- 'cwnd' jest resetowany do małej wartości początkowej (np. 1 MSS).
- Połączenie ponownie wchodzi w fazę Powolnego Startu.
Ta drastyczna redukcja i ponowne uruchomienie zapewniają, że nadawca szybko zmniejsza swoje obciążenie w mocno przeciążonej sieci.
Reakcja na Trzy Zduplikowane ACK: Szybka Retransmisja i Szybkie Odzyskiwanie
Trzy zduplikowane ACK sygnalizują mniej poważny problem. Oznacza to, że prawdopodobnie zginął jeden segment, ale sieć wciąż jest w stanie dostarczać kolejne. Reagowanie pełnym Powolnym Startem byłoby przesadą i niepotrzebnie zmniejszyłoby przepustowość. Aby poradzić sobie z tym inteligentniej, TCP używa algorytmów Szybkiej Retransmisji i Szybkiego Odzyskiwania.
- Szybka Retransmisja: Po otrzymaniu trzeciego zduplikowanego ACK, nadawca nie czeka na wygaśnięcie licznika RTO. Natychmiast retransmituje brakujący segment, na który wskazują potwierdzenia.
- Szybkie Odzyskiwanie: Zamiast resetować 'cwnd' do 1, TCP wykonuje mnożeniowy spadek:
- 'ssthresh' jest ustawiany na połowę bieżącego 'cwnd'.
- 'cwnd' jest również ustawiane na tę nową wartość 'ssthresh' (plus mała wartość za już otrzymane zduplikowane ACK).
To podejście unika kary wydajnościowej związanej z pełnym Powolnym Startem, pozwalając połączeniu na znacznie szybsze odzyskanie sprawności po drobnej utracie pakietów i utrzymanie bardziej stałego wypełnienia "rury" sieciowej. Takie zachowanie jest zdefiniowane przez algorytm TCP Reno.
Ewolucja Kontroli Przeciążenia
Klasyczny algorytm TCP Reno (Powolny Start, Unikanie Przeciążenia, Szybkie Odzyskiwanie) był siłą napędową internetu przez dziesięciolecia. Jednak w miarę ewolucji sieci, które stawały się znacznie szybsze i bardziej złożone (tzw. Długie, Grube Sieci, ang. LFNs), pojawiły się nowe wyzwania, co doprowadziło do rozwoju bardziej zaawansowanych algorytmów.
- TCP NewReno
Udoskonalenie TCP Reno, które sprawniej radzi sobie z wieloma utraconymi pakietami w jednym oknie danych. Unika wielokrotnego wchodzenia w Powolny Start, gdy zaginie wiele segmentów, co prowadzi do płynniejszego odzyskiwania sprawności.
- CUBIC
CUBIC jest domyślnym algorytmem kontroli przeciążenia w nowoczesnych jądrach Linuxa (a zatem w wielu urządzeniach z Androidem i serwerach WWW). W przeciwieństwie do liniowego wzrostu w Reno, CUBIC używa funkcji sześciennej do sterowania wzrostem okna. Początkowo rośnie gwałtownie, potem zwalnia, zbliżając się do ostatniej znanej maksymalnej wartości 'cwnd', a następnie ponownie agresywnie sonduje w poszukiwaniu nowej przepustowości. To zachowanie jest lepiej przystosowane do sieci o wysokim iloczynie przepustowości i opóźnienia, pozwalając na bardziej efektywne i sprawiedliwe skalowanie przepustowości.
- BBR (Bottleneck Bandwidth and Round-trip propagation time)
Opracowany przez Google, BBR przyjmuje fundamentalnie inne podejście. Zamiast polegać na utracie pakietów jako głównym sygnale przeciążenia, BBR próbuje zbudować jawny model ścieżki sieciowej. Ciągle mierzy dwa kluczowe parametry ścieżki: przepustowość wąskiego gardła (najwolniejsze łącze) i czas propagacji w obie strony (bazowe opóźnienie). Następnie próbuje dostosować swoją szybkość wysyłania, aby pasowała do zmierzonej przepustowości, dążąc do utrzymania pełnej "rury" sieciowej, ale bez tworzenia nadmiernych kolejek w buforach routerów (problem znany jako bufferbloat), które mogą powodować tradycyjne algorytmy oparte na stratach. Reprezentuje to dużą zmianę w filozofii kontroli przeciążenia.