To może być głupie pytanie, ale ja i kilku znajomych dyskutowaliśmy o potencjalnych ograniczeniach TCP. Mamy aplikację, która będzie nasłuchiwała klientów (pomyśl o bramie) i kierowała dane wszystkich połączonych klientów przez jednego podłączonego wydawcę kafka do jednego tematu.
Jeden z moich znajomych mówi, że TCP będzie problemem dla tej bramy, ponieważ będzie ustanawiać nowe połączenie dla każdej wysyłanej wiadomości (nie kafka, ale sam protokół transportowy stanowi problem), wymagając za każdym razem nowego portu. W tempie, w jakim będziemy wysyłać te wiadomości klientów (gigabajty), kafce zabraknie portów do odczytu?
Robiłem prace rozwojowe od kilku lat i nigdy wcześniej o tym nie słyszałem i chciałbym uzyskać niższy poziom zrozumienia (jak mi się zdawało), jak działa TCP. Rozumiem, że po ustanowieniu połączenia TCP połączenie to pozostaje otwarte, dopóki nie zostanie przekroczone przez aplikację lub nie zostanie przymusowo zamknięte przez serwer lub klienta. Dane przesyłane przez to połączenie są strumieniem i nie otwierają / zamykają nowych połączeń niezależnie od 3 V (objętość, prędkość, różnorodność).
Jeśli chodzi o porty, jeden port służy do rozgłaszania, a wewnętrzny port deskryptora pliku to aplikacja zarządzana przez program do odczytu / zapisu poszczególnych klientów. Nigdy nie rozumiałem, jak TCP ustanawia nowe połączenia dla każdego zapisywanego pakietu.
Z góry przepraszam, jeśli to pytanie nie jest bezpośrednie i zbyt niejasne. Naprawdę jestem zaskoczony i mam nadzieję, że ktoś może zapewnić więcej kontekstu do tego, co mówią moi koledzy?
źródło
SO_REUSEADDR
szybsze zamykanie gniazd, zwiększanie zasięgu portów efemerycznych itp. PonadtoTCP_FASTOPEN
można użyć kilku przełączników na poziomie systemu operacyjnego, aby obejść inne dobrze znane ograniczenia TCP. Tak czy inaczej, nie ma sensu dyskutować o ograniczeniach TCP, gdy nie masz nawet obciążenia do przetestowania.Odpowiedzi:
Twój przyjaciel jest bardzo zdezorientowany. TCP to protokół zorientowany na strumień. Nie ma pojęcia wiadomości. Oczywiście wykorzystuje pakiety w warstwie IP, ale dla aplikacji jest to szczegół implementacji. TCP wstawia granice pakietów tam, gdzie ma to sens, i niekoniecznie raz na
write()
lubsend()
. Podobnie, łączy kolejne pakiety razem, jeśli odbierasz więcej niż jeden między połączeniami doread()
lubrecv()
.Nie trzeba dodawać, że ten zorientowany na strumień projekt byłby całkowicie niewykonalny, gdyby każde wysyłanie ustanawiało nowe połączenie. Zatem jedynym sposobem na ustanowienie nowego połączenia jest ręczne zamknięcie i ponowne otwarcie połączenia.
(W praktyce większość protokołów zbudowanych na TCP ma coś, co przypomina wiadomości, takie jak żądania HTTP i odpowiedzi. Ale TCP nie zna ani nie dba o struktury takich rzeczy.)
Możliwe, że twój przyjaciel myślał o UDP, który ma wiadomości, ale jest również bezpołączeniowy. Większość implementacji gniazd pozwala „połączyć” gniazdo UDP ze zdalnym hostem, ale jest to tylko wygodny sposób na uniknięcie konieczności wielokrotnego określania adresu IP i portu. W rzeczywistości nic nie robi na poziomie sieci. Niemniej jednak możesz ręcznie śledzić, z którymi użytkownikami rozmawiasz w ramach UDP. Ale jeśli to zrobisz, to decydowanie o tym, co liczy się jako „połączenie”, jest twoim problemem, a nie systemem operacyjnym. Jeśli chcesz ponownie ustanowić „połączenie” dla każdej wiadomości, możesz to zrobić. Prawdopodobnie nie jest to jednak zbyt dobry pomysł.
źródło
Z punktu widzenia TCP nie ma klienta ani serwera (klient / serwer to koncepcja aplikacji, która jest tutaj nie na temat). TCP ustanawia połączenie między peerami, a oba peer mogą wysyłać i odbierać połączenia, dopóki peer go nie zamknie lub upłynie limit czasu braku aktywności.
Problemem może być to, że niektóre aplikacje, np. Przeglądarki, otwierają wiele połączeń, aby jednocześnie ładować takie rzeczy, jak elementy strony internetowej.
TCP nie otwiera nowego połączenia dla każdego wysyłanego segmentu, ale aplikacja może otwierać wiele połączeń TCP. Ponadto po zamknięciu połączenia TCP port TCP używany w połączeniu jest zwalniany i można go ponownie użyć. Ta odpowiedź zawiera pewne informacje i wskazuje na RFC dla TCP.
źródło
Nie, TCP nie musi otwierać nowego połączenia dla każdego wysyłanego pakietu.
Możesz wysyłać wiele pakietów za pomocą trwałych połączeń HTTP , gdzie:
W załączeniu jest rysunek pokazujący różnicę między wieloma połączeniami (ustanowiono wiele połączeń w celu wysłania jednego obiektu na połączenie) a połączeniem trwałym (ustanowiono jedno połączenie i wysłano do niego wiele obiektów):
Źródło: https://www.vcloudnine.de/how-to-dramatically-improve-website-load-times/
źródło
Twoja interpretacja działania TCP jest poprawna.
Co do tego, co powiedział twój przyjaciel, widzę tutaj dwie możliwości:
Źle zrozumiałeś swojego przyjaciela, który odnosił się do pewnych ograniczeń warstwy aplikacji, które powodują, że każda wiadomość jest wysyłana przez nowe połączenie (i nie jest to niekoniecznie niezwykłe; decyzja o takim zachowaniu może, ale nie musi być możliwa, w zależności od oprogramowania stos, którego używasz);
Twój przyjaciel się myli.
źródło
Jak zauważyli inni, TCP absolutnie pozwala, aby połączenie pozostawało otwarte przez dowolny czas, wymieniając dowolną liczbę „wiadomości” w obu kierunkach w tym czasie. To powiedziawszy, to ostatecznie do aplikacji (zarówno klienta, jak i serwera) należy określenie, czy ta zdolność zostanie wykorzystana.
Aby ponownie wykorzystać istniejące połączenie TCP (gniazdo), aplikacja kliencka musi pozostawić to gniazdo otwarte i używać go, gdy musi zapisać więcej danych. Jeśli klient tego nie zrobi, ale zamiast tego odrzuci stare gniazdo i otworzy nowe gniazdo za każdym razem, gdy będzie potrzebne, to rzeczywiście wymusi nowe połączenie, które może powodować problemy z zasobami na kliencie lub serwerze, jeśli będzie to wykonywane wystarczająco często, aby wyczerpać albo pula połączeń stosu TCP.
Podobnie, serwer musi być wystarczająco inteligentny, aby pozostawić gniazdo otwarte z boku i czekać na więcej danych. Podobnie jak klient, ma opcję zamknięcia gniazda, w którym to odporny na błędy klient, który chce wysłać więcej danych, nie będzie miał innego wyjścia, jak otworzyć nowe gniazdo, co prowadzi do tego samego problemu.
Wreszcie, jak wspomnieli inni, TCP jest zorientowany na strumień. Nie ma żadnych ramek. Tylko dlatego, że jeden uczestnik zapisał dane w określony sposób (np. Wywołanie zapisu 1 1024 bajtów, a następnie wywołania zapisu 2 256 bajtów), nie gwarantuje to, że drugi uczestnik odczyta je w kawałkach tego samego rozmiaru (np. Może otrzymać wszystkie 1536 bajtów w jednym odczytanym połączeniu). Dlatego jeśli wysyłasz wiele „wiadomości” przez surowe gniazda TCP, musisz podać własny protokół ramkowania, aby nakreślić różne wiadomości. Chociaż z pewnością istnieją proste sposoby, aby to zrobić, jest to generalnie odradzane, ponieważ istnieje wiele protokołów zbudowanych na TCP, aby rozwiązać ten problem. W celu dalszej dyskusji zapoznaj się z tym: https://blog.stephencleary.com/2009/04/message-framing.html
źródło
Myślę, że twój przyjaciel mówił o HTTP, a nie o TCP.
HTTP był pierwotnie protokołem bezstanowym: każde żądanie HTTP używałoby osobnego połączenia TCP. Dlatego potrzebujemy plików cookie (lub czegoś podobnego) do wdrożenia sesji.
źródło
Wspomniałeś o „pojedynczym połączeniu i wymaganiu nowego portu za każdym razem”, i zinterpretowałbym to, ponieważ masz wielu klientów korzystających z techniki PAT w tym samym środowisku sieciowym, aby połączyć się z serwerem spoza twojej organizacji. PAT miałby limit 65535 (limit sesji TCP dla adresu IPv4). Jeśli to prawda, masz limit.
Czy TCP otwiera nowe połączenie dla każdego wysyłanego pakietu? NIE, nie trwa tak długo, jak ważna jest sesja TCP. i ...
źródło
Podoba mi się doskonała strona wikipedia na TCP . Wyraźnie pokazuje, co dzieje się z numerem portu. Zawiera on również przypadkowo przydatny rozdział na temat wykorzystania zasobów:
Krótko mówiąc, TCP zużywa jeden bardzo skończony zasób, czyli liczbę portów na kliencie (która jest ograniczona rozmiarem pola portu w nagłówku TCP, 16 bitów).
Tak, TCP jest w stanie uruchomić z portów, jeśli klient otwiera wiele połączeń TCP równolegle bez ich zamykania. Problem występuje tylko po stronie klienta i nie ma znaczenia, czy połączenia mają takie same lub różne adresy IP serwera lub porty serwera.
W twoim otoczeniu wydaje się, że masz jedną aplikację, która przyjmuje wiele żądań klientów ( temogą to być pojedyncze żądania TCP, ponieważ być może klienci używają tego do rejestrowania niektórych zdarzeń w aplikacji i nie utrzymują otwartego kanału TCP między nimi) i tworzenia nowego wewnętrznego żądania do brokera Kafka (którym bardzo łatwo mogą być pojedyncze połączenia TCP jeśli zdecydujesz się je wdrożyć w ten sposób). W takim przypadku wąskim gardłem (jeśli chodzi o zasoby, a nie wydajność) byłoby, gdyby udało się uzyskać ogromną liczbę żądań w tym samym czasie od klientów (bez problemu, ponieważ po stronie serwera potrzebny jest tylko jeden port do wszystkie), a Ty otworzysz ogromną liczbę żądań przesyłania dalej do swojej Kafki, a Kafka nie jest w stanie przetworzyć ich wystarczająco szybko, co kończy się tym, że masz więcej niż 16 bitów połączeń otwartych jednocześnie.
Jesteś tutaj własnym sędzią; sprawdź swoją aplikację i spróbuj za każdym razem dowiedzieć się, czy łączysz się z Kafką za pomocą osobnego żądania (być może za pośrednictwem proxy REST API). Jeśli to zrobisz i masz ogromną liczbę klientów, z pewnością jesteś w niebezpieczeństwie.
Jeśli masz tylko garstkę klientów, mniej niż 65 tys. I / lub utrzymujesz jedno połączenie z przeglądarką Kafka, nic ci nie będzie.
źródło