Zwiększenie maksymalnej liczby połączeń TCP / IP w systemie Linux

214

Programuję serwer i wygląda na to, że moja liczba połączeń jest ograniczona, ponieważ moja przepustowość nie jest nasycona, nawet jeśli ustawiłem liczbę połączeń na „nieograniczoną”.

Jak mogę zwiększyć lub wyeliminować maksymalną liczbę połączeń, które może jednocześnie otwierać mój system Ubuntu Linux? Czy system operacyjny to ogranicza, czy jest to router lub dostawca usług internetowych? A może to coś innego?

red0ct
źródło
2
@ Software Monkey: Odpowiedziałem na to mimo wszystko, ponieważ mam nadzieję, że może to być przydatne dla kogoś, kto faktycznie pisze serwer w przyszłości.
derobert
1
@derobert: Widziałem to +1. Właściwie tak samo myślałem po poprzednim komentarzu, ale pomyślałem, że pozwolę temu komentarzowi pozostać.
Lawrence Dol

Odpowiedzi:

396

Pewne ograniczenia po stronie klienta i serwera mają wpływ na maksymalną liczbę połączeń, choć nieco inaczej.

Po stronie klienta: Zwiększ zakres portów efermalnych i zmniejsztcp_fin_timeout

Aby znaleźć wartości domyślne:

sysctl net.ipv4.ip_local_port_range
sysctl net.ipv4.tcp_fin_timeout

Zakres portów efermalnych określa maksymalną liczbę gniazd wychodzących, które host może utworzyć z określonego adresu IP. fin_timeoutOkreśla minimalny czas Gniazda te pozostaną w TIME_WAITstanie (bezużyteczny po używaniu raz). Standardowe ustawienia systemowe to:

  • net.ipv4.ip_local_port_range = 32768 61000
  • net.ipv4.tcp_fin_timeout = 60

Zasadniczo oznacza to, że Twój system nie może konsekwentnie gwarantować więcej niż (61000 - 32768) / 60 = 470gniazd na sekundę. Jeśli nie jesteś z tego zadowolony, możesz zacząć od zwiększenia port_range. Ustawienie zakresu 15000 61000jest obecnie dość powszechne. Możesz dodatkowo zwiększyć dostępność, zmniejszając fin_timeout. Załóżmy, że robisz jedno i drugie, powinieneś zobaczyć ponad 1500 połączeń wychodzących na sekundę, łatwiej.

Aby zmienić wartości :

sysctl net.ipv4.ip_local_port_range="15000 61000"
sysctl net.ipv4.tcp_fin_timeout=30

Powyższego nie należy interpretować jako czynników wpływających na zdolność systemu do wykonywania połączeń wychodzących na sekundę. Ale raczej te czynniki wpływają na zdolność systemu do obsługi równoczesnych połączeń w zrównoważony sposób przez duże okresy „aktywności”.

Domyślne wartości Sysctl w typowym systemie Linux dla tcp_tw_recycle& tcp_tw_reuseby to

net.ipv4.tcp_tw_recycle=0
net.ipv4.tcp_tw_reuse=0

Nie pozwalają one na połączenie z „używanego” gniazda (w stanie oczekiwania) i zmuszają gniazda do zakończenia pełnego time_waitcyklu. Polecam ustawienie:

sysctl net.ipv4.tcp_tw_recycle=1
sysctl net.ipv4.tcp_tw_reuse=1 

Umożliwia to szybkie przełączanie gniazd w time_waitstan i ponowne ich użycie. Ale zanim to zrobisz, upewnij się, że nie powoduje to konfliktu z protokołami, których używałbyś dla aplikacji, która potrzebuje tych gniazd. Przeczytaj post „Radzenie sobie z czasem oczekiwania TCP” autorstwa Vincenta Bernata, aby zrozumieć konsekwencje. Ta net.ipv4.tcp_tw_recycle opcja jest dość problematyczna dla serwerów publicznych, ponieważ nie obsługuje połączeń z dwóch różnych komputerów za tym samym urządzeniem NAT , co jest problemem trudnym do wykrycia i czekającym na ugryzienie. Uwaga, net.ipv4.tcp_tw_recyclektóra została usunięta z systemu Linux 4.12.

Na serwerze Side:net.core.somaxconn wartość odgrywa ważną rolę. Ogranicza maksymalną liczbę żądań w kolejce do gniazda nasłuchującego. Jeśli jesteś pewien możliwości aplikacji serwera, zwiększ ją z domyślnego 128 do czegoś takiego jak 128 do 1024. Teraz możesz skorzystać z tego wzrostu, modyfikując zmienną backlog nasłuchiwania w wywołaniu nasłuchiwania aplikacji, na równą lub wyższą liczbę całkowitą.

sysctl net.core.somaxconn=1024

