Możliwa przyczyna kodów błędów NGINX 499

117

Otrzymuję wiele kodów błędów 499 NGINX. Widzę, że jest to problem po stronie klienta. To nie jest problem z NGINX lub moim stosem uWSGI. Zwracam uwagę na korelację w dziennikach uWSGI, gdy otrzymuję 499.

address space usage: 383692800 bytes/365MB} {rss usage: 167038976
bytes/159MB} [pid: 16614|app: 0|req: 74184/222373] 74.125.191.16 ()
{36 vars in 481 bytes} [Fri Oct 19 10:07:07 2012] POST /bidder/ =>
generated 0 bytes in 8 msecs (HTTP/1.1 200) 1 headers in 59 bytes (1
switches on core 1760)
SIGPIPE: writing to a closed pipe/socket/fd (probably the client
disconnected) on request /bidder/ (ip 74.125.xxx.xxx) !!!
Fri Oct 19 10:07:07 2012 - write(): Broken pipe [proto/uwsgi.c line
143] during POST /bidder/ (74.125.xxx.xxx)
IOError: write error

Szukam bardziej dogłębnego wyjaśnienia i mam nadzieję, że nie ma nic złego w mojej konfiguracji NGINX dla uwsgi. Biorę to za wartość nominalną. Wygląda na to, że jest to problem klienta.

Tampa
źródło
Czy kiedykolwiek znalazłeś rozwiązanie tego problemu? Widzę dokładnie ten sam problem zarówno w przypadku uWSGI, jak i nginx.
Raj
1
Dostaję to, kiedy przerywam żądanie jQuery ajax.
mpen
1
Wiem, że to bardzo stare pytanie, ale ilość błędnie umieszczonych pytań na SO jest oszałamiająca. To wyraźnie należy do SF.
Sosukodo

Odpowiedzi:

165

HTTP 499 w Nginx oznacza, że klient zamknął połączenie, zanim serwer odpowiedział na żądanie. Z mojego doświadczenia wynika, że ​​jest to zwykle spowodowane przekroczeniem limitu czasu po stronie klienta . Jak wiem, jest to kod błędu specyficzny dla Nginx.

mrbo
źródło
1
Jako szczególny przypadek zauważyłem, że czasami zdarza się to, gdy użytkownik końcowy dwukrotnie klika przycisk przesyłania formularza. Formularz jest wysyłany dwukrotnie, ale klient oczekuje tylko jednej odpowiedzi. Można to obejść, wyłączając (przynajmniej na kilka sekund) przyciski w JS po ich pierwszym kliknięciu.
Antoine Pinsard
14
Należy zauważyć, że „klient” może w rzeczywistości być proxy. Na przykład, jeśli używasz modułu równoważenia obciążenia, może on anulować żądanie do serwera nginx z powodu przekroczenia limitu czasu.
Brad Koch
Dzieje się tak w mojej aplikacji Angular, jeśli użytkownik zamknie kartę, a moje żądania API nie zostaną ukończone.
Vivek Saurabh
Należy pamiętać, że może to być również spowodowane przez serwer ; jeśli odpowiedź serwera trwa zbyt długo, klient się poddaje.
ijoseph
78

W moim przypadku byłem niecierpliwy i źle zinterpretowałem dziennik.

W rzeczywistości prawdziwym problemem była komunikacja między nginx i uwsgi, a nie między przeglądarką a nginx. Gdybym załadował stronę w przeglądarce i czekał wystarczająco długo, uzyskałbym „504 - Bad Gateway”. Ale trwało to tak długo, że próbowałem różnych rzeczy, a potem odświeżałem w przeglądarce. Więc nigdy nie czekałem wystarczająco długo, aby zobaczyć błąd 504. Podczas odświeżania w przeglądarce oznacza to, że poprzednie żądanie jest zamykane, a Nginx zapisuje to w dzienniku jako 499.

Opracowanie

Tutaj założę, że czytelnik wie tak mało, jak ja, kiedy zacząłem się bawić.

Moja konfiguracja była odwrotnym proxy, serwerem nginx i serwerem aplikacji, stojącym za nim serwerem uWSGI. Wszystkie żądania od klienta trafiały do ​​serwera nginx, a następnie przekazywane do serwera uWSGI, a następnie odpowiedź była wysyłana w ten sam sposób. Myślę, że tak wszyscy używają nginx / uwsgi i powinni go używać.

