Gniazda znalezione przez lsof, ale nie przez netstat

19

Mam aplikację, której brakuje deskryptorów plików, najwyraźniej przez otwarcie gniazd, ale nie mogę dokładnie dowiedzieć się, co robią te gniazda. Pojawiają się one w wynikach lsof jako

java    9689 appuser 1010u  sock       0,5          263746675 can't identify protocol
java    9689 appuser 1011u  sock       0,5          263746676 can't identify protocol
java    9689 appuser 1012u  sock       0,5          263746677 can't identify protocol
java    9689 appuser 1014u  sock       0,5          263746678 can't identify protocol
java    9689 appuser 1015u  sock       0,5          263746679 can't identify protocol
java    9689 appuser 1016u  sock       0,5          263746681 can't identify protocol

oraz w / proc / $ PID / fd jako

lrwx------ 1 appuser appuser 64 Jun 23 11:49 990 -> socket:[263732085]
lrwx------ 1 appuser appuser 64 Jun 23 11:49 991 -> socket:[263732086]
lrwx------ 1 appuser appuser 64 Jun 23 11:49 992 -> socket:[263735307]
lrwx------ 1 appuser appuser 64 Jun 23 11:49 993 -> socket:[263732088]
lrwx------ 1 appuser appuser 64 Jun 23 11:49 995 -> socket:[263735308]
lrwx------ 1 appuser appuser 64 Jun 23 11:49 996 -> socket:[263735309]
lrwx------ 1 appuser appuser 64 Jun 23 11:49 997 -> socket:[263745434]
lrwx------ 1 appuser appuser 64 Jun 23 11:49 998 -> socket:[263745435]
lrwx------ 1 appuser appuser 64 Jun 23 11:49 999 -> socket:[263745436]

ale nie ma podobnego wyniku w netstat -a.

Co to są te gniazda i jak mogę dowiedzieć się, co robią?

Edycja : Próbowałem uruchomić grep $SOCKET /proc/net, zgodnie z zaleceniami w lsof FAQ , gdzie $ SOCKET to na przykład 263746679, ale to również nie dało rezultatów.


Jako tło aplikacja jest pojemnikiem na wiele zadań, które między innymi wykonują połączenia sieciowe. Muszę wyróżnić ten, który wpadnie w szał, ale dopóki nie dowiem się, z kim komunikują się te gniazda, utknąłem.

Robert Munteanu
źródło
Ostatnio mamy do czynienia z tym problemem z jedną z naszych aplikacji internetowych .NET Core (serwer Ubuntu z Kestrel), ale zarejestrowane urządzenie to „0,9” z nazwą „protokół”: TCP ”. Próba ustalenia, jakie dokładnie urządzenia 0 i 9 są sprawdzone, jest trudna. Ale wszystkie objawy wyglądają jak ten sam przypadek otwierania gniazd bez ich wiązania i używania.
icelava

Odpowiedzi:

17

Może się to zdarzyć, jeśli utworzysz gniazdo, ale nigdy nie łącz z nim gniazda () ani bind (). Najlepszym rozwiązaniem może być zapisanie (-fF) aplikacji, a następnie odsyłanie do wyniku lsof w celu ustalenia, które gniazda powodują problem. Jako dodatkowa metoda debugowania: jeśli otoczysz wywołania gniazda informacjami debugowania i wypiszesz je w katalogu / dev / null, pojawi się ono w trybie ciągłym bez dostarczania komicznie dużych plików dziennika.

BMDan
źródło
Dzięki, to brzmi interesująco. Spróbuję dowiedzieć się, czy tak rzeczywiście jest w przypadku naszej aplikacji.
Robert Munteanu
1
Nieco w tej samej linii, ponieważ jest to Java, może być bardzo trudno używać strace; lepszym sposobem może być utworzenie własnej podklasy gniazd, która rejestruje informacje przed przekazaniem ich do nadrzędnego (rzeczywistego) gniazda JDK. strace widzi tylko podstawowe wywołania Java do systemu operacyjnego i nie może zobaczyć w twoich wątkach tego, co faktycznie wykonuje te wywołania gniazd, aby strace to wszystko wygląda jak jedna wielka kula java.
troyengel
@troyengel: Odkryłem (ponownie) Byteman ( jboss.org/byteman ) bardzo schludne narzędzie, które pozwala mi wprowadzić kod bajtowy potrzebny do śledzenia tych wywołań.
Robert Munteanu
Najbardziej użyteczna odpowiedź, więc dostaje nagrodę. Dzięki!
Robert Munteanu,
2

Za pomocą Pythona napotkałem ten sam problem na gniazdach SSL:

  • Kiedy używam socket.close (), gniazdo pozostaje w stanie CLOSE_WAIT na czas nieokreślony
  • kiedy używam socket.shutdown (), lsof mówi „nie mogę zidentyfikować protokołu”