txqueuelenrolę mają również parametry kart Ethernet. Domyślne wartości to 1000, więc zwiększ je do 5000, a nawet więcej, jeśli twój system może to obsłużyć.

ifconfig eth0 txqueuelen 5000
echo "/sbin/ifconfig eth0 txqueuelen 5000" >> /etc/rc.local

Podobnie zwiększ wartości dla net.core.netdev_max_backlogi net.ipv4.tcp_max_syn_backlog. Ich wartości domyślne to odpowiednio 1000 i 1024.

sysctl net.core.netdev_max_backlog=2000
sysctl net.ipv4.tcp_max_syn_backlog=2048

Teraz pamiętaj, aby uruchomić aplikacje po stronie klienta i serwera, zwiększając ulimty FD w powłoce.

Oprócz powyższej jeszcze jedną popularniejszą techniką stosowaną przez programistów jest zmniejszenie liczby wywołań pisania TCP . Moje własne preferencje to użycie bufora, w którym przesyłam dane, które chcę wysłać do klienta, a następnie w odpowiednich punktach zapisuję buforowane dane do właściwego gniazda. Ta technika pozwala mi korzystać z dużych pakietów danych, zmniejszać fragmentację, zmniejszać wykorzystanie procesora zarówno na poziomie użytkownika, jak i na poziomie jądra.

mdk
źródło
4
Genialna odpowiedź! Mój problem był nieco inny, tzn. Próbowałem przenieść informacje o sesji z magazynu sesji na poziomie aplikacji do redis przez PHP. Z jakiegoś powodu nie mogłem dodać więcej niż 28230 sesji bez dodawania dużej ilości snu za jednym razem, bez błędów widocznych ani w php, ani w dziennikach redis. Rozbiliśmy sobie głowy przez cały dzień, dopóki nie pomyślałem, że może problem nie dotyczy php / redis, ale warstwy tcp / ip łączącej oba i doszliśmy do tej odpowiedzi. Udało się naprawić problem w krótkim czasie :) Bardzo dziękuję!
s1d
27
Nie zapominaj, że zawsze mówimy o porcie IP +. Możesz mieć „nieograniczone” gniazda otwarte na port XY z wielu różnych adresów IP. Limit 470 dotyczy tylko otwartych gniazd jednocześnie z tym samym adresem IP. Inny adres IP może mieć własne 470 połączeń z tymi samymi portami.
Marki555
6
@ Marki555: Twój komentarz jest bardzo poprawny. Aplikacje opracowane do generowania i utrzymywania dużej liczby połączeń wychodzących muszą mieć „świadomość” dostępnych adresów IP do tworzenia połączeń wychodzących, a następnie muszą odpowiednio łączyć się z tymi adresami IP za pomocą pewnego rodzaju „algorytmu round-robin” i utrzymywać „tablica wyników”.
mdk
8
Ta odpowiedź zawiera błędy. Po pierwsze, net.ipv4.tcp_fin_timeout dotyczy tylko stanu FIN_WAIT_2 ( cs.uwaterloo.ca/~brecht/servers/ip-sysctl.txt ). Po drugie, jak powiedział @Eric, „470 gniazd w danym momencie” jest nieprawidłowe.
Sharvanath
3
@mdk: Nie jestem pewien w tej części dotyczącej obliczeń (61000 - 32768) / 60 = 470 sockets per second. Czy możesz to rozwinąć?
Tom Taylor,
64

Istnieje kilka zmiennych do ustawienia maksymalnej liczby połączeń. Najprawdopodobniej najpierw brakuje Ci numerów plików. Sprawdź ulimit -n. Potem są ustawienia w / proc, ale te domyślne to dziesiątki tysięcy.

Co ważniejsze, wygląda na to, że robisz coś złego. Pojedyncze połączenie TCP powinno być w stanie wykorzystać całą przepustowość między dwiema stronami; jeśli to nie jest:

  • Sprawdź, czy ustawienie okna TCP jest wystarczająco duże. Domyślne ustawienia Linuksa są odpowiednie do wszystkiego oprócz naprawdę szybkiego łącza inet (setki Mb / s) lub szybkich łączy satelitarnych. Jaki jest twój produkt opóźnienie przepustowość *?
  • Sprawdź utratę pakietów za pomocą polecenia ping z dużymi pakietami ( ping -s 1472...)
  • Sprawdź ograniczenia prędkości. W systemie Linux jest to skonfigurowane za pomocątc
  • Potwierdź, że przepustowość, o której myślisz, że faktycznie istnieje, za pomocą np. iperf
  • Potwierdź, że twój protokół jest rozsądny. Pamiętaj o opóźnieniu.
  • Jeśli jest to gigabit + LAN, czy możesz używać dużych pakietów? Jesteś?

