Czy jest sposób, aby zmusić Nginx do powiadamiania mnie, jeśli trafienia od strony odsyłającej przekroczą próg?
np. jeśli moja strona internetowa jest prezentowana na Slashdot i nagle mam godzinę 2 000 wyświetleń w ciągu godziny, chcę otrzymywać powiadomienia, gdy liczba wyświetleń przekroczy 1 000 godzin.
Czy będzie to możliwe w Nginx? Być może bez lua? (ponieważ mój prod nie jest skompilowany lua)
Odpowiedzi:
Najbardziej skutecznym rozwiązaniem może być napisać demona, który i śledzić na polu.
tail -f
access.log
$http_referer
Jednak szybkim i brudnym rozwiązaniem byłoby dodanie dodatkowego
access_log
pliku, rejestrowanie tylko$http_referer
zmiennej za pomocą niestandardowejlog_format
i automatyczne obracanie dziennika co X minut.Można tego dokonać za pomocą standardowych skryptów logrotate, które mogą wymagać płynnego ponownego uruchomienia nginx w celu ponownego otwarcia plików (np. Standardowa procedura, spójrz na / a / 15183322 na SO przez prosty czas- oparty na skrypcie)…
Lub, używając zmiennych wewnątrz
access_log
, być może poprzez wyciągnięcie najdrobniejszej specyfikacji$time_iso8601
za pomocą dyrektywymap
lubif
(w zależności od tego, gdzie chcesz umieścićaccess_log
).Tak więc, z powyższym, możesz mieć 6 plików dziennika, z których każdy obejmuje okres 10 minut
http_referer.Txx{0,1,2,3,4,5}x.log
, np. Poprzez uzyskanie pierwszej cyfry minuty w celu odróżnienia każdego pliku.Teraz wystarczy prosty skrypt powłoki, który mógłby uruchamiać się co 10 minut,
cat
wszystkie powyższe pliki razem, potokować dosort
, potokować douniq -c
, dosort -rn
, dohead -16
i masz listę 16 najczęstszychReferer
odmian - swobodnie decyduj, czy dowolne kombinacje liczb i pól przekraczają twoje kryteria, i dokonaj powiadomienia.Następnie, po pojedynczym pomyślnym powiadomieniu, możesz usunąć wszystkie te 6 plików, aw kolejnych uruchomieniach nie wydawać żadnego powiadomienia, chyba że wszystkie sześć plików jest obecnych (i / lub pewna inna liczba, którą uważasz za stosowną).
źródło
Myślę, że lepiej byłoby to zrobić przy logtailu i grep. Nawet jeśli jest to możliwe w przypadku lua inline, nie chcesz tego narzutu dla każdego żądania, a szczególnie nie chcesz go, gdy jesteś Slashdotted.
Oto 5-sekundowa wersja. Umieść go w skrypcie i umieść wokół niego bardziej czytelny tekst, a będziesz złoty.
Oczywiście całkowicie to ignoruje reddit.com i facebook.com oraz wszystkie miliony innych witryn, które mogą generować duży ruch. Nie wspominając o 100 różnych witrynach, z których każda odwiedza 20 osób. Prawdopodobnie powinieneś mieć zwykły stary próg ruchu, który spowoduje wysłanie wiadomości e-mail, niezależnie od strony polecającej.
źródło
-o
Opcja dotyczy pliku przesunięcia, aby wiedział, od czego zacząć czytanie następnym razem.Dyrektywa nginx limit_req_zone może opierać swoje strefy na dowolnej zmiennej, w tym $ http_referrer.
Będziesz także chciał zrobić coś, aby ograniczyć wymagany stan na serwerze WWW, ponieważ nagłówki strony odsyłającej mogą być dość długie i zróżnicowane, a może pojawić się nieskończona odmiana. Za pomocą funkcji split_clients nginx można ustawić zmienną dla wszystkich żądań opartą na haszu nagłówka strony odsyłającej. Poniższy przykład wykorzystuje tylko 10 bucków, ale równie łatwo możesz to zrobić z 1000. Więc jeśli zostaniesz slashdotowany, osoby, które skierowały się do hashowania do tego samego segmentu, co adres URL slashdot, również zostaną zablokowane, ale możesz ograniczyć to do 0,1% odwiedzających, używając 1000 segmentów w split_clients.
Wyglądałoby to mniej więcej tak (całkowicie nie przetestowane, ale poprawne kierunkowo):
źródło
split_clients
może być błędne -limit_req
opiera się na „nieszczelnym pojemniku”, co oznacza, że ogólny stan nigdy nie powinien przekraczać wielkości określonej strefy.Tak, oczywiście jest to możliwe w NGINX!
Co możesz zrobić, to wdrożyć następujący DFA :
Zaimplementuj ograniczenie szybkości, oparte na
$http_referer
ewentualnym użyciu wyrażeń regularnych poprzez a wmap
celu normalizacji wartości. Po przekroczeniu limitu pojawia się strona błędu wewnętrznego, którą można przechwycić przezerror_page
moduł obsługi zgodnie z powiązanym pytaniem , przechodząc do nowej lokalizacji wewnętrznej jako przekierowanie wewnętrzne (niewidoczne dla klienta).W powyższej lokalizacji dla przekroczonych limitów wykonujesz żądanie alertu, pozwalając zewnętrznej logice wykonać powiadomienie; to żądanie jest następnie buforowane, zapewniając, że otrzymasz tylko 1 unikalne żądanie w danym oknie czasowym.
Złap kod stanu HTTP z poprzedniego żądania (zwracając kod stanu ≥ 300 i używając
proxy_intercept_errors on
lub, alternatywnie, skorzystaj z domyślnie niezbudowanegoauth_request
lubadd_after_body
zrób „bezpłatne” żądanie) i wypełnij pierwotne żądanie, jakby poprzedni krok nie był zaangażowany. Pamiętaj, że abyerror_page
to zadziałało , musimy włączyć obsługę rekurencyjną .Oto mój PoC i MVP, również na https://github.com/cnst/StackOverflow.cnst.nginx.conf/blob/master/sf.432636.detecting-slashdot-effect-in-nginx.conf :
Pamiętaj, że działa to zgodnie z oczekiwaniami:
Widać, że pierwsze żądanie skutkuje jednym trafieniem frontonu i jednego backendu, zgodnie z oczekiwaniami (musiałem dodać fikcyjny backend do lokalizacji, która ma
limit_req
, ponieważ ponieważreturn 200
miałby pierwszeństwo przed limitami, prawdziwy backend nie jest konieczny do końca obsługi).Drugie żądanie jest powyżej limitu, więc wysyłamy alert (otrzymujemy
200
) i buforujemy go, zwracając429
(jest to konieczne ze względu na wspomniane ograniczenie, że żądania poniżej 300 nie mogą zostać przechwycone), które są następnie przechwytywane przez interfejs , który jest teraz wolny i może robić, co chce.Trzecie żądanie wciąż przekracza limit, ale już wysłaliśmy alert, więc żaden nowy alert nie zostanie wysłany.
Gotowy! Nie zapomnij rozwidlić go na GitHub!
źródło
limit_req
, a drugi tolimit_conn
, po prostu użyjlimit_req_status 429
powyższego (wymaga bardzo nowego nginx) i myślę, że powinieneś być złoty; mogą istnieć inne opcje (jedną z nich na pewno jest połączenie nginx w /set_real_ip_from
, ale w zależności od tego, co dokładnie chcesz zrobić, mogą być bardziej wydajne opcje).golang
lub zajrzeć do opcji limitu czasu dla upstreams; także może chcieć również użyćproxy_cache_lock on
i ewentualnie dodać obsługę błędów, co zrobić, jeśli skrypt zawiedzie (np. przy użyciu,error_page
a takżeproxy_intercept_errors
ponownie). Ufam, że mój POC to dobry początek. :)limit_req
/limit_conn
? Na przykład po prostu umieść powyższą konfigurację przed bieżącym serwerem frontonu. Możesz użyćset_real_ip_from
w nginx w górę, aby upewnić się, że adresy IP są poprawnie rozliczane wzdłuż linii. W przeciwnym razie, jeśli nadal nie będzie pasować, myślę, że musisz precyzyjniej określić swoje ograniczenia i specyfikację - o jakich natężeniach ruchu mówimy? Jak często statystyki muszą się uruchamiać (1min / 5min / 1h)? Co jest nie tak ze starymlogtail
rozwiązaniem?