OpenVPN prin TCP: rto_min nu rezolvă blocajul de la capul cozii
Un flux de octeți exterior livrează în ordine, așa că un singur segment lipsă poate opri fiecare flux interior. Un temporizator interior mai lung schimbă un ceas, nu această constrângere.
Un shell interactiv prin tunel îngheață câteva secunde în timp ce rulează o copiere de fișiere, apoi își revine ca și cum nimic nu s-ar fi întâmplat. Descărcarea este singurul lucru cu o problemă, totuși și shell-ul așteaptă, iar ambele se recuperează în același moment. Acest tipar este semnătura unui singur flux fiabil care duce multe conversații și supraviețuiește aproape oricărei ajustări pe care oamenii o încearcă mai întâi.
Simptomul merită citit cu atenție pentru că indică un mecanism, nu o setare: pauza este partajată, deci cauza este partajată, iar obiectul partajat este conexiunea exterioară. Restul jurnalului arată normal în timp ce se întâmplă.
Simptomul: totul se oprește deodată
Două conexiuni interioare care se blochează împreună sunt indiciul. O descărcare lentă și un shell lent sunt conversații TCP separate, cu numere de secvență și stare de congestionare separate, așa că o defecțiune într-una nu ar trebui să o atingă pe cealaltă. Când fac pauză în același ritm, ceva deasupra lor le ordonează octeții.
Acel ceva este transportul tunelului. În modul TCP, OpenVPN scrie pachetele încapsulate într-o singură conexiune TCP către peer, iar contractul TCP este livrarea în ordine a unui singur flux de octeți. Octeții următori nu sunt predați aplicației înaintea celor lipsă, indiferent cărei conversații interioare aparțin.
Observați ce nu spune simptomul. Nu spune că traseul este stricat, că serverul este supraîncărcat sau că cifrul este lent. Acestea produc forme diferite: debit uniform scăzut, expirări de timp sau defecțiuni care afectează o singură direcție. O pauză partajată care se termină când un segment pierdut este retransmis este în mod specific un efect de ordonare.
Un flux de octeți exterior, multe conversații interioare
Imaginați-vă tunelul ducând simultan o descărcare și un shell. Un segment al conexiunii exterioare se pierde. Segmentele următoare pot ajunge la gazda receptoare, iar stiva TCP receptoare le va confirma, dar nu le poate livra mai sus până când golul nu este umplut. Prin urmare, OpenVPN nu are nimic de citit pentru shell, deși octeții shell-ului au sosit de mult.
Acesta este blocajul de la capul cozii în interiorul unei conexiuni și nu cere ca cele două aplicații să împartă o conexiune interioară. Împart cea exterioară, și asta este suficient.
Efectul de ordinul doi îl agravează. Un expeditor interior care nu vede confirmări poate retransmite date pe care stratul exterior încearcă deja să le livreze. Această retransmisie devine trafic suplimentar de tunel pe un traseu care tocmai a dovedit că poate pierde segmente, așa că munca de recuperare concurează cu recuperarea care contează.
Nimic din toate acestea nu garantează o prăbușire. Pe un traseu curat, cu dus-întors scurte și cozi generoase, aranjamentul poate fi perfect utilizabil ani de zile. Defecțiunea apare când pierderea, întârzierea și adâncimea cozii se aliniază, motiv pentru care rapoartele se grupează în jurul legăturilor ascendente congestionate și al legăturilor mobile, nu al birourilor bine dotate.
De ce un temporizator interior mai lung nu poate ajuta
Creșterea timpului minim de retransmisie pe o rută, cu rto_min, amână retransmisia bazată pe temporizator pentru traficul TCP generat local care folosește ruta. Este un buton real cu un efect real și abordează problema de ordinul doi, nu pe cea dintâi.
ip route show
ip route show cache 2>/dev/null | head
ss -tin dst 10.70.0.1
Ce nu poate face: să elibereze octeții exteriori în afara ordinii, să recupereze segmentul lipsă mai repede sau să facă serverul de aplicații de la distanță să adopte același temporizator. Stiva receptoare livrează în continuare în ordine, iar expeditorul de la capătul îndepărtat își folosește în continuare propriile măsurători de dus-întors.
Nu dezactivează nici toate celelalte mecanisme de recuperare a pierderilor. Acolo unde pierderea reală cere recuperare pe bază de temporizator, un minim mai mare face aplicația să aștepte mai mult înainte de a acționa, ceea ce este opusul a ceea ce are nevoie o sesiune sensibilă la latență.
Un prag fix de douăsprezece secunde apare în sfaturile pe acest subiect de parcă existența unui tunel TCP l-ar implica. Temporizatorul de retransmisie descris în RFC 6298 derivă din timpul de dus-întors măsurat, cu retragere exponențială, așa că un prag constant este o alegere de politică pentru un anumit traseu, nu o cerință a protocolului. Aplicat larg, ascunde simptomul modificând în același timp recuperarea pentru fiecare flux acoperit de ruta respectivă.
Ce face și ce nu face tcp-nodelay
Opțiunea de tunel care dezactivează tamponarea în stil Nagle pe socketul de transport are un scop îngust: oprește scrierile mici să aștepte mai multe date înainte de transmisie. Asta ajută traficul interactiv să evite o întârziere suplimentară pe un traseu altfel sănătos.
Nu transformă transportul într-unul fără ordine și nu scurtează recuperarea după un segment pierdut. Un shell care face pauză pentru un timp de retransmisie va face pauză în continuare cu opțiunea activată, pentru că opțiunea nu a atins niciodată regula de ordonare.
Confundarea celor două este ușoară pentru că ambele sunt descrise ca remedii de latență. Una elimină o așteptare artificială introdusă de tamponare, cealaltă ar cere schimbarea a ceea ce garantează transportul, iar nicio opțiune de socket nu face asta.
Cum comparăm transporturile corect
Dacă operatorul permite și ambele servicii există, comparați un serviciu UDP și un serviciu TCP care duc aceeași încărcare pe același traseu de acces către același server. Schimbarea țării, a furnizorului sau a generației de server între cele două teste introduce o variabilă care va domina rezultatul.
Urmăriți pauza partajată, nu o cifră de debit. Mai multe aplicații interioare care se blochează împreună în timpul pierderii exterioare este observația care identifică blocajul de la capul cozii; o singură cifră mai mică la testul de viteză nu o face.
Capturile autorizate pe interfața tunelului și pe interfața fizică separă curat cele două straturi, pentru că retransmisiile interioare și exterioare aparțin unor conexiuni diferite, cu spații de secvență diferite. A vedea retransmisii interioare în timp ce fluxul exterior arată un gol este dovada directă.
Trecerea la UDP elimină fluxul fiabil suplimentar și lasă TCP-ul interior să își facă propria recuperare. Nu promite pierderi zero sau latență mică, iar o legătură ascendentă saturată, cozi supradimensionate sau o problemă de MTU vor trebui totuși rezolvate. Traficul din afara tunelului nu este niciodată ținut în spatele golului de secvență al tunelului, deși poate suferi în continuare când ambele trasee împart o legătură congestionată, iar acel blocaj partajat merită o investigație proprie în loc să fie pliat în aceasta.