Mój nginx działał tak, jak powinien, ale coś było nie tak z serwerem uwsgi. Istnieją dwa sposoby (może więcej), w których serwer uwsgi może nie odpowiedzieć serwerowi nginx.

1) uWSGI mówi: „Przetwarzam, poczekaj, a wkrótce otrzymasz odpowiedź”. nginx ma pewien okres czasu, który jest skłonny czekać, fx 20 sekund. Następnie odpowie klientowi z błędem 504.

2) uWSGI jest martwy lub uWSGi umiera, podczas gdy nginx na to czeka. nginx widzi to od razu iw takim przypadku zwraca błąd 499.

Testowałem moją konfigurację, wysyłając żądania w kliencie (przeglądarce). W przeglądarce nic się nie działo, po prostu wisiało. Po około 10 sekundach (mniej niż upłynął limit czasu) stwierdziłem, że coś jest nie tak (co było prawdą) i zamknąłem serwer uWSGI z wiersza poleceń. Następnie przeszedłem do ustawień uWSGI, spróbowałem czegoś nowego, a następnie ponownie uruchomiłem serwer uWSGI. W momencie zamknięcia serwera uWSGI serwer nginx zwróciłby błąd 499.

Więc nadal debugowałem z błędem 499, co oznacza szukanie w Google błędu 499. Ale gdybym czekał wystarczająco długo, dostałbym błąd 504. Gdybym miał błąd 504, byłbym w stanie lepiej zrozumieć problem, a następnie być w stanie debugować.

A więc wniosek jest taki, że problem był z uWGSI, który ciągle się zawieszał („Poczekaj jeszcze trochę, jeszcze trochę, wtedy będę miał dla ciebie odpowiedź…”).

Jak rozwiązałem ten problem, nie pamiętam. Myślę, że może to być spowodowane wieloma rzeczami.

Mads Skjern
źródło
1
Jak ostatecznie to rozwiązałeś? Mam ten sam problem i nie byłem w stanie określić przyczyny.
Colin Nichols,
1
Dodałem niestety opracowanie, nie sądzę, że rozwiąże to twój problem.
Mads Skjern,
1
Chciałem tylko podziękować! Miałem dokładnie taką samą sytuację i to postawiło mnie na właściwej drodze.
Aaron,
3
@Shafiul: Moje opracowanie nie wyjaśnia, co spowodowało problem z uWSGI, po prostu wyjaśnia, że ​​przyczyną był uWSGI (a nie nginx). W artykule opisano objawy i sposób ich błędnej interpretacji. Rozumiem twoje rozczarowanie, ale źle zrozumiałeś istotę mojej odpowiedzi. Szczerze.
Mads Skjern,
2
Niezwykle przydatna odpowiedź, nigdy nie usuwaj! Te koncepcje powinny zostać rozwinięte gdzieś w dokumentacji, robisz wielką przysługę, wyjaśniając, jak zachowuje się inaczej, niż sugerowałyby to dokumenty!
jerclarke
21

Klient zamknął połączenie nie oznacza, że ​​jest to problem z przeglądarką !? Ani trochę!

Możesz znaleźć 499 błędów w pliku dziennika, jeśli masz LB (load balancer) przed swoim serwerem internetowym (nginx) albo AWS, albo haproxy (niestandardowy). To powiedziawszy, LB będzie działać jako klient nginx.

Jeśli uruchomisz haproxy domyślne wartości dla:

    timeout client  60000
    timeout server  60000

Oznaczałoby to, że LB wygaśnie po 60000 ms, jeśli nie ma odpowiedzi od nginx. W przypadku zajętych witryn internetowych lub skryptów, które wymagają więcej czasu na wykonanie, mogą wystąpić przerwy. Musisz znaleźć czas, który będzie dla Ciebie odpowiedni. Na przykład rozszerz go na:

    timeout client  180s
    timeout server  180s

I prawdopodobnie będziesz ustawiony.

W zależności od konfiguracji, w przeglądarce może pojawić się błąd przekroczenia limitu czasu bramy 504, który wskazuje, że coś jest nie tak z php-fpm, ale nie będzie to miało miejsca w przypadku błędów 499 w plikach dziennika.