Być może źle zrozumiałem. Może robisz coś takiego jak Bittorrent, gdzie potrzebujesz wielu połączeń. Jeśli tak, musisz dowiedzieć się, ile połączeń faktycznie używasz (spróbuj netstatlub lsof). Jeśli liczba ta jest znaczna, możesz:

  • Mają dużą przepustowość, np. 100 Mb / s +. W takim przypadku może być konieczne zwiększenie ulimit -n. Mimo to ~ 1000 połączeń (domyślnie w moim systemie) to całkiem sporo.
  • Masz problemy z siecią, które spowalniają połączenia (np. Utrata pakietów)
  • Masz coś innego, co Cię spowalnia, np. Przepustowość IO, szczególnie jeśli szukasz. Sprawdziłeś iostat -x?

Ponadto, jeśli używasz routera NAT klasy konsumenckiej (Linksys, Netgear, DLink itp.), Strzeż się, że możesz przekroczyć jego możliwości przy tysiącach połączeń.

Mam nadzieję, że to pomoże. Naprawdę zadajesz pytanie dotyczące sieci.

derobert
źródło
16

Aby poprawić odpowiedź udzieloną przez derobert,

Możesz ustalić limit połączenia z systemem operacyjnym, catting nf_conntrack_max.

Na przykład: cat / proc / sys / net / netfilter / nf_conntrack_max

Możesz użyć następującego skryptu, aby policzyć liczbę połączeń TCP do danego zakresu portów TCP. Domyślnie 1-65535.

Potwierdzi to, czy maksymalny limit połączenia systemu operacyjnego jest przekroczony.

Oto skrypt.

#!/bin/bash
OS=$(uname)

case "$OS" in
    'SunOS')
            AWK=/usr/bin/nawk
            ;;
    'Linux')
            AWK=/bin/awk
            ;;
    'AIX')
            AWK=/usr/bin/awk
            ;;
esac

netstat -an | $AWK -v start=1 -v end=65535 ' $NF ~ /TIME_WAIT|ESTABLISHED/ && $4 !~ /127\.0\.0\.1/ {
    if ($1 ~ /\./)
            {sip=$1}
    else {sip=$4}

    if ( sip ~ /:/ )
            {d=2}
    else {d=5}

    split( sip, a, /:|\./ )

    if ( a[d] >= start && a[d] <= end ) {
            ++connections;
            }
    }
    END {print connections}'
whitehat237
źródło
3
which awkjest twoim przyjacielem do określenia ścieżki do awk, SunOS również ma do niego link :)
Panagiotis Moustafellos
2
@PanagiotisM. whichpolega na tym, że program jest w PATHtakim przypadku, którego można użyć awkzamiast podać pełną ścieżkę. (powiedziawszy, nie jestem pewien, czy rozwiązanie w skrypcie jest bliższe doskonałości, ale nie o to chodzi w skrypcie).
Michael Krelin - haker
5
Uwielbiam sposób, w jaki ten skrypt zmienia się w balistyczny, aby określić awklokalizację, ale zakładam, że shell jest zawsze /bin/bash (wskazówka pro: AIX5 / 6 domyślnie nawet nie ma basha).
kubańczyk
Czy awkwykrywanie jest przydatne? Osobiście po prostu założyłbym, że mam poprawkęPATH ale rozsądną alternatywą może być /usr/bin/env awki /usr/bin/env bashodpowiednio. Jeśli chodzi o to, co jest warte, błędnie wskazała lokalizację w moim systemie Linux. Nie /usr/bin/awkma/bin/awk
Wolph
1
kiedy uruchamiam ten skrypt, otrzymuję 798, więc co to znaczy?
10

Na poziomie aplikacji programista może zrobić:

Po stronie serwera:

  1. Sprawdź, czy moduł równoważenia obciążenia (jeśli masz), działa poprawnie.

  2. Przekształć wolne limity czasu TCP w 503 Szybka natychmiastowa odpowiedź, jeśli moduł równoważenia obciążenia działa poprawnie, powinien wybrać działający zasób do obsłużenia, i jest lepszy niż zawieszanie się tam z nieoczekiwanymi komunikatami o błędach.

Np .: jeśli używasz serwera węzłów, możesz użyć toobusy z npm. Realizacja coś takiego:

var toobusy = require('toobusy');
app.use(function(req, res, next) {
  if (toobusy()) res.send(503, "I'm busy right now, sorry.");
  else next();
});

Dlaczego 503? Oto kilka dobrych informacji na temat przeciążenia: http://ferd.ca/queues-don-t-fix-overload.html

Możemy również wykonać trochę pracy po stronie klienta:

  1. Spróbuj grupować połączenia wsadowo, zmniejszyć ruch i łączną liczbę żądań czarno-białych klientów i serwerów.

  2. Spróbuj zbudować pamięć podręczną warstwy pośredniej, aby obsłużyć niepotrzebne duplikaty żądań.

Kev
źródło