Rozwiązaniem było rozpakowanie warstwy SSL przed zamknięciem:

  • origsock = socket.unwrap ()
  • origsock.close ()

To poprawnie zamyka gniazda w mojej aplikacji.

użytkownik48134
źródło
1

Pierwszą rzeczą, którą bym zrobił, było zwiększenie, jeśli limit deskryptora pliku:

~# vi /etc/sysctl.conf
fs.file-max = 331287

Następnie upewnię się, że twój system jest aktualny, dotyczy to wszystkich bibliotek i serwerów. Możliwe, że Twój serwer aplikacji Java jest nieaktualny (jeśli go używasz). Istnieje również możliwość, że serwer aplikacji jest źle skonfigurowany, powinieneś spojrzeć na plik konfiguracyjny i obniżyć swój connectionTimeouti / lub swój maxKeepAliveRequests(nie jestem pewien, jakiego serwera aplikacji używasz, czy w ogóle go używasz ...).

Nie jestem pewien, co robi ta aplikacja, ale jeśli uważasz, że nie wymaga dziesiątek tysięcy gniazd, prawie na pewno jest to „wyciek deskryptora pliku” w Twojej aplikacji Java. Może być konieczne wysłanie raportu o błędzie do dostawcy. W tym raporcie o błędzie należy podać informacje o sposobie odtworzenia problemu.

Oto kilka sposobów debugowania problemu.

Wireshark (lub twireshark dla cli) to najlepsze narzędzie, aby zobaczyć, jak te gniazda są używane. Wireshark da ci podział na rodzaj ruchu wyrzucanego przez drut. Najprawdopodobniej kilka pierwszych połączeń zakończy się powodzeniem, a następnie osiągnie limit deskryptora pliku. Po przekroczeniu limitu deskryptora pliku Wireshark nie będzie niczego wychwytywał (a do tego fajniejszy jest netstat), ale pomoże to zawęzić problem. Może się zdarzyć, że wysyłanych jest wiele wychodzących SYN, jednak żadne SYN / ACK nie są odbierane, dlatego wiele połączeń TCP zostaje zablokowanych w stanie SYN_WAIT.

Jeśli masz dostęp do kodu źródłowego i znasz typ tworzonych gniazd (np. Użycie strace lub po prostu przeszukiwanie kodu), możesz otworzyć projekt w Eclipse (lub innym IDE) i ustawić punkt przerwania dla funkcji, która tworzy te gniazda. Kiedy punkt przerwania zostanie trafiony, możesz spojrzeć na ślad stosu. Ten wyciek deskryptora pliku może być zwykłą nieskończoną pętlą lub może wartość limitu czasu gniazda jest zbyt duża. Inną możliwością jest to, że aplikacja Java nie wykonuje socket.close()czyszczenia połączeń. Zamykanie jest zwykle wykonywane w finelybloku try/catch(Tak, gniazdo musi zawsze mieć try / catch w Javie, inaczej się nie zbuduje :). Pod koniec dnia jest prawdopodobne, że aplikacja Java nie obsługuje poprawnie wyjątku IOException.

Wieża
źródło
Dziękuję za odpowiedź. Właściwie tworzę tę aplikację - część kontenerową - zamiast tylko nią zarządzać, i nie mogłem znaleźć żadnych problemów związanych z niezamknięciem gniazd. Ale wskazówka wireshark / twireshark jest dobra, użyję tego.
Robert Munteanu
@Robert Munteanu Jeśli budujesz tę aplikację, jest to pytanie dotyczące stackoverflow. Niemniej jednak otwierasz zbyt wiele gniazd.
Rook
Rook: Zrezygnowałem z odkrycia tego pod względem kodu i próbowałem wyśledzić to jako sysadmin. Dlatego opublikowałem na SF. I tak, wiem, że jakoś zbyt wiele gniazd jest otwartych. Ale nie ma żadnych wskazówek, gdzie ...
Robert Munteanu,
@Robert Munteanu Musisz ustawić punkty przerwania podczas tworzenia gniazda i spojrzeć na ślad stosu i pamięć w tym punkcie. Podejrzewam, że wpadasz w nieskończoną pętlę. Będąc w stanie spojrzeć na dowolną zmienną i wykonać krok, Twój kod będzie najlepszym podejściem do złożonych problemów takich jak ten.
Rook
Rook niestety dzieje się to na pozór losowo na jednym z 20 serwerów - nie zawsze tak samo - tylko w środowiskach produkcyjnych i być może dwa razy w tygodniu. W przeciwnym razie byłoby to dość proste. Obecnie używam Byteman ( jboss.org/byteman ) do śledzenia tworzenia gniazd / wiązania / łączenia / zamykania połączeń. Mam nadzieję, że coś z tego wyjdzie.
Robert Munteanu,