Gibt es eine Möglichkeit, Nginx dazu zu bringen, mich zu benachrichtigen, wenn Treffer eines Empfehlers einen Schwellenwert überschreiten?
Beispiel: Wenn meine Website bei Slashdot vorgestellt wird und plötzlich in einer Stunde 2.000 Treffer eingehen, möchte ich benachrichtigt werden, wenn mehr als 1.000 Treffer pro Stunde erzielt werden.
Wird es möglich sein, dies in Nginx zu tun? Möglicherweise ohne lua? (da mein prod nicht lua kompiliert ist)
Antworten:
Die effizienteste Lösung könnte darin bestehen, einen Daemon zu schreiben, würde
tail -f
dasaccess.log
, und Spur des halten$http_referer
Feld.Eine schnelle und schmutzige Lösung wäre jedoch, eine zusätzliche
access_log
Datei hinzuzufügen , nur die$http_referer
Variable mit einem benutzerdefinierten Protokoll zu protokollierenlog_format
und das Protokoll automatisch alle X Minuten zu drehen.Dies kann mithilfe von Standard-Logrotate-Skripten erreicht werden, die möglicherweise ordnungsgemäße Neustarts von Nginx durchführen müssen, damit die Dateien erneut geöffnet werden können (z. B. die Standardprozedur, sehen Sie sich für eine einfache Zeit / a / 15183322 auf SO an. basiertes Skript)…
Oder indem Sie Variablen innerhalb verwenden
access_log
, möglicherweise indem Sie die Minutenangabe$time_iso8601
mithilfe dermap
oder einerif
Direktive herausholen (je nachdem, wo Sie Ihre platzieren möchtenaccess_log
).Mit dem oben Gesagten können Sie also 6 Protokolldateien haben, die jeweils einen Zeitraum von 10 Minuten abdecken
http_referer.Txx{0,1,2,3,4,5}x.log
, z. B. indem Sie die erste Ziffer der Minute abrufen, um jede Datei zu unterscheiden.Jetzt müssen Sie nur noch ein einfaches Shell-Skript
cat
erstellen, das alle 10 Minuten ausgeführt werden kann. Alle oben genannten Dateien werden zusammen geleitet,sort
anuniq -c
, ansort -rn
, anhead -16
und weitergeleitet, und Sie haben eine Liste der 16 häufigstenReferer
Variationen - Sie können frei entscheiden, ob Kombinationen von Zahlen und Feldern Ihre Kriterien überschreiten, und eine Benachrichtigung durchführen.Anschließend können Sie nach einer einzigen erfolgreichen Benachrichtigung alle diese 6 Dateien entfernen und in nachfolgenden Läufen keine Benachrichtigung ausgeben, es sei denn, alle sechs Dateien sind vorhanden (und / oder eine bestimmte andere Nummer, wie Sie es für richtig halten).
quelle
Ich denke, das wäre mit Logtail und Grep weitaus besser. Selbst wenn es möglich ist, mit lua inline zu arbeiten, möchten Sie diesen Overhead nicht für jede Anfrage und insbesondere nicht, wenn Sie einen Slashdotted erhalten haben.
Hier ist eine 5-Sekunden-Version. Stecke es in ein Skript und füge etwas lesbareren Text hinzu, und du bist golden.
Das ignoriert natürlich völlig reddit.com und facebook.com und all die Millionen anderer Websites, die Ihnen viel Verkehr schicken könnten. Ganz zu schweigen von 100 verschiedenen Websites, die Ihnen jeweils 20 Besucher senden. Sie sollten wahrscheinlich nur einen einfachen alten Verkehrsschwellenwert haben , der dazu führt, dass eine E-Mail an Sie gesendet wird, unabhängig vom Referrer.
quelle
-o
Option gilt für eine Offset-Datei, damit sie weiß, wo sie beim nächsten Mal mit dem Lesen beginnen soll.Die Direktive nginx limit_req_zone kann ihre Zonen auf einer beliebigen Variablen basieren, einschließlich $ http_referrer.
Sie sollten jedoch auch etwas tun, um den auf dem Webserver erforderlichen Status zu begrenzen, da die Referrer-Header sehr lang und unterschiedlich sein können und möglicherweise eine unendliche Vielfalt angezeigt wird. Mit der Funktion nginx split_clients können Sie eine Variable für alle Anforderungen festlegen , die auf dem Hash des Referrer-Headers basiert. Das folgende Beispiel verwendet nur 10 Böcke, aber Sie können es genauso einfach mit 1000 machen. Wenn Sie also einen Schrägstrich erhalten, werden auch Personen blockiert, deren Verweis zufällig in denselben Bucket wie die Slashdot-URL gehasht wurde. Sie können dies jedoch auf 0,1% der Besucher beschränken, indem Sie 1000 Buckets in split_clients verwenden.
Es würde ungefähr so aussehen (völlig ungetestet, aber richtungsrichtig):
quelle
split_clients
möglicherweise falsch informiert ist -limit_req
basiert auf einem "undichten Eimer", was bedeutet, dass der Gesamtzustand niemals die Größe der angegebenen Zone überschreiten sollte.Ja, natürlich ist das in NGINX möglich!
Sie können den folgenden DFA implementieren :
Implementieren Sie eine Ratenbegrenzung, basierend auf der
$http_referer
Verwendung von Regex durch amap
, um die Werte zu normalisieren. Wenn das Limit überschritten wird, wird eine interne Fehlerseite ausgelöst, die Sie gemäß einer verwandten Frage über einenerror_page
Handler abrufen können und die als interne Umleitung an einen neuen internen Speicherort wechselt (für den Client nicht sichtbar).An der oben genannten Stelle für überschrittene Grenzwerte führen Sie eine Warnanforderung aus, sodass die externe Logik die Benachrichtigung ausführt. Diese Anfrage wird anschließend zwischengespeichert, um sicherzustellen, dass Sie nur 1 eindeutige Anfrage pro Zeitfenster erhalten.
Fangen Sie den HTTP-Statuscode der vorherigen Anforderung ab (indem Sie einen Statuscode ≥ 300 zurückgeben und
proxy_intercept_errors on
den nicht standardmäßig nicht erstellten Standardcode verwendenauth_request
oder alternativadd_after_body
eine "kostenlose" Unteranforderung erstellen) und schließen Sie die ursprüngliche Anforderung so ab, als ob Der vorherige Schritt war nicht beteiligt. Beachten Sie, dass wir die rekursiveerror_page
Behandlung aktivieren müssen, damit dies funktioniert.Hier ist mein PoC und ein MVP, ebenfalls unter https://github.com/cnst/StackOverflow.cnst.nginx.conf/blob/master/sf.432636.detecting-slashdot-effect-in-nginx.conf :
Beachten Sie, dass dies wie erwartet funktioniert:
Sie können sehen, dass die erste Anforderung erwartungsgemäß zu einem Front-End- und einem Back-End-Treffer führt (ich musste dem Speicherort ein Dummy-Back-End hinzufügen , bei dem ein echtes Back -End nicht erforderlich ist
limit_req
, da areturn 200
Vorrang vor den Grenzwerten hat für den Rest der Handhabung).Die zweite Anforderung liegt über dem Grenzwert, daher senden wir die Warnung (Abrufen
200
) und zwischenspeichern sie und geben sie zurück429
(dies ist aufgrund der oben genannten Einschränkung erforderlich, dass Anforderungen unter 300 nicht abgefangen werden können), die anschließend vom Front-End abgefangen wird , die jetzt frei ist, frei zu tun, was sie will.Die dritte Anforderung überschreitet immer noch das Limit, aber wir haben die Warnung bereits gesendet, sodass keine neue Warnung gesendet wird.
Erledigt! Vergiss nicht, es auf GitHub zu teilen!
quelle
limit_req
und der andere ein istlimit_conn
, dann benutze einfach denlimit_req_status 429
obigen (erfordert sehr neuen Nginx), und ich denke, du solltest golden sein; Möglicherweise gibt es andere Optionen (eine, die sicher funktioniert, ist die Verkettung von Nginx mitset_real_ip_from
, aber je nachdem, was genau Sie tun möchten, gibt es möglicherweise effizientere Optionen).golang
oder die Timeout-Optionen für Upstreams prüfen. Vielleicht möchten Sie auch verwendenproxy_cache_lock on
und möglicherweise eine Fehlerbehandlung hinzufügen, um zu tun, was zu tun ist, wenn das Skript fehlschlägt (z.error_page
B. auch undproxy_intercept_errors
erneut). Ich vertraue darauf, dass mein POC ein guter Start ist. :)limit_req
/ verwendetlimit_conn
? Stellen Sie beispielsweise die obige Konfiguration einfach vor Ihren aktuellen Front-End-Server. Sie könnenset_real_ip_from
in Upstream-Nginx verwenden, um sicherzustellen, dass IPs auf der ganzen Linie korrekt berücksichtigt werden. Sonst, wenn es immer noch nicht passt, müssen Sie Ihre genauen Einschränkungen und die Spezifikation anschaulicher formulieren - über welches Verkehrsniveau sprechen wir? Wie oft muss die Statistik ausgeführt werden (1 Minute / 5 Minuten / 1 Stunde)? Was ist los mit der altenlogtail
Lösung?