mrki
źródło
12

Gdy 499wskażesz przerwanie połączenia zarejestrowane przez nginx. Ale zwykle dzieje się tak, gdy serwer zaplecza działa zbyt wolno i najpierw upływa inny limit czasu serwera proxy lub oprogramowanie użytkownika przerywa połączenie. Sprawdź więc, czy uWSGI odpowiada szybko, czy nie, czy jest jakieś obciążenie na serwerze uWSGI / bazy danych.

W wielu przypadkach istnieją inne serwery proxy między użytkownikiem a nginx. Niektóre z nich mogą znajdować się w Twojej infrastrukturze, na przykład CDN, Load Balacer, pamięć podręczna Varnish itp. Inne mogą znajdować się po stronie użytkownika, np. Buforujący serwer proxy itp.

Jeśli po Twojej stronie są serwery proxy, takie jak LoadBalancer / CDN ... powinieneś ustawić limity czasu, aby najpierw ustawić swój backend, a stopniowo inne serwery proxy dla użytkownika.

Jeśli masz:

user >>> CDN >>> Load Balancer >>> Nginx >>> uWSGI

Polecam ustawić:

  • n sekund do przekroczenia limitu czasu uWSGI
  • n+1 sekund do przekroczenia limitu czasu nginx
  • n+2 senconds do przekroczenia limitu czasu do modułu równoważenia obciążenia
  • n+3 sekund limitu czasu do CDN.

Jeśli nie możesz ustawić niektórych limitów czasu (takich jak CDN), znajdź jego limit czasu i dostosuj inne zgodnie z nim ( n, n-1...).

Zapewnia to prawidłowy łańcuch limitów czasu. a dowiesz się, czy naprawdę podał limit czasu i zwrócisz właściwy kod odpowiedzi do użytkownika.

bartomeu
źródło
8

W moim przypadku otrzymałem 499, gdy API klienta zamknęło połączenie, zanim otrzyma jakąkolwiek odpowiedź. Dosłownie wysłał POST i natychmiast zamknął połączenie. Można to rozwiązać za pomocą opcji:

proxy_ignore_client_abort on

Nginx doc

DerSkythe
źródło
3
Nie rozumiem, jak to pomaga
Vladimir Starkov
Może to nie twoja sprawa? Klient przesyła dane i nie jest zainteresowany tym, co się z nim stanie i jaka będzie odpowiedź. Ale moja aplikacja powinna przetwarzać dane. Bez tej opcji dane po prostu nie mają czasu na dotarcie do mojej aplikacji.
DerSkythe
Dziękuję Ci. Dokładne objawy i doskonałe rozwiązanie.
TTimo,
Whoa! To prawie dokładnie to, czego potrzebuję. Jedyną rzeczą, którą bym dodał - byłoby wysłanie odpowiedzi 200 do źródła webhooka trochę przed zamknięciem samego połączenia. W przeciwnym razie mają tendencję do wyłączania webhooków i nie wysyłania ich ponownie… Czy mogę to zrobić dla wybranych adresów URL?
pilat
1
Nie rozwiązuje to problemu braku odpowiedzi klienta. Eliminuje tylko 499 błędów w dziennikach i zastępuje je kodem stanu 200. Zły pomysł, aby to zrobić. Prawdziwym rozwiązaniem jest powiedzenie klientowi, aby zwiększył ustawienie limitu czasu ...
marcinx
7

Okazuje się, że liczba 499 naprawdę oznacza „przerwanie połączenia przez klienta”.

Miałem limit czasu odczytu klienta wynoszący 60 sekund (a nginx ma również domyślną wartość proxy_read_timeout wynoszącą 60 sekund). Więc to, co działo się w moim przypadku, to fakt, że nginx wyrzuciłby error.log an, upstream timed out (110: Connection timed out) while reading upstreama następnie nginx ponawia próbę „następnego serwera proxy w skonfigurowanej grupie serwerów zaplecza”. To znaczy, jeśli masz więcej niż jeden.

Następnie próbuje następną i następną, aż ( domyślnie ) wyczerpała je wszystkie. Po przekroczeniu limitu czasu usuwa je również z listy „aktywnych” serwerów zaplecza. Po wyczerpaniu wszystkich zwraca a504 gateway timeout.

Więc w moim przypadku nginx oznaczył serwer jako „niedostępny”, spróbował go ponownie na następnym serwerze, a następnie 60swystąpił limit czasu mojego klienta (natychmiast), więc zobaczyłem upstream timed out (110: Connection timed out) while reading upstreamdziennik, a zaraz po nim dziennik 499. Ale to był tylko zbieg okoliczności.

Związane z:

Jeśli wszystkie serwery w grupie są oznaczone jako obecnie niedostępne, zwraca również 502 Bad Gateway.przez 10 sekund. Zobacz tutaj max_fails i fail_timeout. Podaj dzienniki, które powieno live upstreams while connecting to upstream.

Jeśli masz tylko jeden serwer proxy w swojej grupie serwerów, po prostu spróbuj użyć jednego serwera i zwraca a 504 Gateway Time-outi nie usuwa pojedynczego serwera z listy serwerów aktywnych, jeśli proxy_read_timeoutzostanie przekroczony. Zobacz tutaj „Jeśli w grupie jest tylko jeden serwer, parametry max_fails, fail_timeout i slow_start są ignorowane, a taki serwer nigdy nie będzie uważany za niedostępny.”

Naprawdę trudną częścią jest to, że jeśli określisz proxy_pass na "localhost", a twoje pudełko ma jednocześnie "wersje lokalizacji" ipv6 i ipv4 (większość pól ma to domyślnie), będzie się liczyć tak, jakbyś miał "lista" wielu serwerów w twojej grupie serwerów, co oznacza, że ​​możesz dostać się do powyższej sytuacji, w której zwraca "502 przez 10s", nawet jeśli podajesz tylko jeden serwer . Zobacz tutaj „Jeśli nazwa domeny obejmuje kilka adresów, wszystkie z nich będą używane w sposób okrężny”. Jednym ze sposobów obejścia tego problemu jest zadeklarowanie go jako proxy_pass http://127.0.0.1:5001;(jego adresu IPv4), aby nie był to jednocześnie ipv6 i ipv4. Wtedy liczy się to jako zachowanie „tylko jednego serwera”.

Istnieje kilka różnych ustawień, które można dostosować, aby „zmniejszyć” to problem. Na przykład zwiększanie limitów czasu lub ustawianie go tak, aby nie oznaczał serwerów jako „wyłączonych” po przekroczeniu limitu czasu ... lub naprawianie listy, aby miała tylko rozmiar 1, patrz powyżej :)

Zobacz też: https://serverfault.com/a/783624/27813

rogerdpack
źródło
3

Ten błąd jest dość łatwy do odtworzenia przy użyciu standardowej konfiguracji nginx z php-fpm.

Przytrzymanie przycisku F5 na stronie spowoduje utworzenie dziesiątek żądań odświeżenia do serwera. Każde poprzednie żądanie jest anulowane przez przeglądarkę przy nowym odświeżaniu. W moim przypadku znalazłem dziesiątki 499 w pliku dziennika sklepu internetowego mojego klienta. Z punktu widzenia nginx: jeśli odpowiedź nie została dostarczona klientowi przed następnym żądaniem odświeżenia, nginx rejestruje błąd 499.

mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:32 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:33 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:33 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:33 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:33 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:34 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:34 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:34 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:34 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:35 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:35 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)

Jeśli przetwarzanie php-fpm trwa dłużej (jak ciężka strona WP), może to oczywiście powodować problemy. Słyszałem na przykład o awariach php-fpm, ale uważam, że można im zapobiec w prawidłowej konfiguracji usług, takich jak obsługa wywołań xmlrpc.php.

karvonen
źródło
2

... przyszedł tutaj z wyszukiwarki Google

Znalazłem odpowiedź gdzie indziej tutaj -> https://stackoverflow.com/a/15621223/1093174

co miało zwiększyć limit czasu bezczynności połączenia mojego elastycznego modułu równoważenia obciążenia AWS!

(Skonfigurowałem witrynę Django z odwrotnym proxy nginx / apache i naprawdę, naprawdę, naprawdę, naprawdę bardzo mi się skończył logować zadanie / widok zaplecza)

David Lam
źródło
1

Wiem, że to stary wątek, ale dokładnie pasuje do tego, co ostatnio mi się przytrafiło i pomyślałem, że udokumentuję to tutaj. Konfiguracja (w Dockerze) wygląda następująco:

  • nginx_proxy
  • nginx
  • php_fpm z uruchomioną aplikacją.

Objawem był „Limit czasu bramy 502” w monicie logowania do aplikacji. Analiza znalezionych logów:

  • przycisk działa przez HTTP POSTdo /login... i tak ...
  • nginx-proxy odebrał /loginżądanie i ostatecznie zgłosił przekroczenie limitu czasu.
  • nginx zwrócił 499odpowiedź, co oczywiście oznacza „host zmarł”.
  • /loginprośba nie pojawiają się na wszystkich (!) w logach serwera FPM głównej!
  • w FPM nie było żadnych śledzenia ani komunikatów o błędach ... nada, zero, zippo, none.

Okazało się, że problemem był brak połączenia z bazą danych w celu weryfikacji logowania. Ale jak to zrozumieć, okazało się czystym domysłem.

Całkowity brak dzienników śledzenia aplikacji ... lub nawet zapisu, że żądanie zostało odebrane przez FPM ... był dla mnie całkowitym (i druzgocącym ...) zaskoczeniem. Tak, aplikacja ma rejestrować awarie, ale w tym przypadku wygląda na to, że proces roboczy FPM zgasł z powodu błędu uruchomieniowego, co doprowadziło do 499odpowiedzi z nginx. To oczywiście jest problem w naszej aplikacji ... gdzieś. Ale chciałem nagrać szczegóły tego, co się stało, z korzyścią dla następnych ludzi, którzy mają do czynienia z czymś takim.

Mike Robinson
źródło
0

Gdy otrzymałem 499 „Żądanie zostało zabronione przez program antywirusowy” jako odpowiedź http AJAX (fałszywie pozytywny wynik testu Kaspersky Internet Security z lekką analizą heurystyczną, głęboka analiza heurystyczna wiedziała, że ​​nie ma nic złego).

TeeJay
źródło
0

Napotkałem ten problem, a przyczyną była wtyczka Kaspersky Protection w przeglądarce. Jeśli napotykasz ten problem, spróbuj wyłączyć wtyczki i sprawdź, czy to rozwiąże problem.

EGN
źródło
0

Jednym z powodów tego zachowania może być to, że używasz httpfor uwsgizamiast socket. Użyj polecenia poniżej, jeśli używasz uwsgibezpośrednio.

uwsgi --socket :8080 --module app-name.wsgi

To samo polecenie w pliku .ini jest

chdir = /path/to/app/folder
socket = :8080
module = app-name.wsgi
Penkey Suresh
źródło
0

To nie odpowiada na pytanie PO, ale ponieważ znalazłem się tutaj po wściekłym poszukiwaniu odpowiedzi, chciałem podzielić się tym, co odkryliśmy.

W naszym przypadku okazuje się, że te 499 są oczekiwane. Na przykład, gdy użytkownicy używają funkcji automatycznego uzupełniania w niektórych polach wyszukiwania, w dziennikach widzimy coś takiego.

GET /api/search?q=h [Status 499] 
GET /api/search?q=he [Status 499]
GET /api/search?q=hel [Status 499]
GET /api/search?q=hell [Status 499]
GET /api/search?q=hello [Status 200]

Więc w naszym przypadku myślę, że jest bezpieczny w użyciu, proxy_ignore_client_abort onco zostało zasugerowane w poprzedniej odpowiedzi. Dziękuję za to!

Ron DeFulio
źródło
0

Ze swojej strony włączyłem, ufwale zapomniałem ujawnić moich portów upstreams ._.

Alexandre Daubricourt
źródło
0

W moim przypadku mam taką konfigurację

AWS ELB >> ECS(nginx) >> ECS(php-fpm).

Skonfigurowałem niewłaściwą grupę bezpieczeństwa AWS dla usługi ECS (php-fpm), więc Nginx nie był w stanie dotrzeć do kontenera zadań php-fpm. Dlatego otrzymywałem błędy w dzienniku zadań Nginx

499 0 - elb-healthchecker/2.0

Sprawdzanie stanu zostało skonfigurowane tak, aby sprawdzić usługę php-fpm, potwierdzić, że działa i udzielić odpowiedzi.

Piyush Sonigra
źródło