de Sébastien Portebois

Limitarea ratei NGINX pe scurt

Limitarea ratei NGINX pe scurt
Imagine de Wonderlane, pe Flickr

NGINX este minunat … dar am găsit că documentația sa privind limitarea ratei este oarecum … limitată. Așa că am scris acest ghid pentru limitarea ratei și modelarea traficului cu NGINX.

Mergem la:

  • descrie directivele NGINX
  • explica logica de acceptare / respingere a NGINX
  • vă ajută să vizualizați modul în care este procesată o adevărată explozie de trafic folosind diverse setări: limitarea ratei, politica de trafic și permiterea rafalelor mici

Ca bonus, am inclus aa Repo GitHub iar rezultatul Imagine Docker astfel încât să puteți experimenta și reproduce testele. Întotdeauna este mai ușor să înveți făcând!

Directivele NGINX privind rata-limită și rolurile acestora

Limitarea ratei NGINX pe scurt

Acest post se concentrează pe ngx_http_limit_req_module, care vă oferă limit_req_zone și limit_req directivelor. De asemenea, oferă limit_req_status și limit_req_level. Împreună, acestea vă permit să controlați codul de stare a răspunsului HTTP pentru solicitările respinse și modul în care aceste înregistrări sunt înregistrate.

Cea mai mare confuzie provine din logica respingerii.

În primul rând, trebuie să înțelegeți limit_req directivă, care are nevoie de o zone parametru și oferă, de asemenea opțional burst și nodelay parametrii.

Există mai multe concepte în joc aici:

  • zone vă permite să definiți o bucket, un „spațiu” comun în care să numărați solicitările primite. Toate cererile care intră în același compartiment vor fi numărate în aceeași limită de tarif. Aceasta este ceea ce vă permite să limitați pe URL, pe IP sau orice altceva.
  • burst este opțional. Dacă este setat, acesta definește câte cereri depășești puteți accepta peste rata de bază. Un lucru important de remarcat aici: rafala este o valoare absolută, nu este o rată.
  • nodelay este, de asemenea, opțional și este util numai atunci când setați și un burst valoare și vom vedea de ce mai jos.

Cum decide NGINX dacă o cerere este acceptată sau respinsă?

Când setați o zonă, definiți o rată, cum ar fi 300r/m pentru a permite 300 de solicitări pe minut sau 5r/s pentru a permite 5 cereri în fiecare secundă.

De exemplu:

  • limit_req_zone $request_uri zone=zone1:10m rate=300r/m;
  • limit_req_zone $request_uri zone=zone2:10m rate=5r/s;

Este important să înțelegem că aceste 2 zone au aceleași limite. rate setarea este utilizată de NGINX pentru a calcula o frecvență: care este intervalul de timp înainte de a accepta o nouă solicitare? NGINX va aplica algoritmul bucket leaky cu această rată de reîmprospătare a simbolurilor.

Pentru NGINX, 300r/m și 5r/s sunt tratate la fel: permiteți o cerere la fiecare 0,2 secunde pentru această zonă. La fiecare 0,2 secunde, în acest caz, NGINX va seta un steag pentru a-și aminti că poate accepta o cerere. Când apare o cerere care se potrivește în această zonă, NGINX setează steagul la fals și îl procesează. Dacă apare o altă solicitare înainte ca temporizatorul să bifeze, aceasta va fi respinsă imediat cu un cod de stare 503. Dacă temporizatorul bifează și steagul a fost deja setat pentru a accepta o cerere, nimic nu se schimbă.

Aveți nevoie de limitarea ratei sau de formare a traficului?

Introduceți fișierul burst parametru. Pentru a o înțelege, imaginați-vă că steagul pe care l-am explicat mai sus nu mai este un boolean, ci un număr întreg: numărul maxim de cereri pe care NGINX le poate permite într-o explozie.

Acesta nu mai este un algoritm de găleată scurgeri, ci o găleată de token. rate controlează cât de repede se bifează cronometrul, dar nu mai este un simbol adevărat / fals, ci un contor care merge 0 la 1+burst value. De fiecare dată când cronometrul bifează, contorul este incrementat, cu excepția cazului în care este deja la valoarea maximă de b+1. Acum ar trebui să înțelegeți de ce burst setarea este o valoare și nu o rată.

Când vine o nouă solicitare, NGINX verifică dacă este disponibil un jeton (adică contorul este> 0), dacă nu, solicitarea este respinsă. Dacă există un jeton, atunci cererea este acceptată și va fi tratată, iar jetonul respectiv va fi consumat (contorul este decrementat).

Ok, deci NGINX va accepta solicitarea dacă este disponibil un jeton de rafală. Dar când va procesa NGINX această solicitare?

Ați solicitat NGINX să aplice o rată maximă de 5r/s, NGINX acceptă cererile care depășesc dacă sunt disponibile jetoane de explozie, dar va aștepta un spațiu pentru a le procesa în limita maximă a ratei. Prin urmare aceste cereri de rafală vor fi procesate cu o anumită întârziere, sau vor expira.

Cu alte cuvinte, NGINX nu va depăși limita de rată stabilită în declarația de zonă și, prin urmare, va pune la coadă cererile suplimentare și le va procesa cu o anumită întârziere, pe măsură ce se primesc bifele timer-token și mai puține cereri.

Pentru a utiliza un exemplu simplu, să presupunem că aveți o rată de 1r/s, și o explozie de 3. NGINX primește 5 cereri în același timp:

  • Primul este acceptat și procesat
  • Deoarece permiteți 1 + 3, există o cerere care este respinsă imediat, cu un cod de stare 503
  • Celelalte 3 vor fi tratate, unul câte unul, dar nu imediat. Vor fi tratați cu o rată de 1r/s să rămâi în limita setului tău. Dacă nu apare nicio altă solicitare, consumând deja această cotă. Odată ce coada este goală, contorul de rafală va începe să fie din nou incrementat (cupa de jetoane începe să fie din nou umplută)

Dacă utilizați NGINX ca proxy, în amonte va primi solicitarea la o rată maximă de 1r/sși nu va fi conștient de nicio explozie de cereri primite, totul va fi limitat la acel ritm.

Tocmai ați făcut o anumită formare a traficului, introducând o anumită întârziere pentru a regla rafalele și pentru a produce un flux mai regulat în afara NGINX.

Introduceți nodelay

nodelay îi spune lui NGINX că solicitările pe care le acceptă în fereastra de rafală trebuie procesate imediat, la fel ca solicitările obișnuite.

În consecință, vârfurile se vor propaga către fluxurile ascendente NGINX, dar cu o anumită limită, definită de burst valoare.

Vizualizarea limitelor ratei

Deoarece cred că cel mai bun mod de a-ți aminti acest lucru este să-l experimentezi într-un mod practic, am configurat o mică imagine Docker cu o configurație NGINX expunând diverse setări limită de rată pentru a vedea răspunsurile la o locație limitată a ratei de bază, la o burst– locație cu tarif limitat activată și către burst cu nodelay locație cu tarif limitat, să ne jucăm cu asta.

Aceste mostre utilizează această configurare simplă NGINX (pentru care vom oferi o imagine Docker la sfârșitul acestei postări, astfel încât să puteți testa mai ușor acest lucru):

limit_req_zone $request_uri zone=by_uri:10m rate=30r/m;

server {
    listen 80;

    location /by-uri/burst0 {
        limit_req zone=by_uri;
        try_files $uri /index.html;
    }

    location /by-uri/burst5 {
        limit_req zone=by_uri burst=5;
        try_files $uri /index.html;
    }

    location /by-uri/burst5_nodelay {
        limit_req zone=by_uri burst=5 nodelay;
        try_files $uri /index.html;
    }
}

Începând cu această configurare, toate eșantioanele de mai jos vor trimite 10 cereri simultane simultan. Să vedem:

  • câți sunt respinși de rata limită?
  • care este rata de procesare a celor acceptate?

Trimiterea a 10 solicitări paralele către un punct final limitat cu rata

Limitarea ratei NGINX pe scurt
10 solicitări care ating în același timp un punct final limitat cu rata

Această configurare permite 30 de solicitări pe minut. Dar 9 din 10 cereri sunt respinse în acest caz. Dacă ați urmat pașii anteriori, acest lucru ar trebui să aibă sens: 30r/m înseamnă că o nouă solicitare este permisă la fiecare 2 secunde. Aici 10 solicitări sosesc în același timp, una este permisă, celelalte 9 sunt văzute de NGINX înainte de bifările timer-token și, prin urmare, toate sunt respinse.

Dar sunt bine să tolerez o explozie pentru unii clienți / puncte finale

Ok, deci să adăugăm burst=5 argument pentru a permite NGINX să gestioneze rafale mici pentru acest punct final al zonei cu rată limitată:

1611922690 739 Limitarea ratei NGINX pe scurt
10 cereri simultane trimise simultan la un punct final cu un argument de rafală = 5

Ce se petrece aici? După cum era de așteptat cu burst argument, sunt acceptate încă 5 cereri, așa că am trecut de la 10/10 la 6/10 de succes (iar restul este respins). Dar modul în care NGINX și-a reîmprospătat simbolul și a procesat solicitările acceptate este destul de vizibil aici: rata de ieșire este limitată la 30r/m care este echivalent cu 1 cerere la fiecare 2 secunde.

Primul este returnat după 0,2 secunde. Temporizatorul bifează după 2 secunde, iar una dintre solicitările în așteptare este procesată și returnată, cu un timp total dus-întors de 2,02 secunde. 2 secunde mai târziu, cronometrul bifează din nou, procesând o altă cerere în așteptare, care este returnată cu un timp total dus-întors de 4,02 secunde. Și așa mai departe și așa mai departe …

burst argumentul vă permite doar să transformați rata limită NGINX de la un filtru de prag de bază la un gateway de politică de formare a traficului.

Serverul meu are o capacitate suplimentară. Vreau să folosesc o limită de rată pentru a preveni depășirea acestei capacități.

În acest caz, nodelay argumentul va fi de ajutor. Să trimitem aceleași 10 cereri către un burst=5 nodelay obiectiv final:

1611922690 714 Limitarea ratei NGINX pe scurt
10 cereri simultane trimise la un punct final configurat cu rafală = 5 nodelay

Așa cum era de așteptat cu burst=5 avem în continuare același număr de stări 200 și 503. Dar acum rata de ieșire nu mai este strict limitată la rata de 1 cereri la fiecare 2 secunde. Atâta timp cât sunt disponibile unele jetoane de rafală, orice cerere primită este acceptată și procesată imediat. Rata de bifare a temporizatorului este încă la fel de importantă ca înainte pentru a controla rata de reîmprospătare / reumplere a acestor jetoane de rafală, dar solicitările acceptate nu mai suferă nicio întârziere suplimentară.

Notă: în acest caz, zone folosește $request_uri dar toate testele următoare funcționează exact la fel pentru un $binary_remote_addr config care ar rata-limită de IP client. Vă veți putea juca cu aceasta în imaginea Docker.

Să recapitulăm

Dacă încercăm să vizualizăm modul în care NGINX acceptă solicitările primite, atunci le procesează în funcție de rate, burst, și nodelay parametru, iată o vedere sintetică.

Pentru a simplifica lucrurile, vom arăta numărul de solicitări primite (apoi acceptate sau respinse și procesate) pe fiecare pas, valoarea pasului de timp depinzând de limita de rată definită de zonă. Dar durata efectivă a acestui pas nu contează în cele din urmă. Ceea ce este semnificativ este numărul de solicitări pe care NGINX trebuie să le proceseze în cadrul fiecăruia dintre acești pași.

Iată deci traficul pe care îl vom trimite prin diferite setări ale limitei tarifelor:

1611922690 192 Limitarea ratei NGINX pe scurt
Cererile primite și rata limită definită în această zonă
1611922690 494 Limitarea ratei NGINX pe scurt
Solicitări acceptate și respinse atunci când nu este definită nicio setare de rafală

Fără a utiliza rafala (adică burst=0), am văzut că NGINX acționează ca un actor pur în limita ratei / politicii de trafic. Toate cererile sunt fie procesate imediat, dacă temporizatorul de rată a bifat, fie imediat a respins altfel.

Acum, dacă vrem să permitem rafalei mici să utilizeze capacitatea neutilizată sub limita ratei, am văzut că adăugarea unui burst argumentul permite utilizării să facă asta, ceea ce implică o întârziere suplimentară în procesarea cererilor care consumă jetoanele de rafală:

1611922690 174 Limitarea ratei NGINX pe scurt
Acceptate, acceptate cu întârziere și cereri respinse când se utilizează rafala

Putem vedea că numărul total de solicitări respinse este mai mic, iar NGINX procesează mai multe solicitări. Doar cererile suplimentare atunci când nu sunt disponibile jetoane de explozie sunt respinse. În această configurație, NGINX efectuează o anumită formare a traficului real.

În cele din urmă, am văzut că NGINX poate fi folosit fie pentru a face unele politici de trafic, fie pentru a limita dimensiunea exploziei, dar totuși propagă unele dintre aceste explozii către lucrătorii care procesează (fluxuri ascendente sau locale), care, în cele din urmă, rata de ieșire mai puțin stabilă, dar cu o latență mai bună, dacă puteți procesa aceste solicitări suplimentare:

1611922690 648 Limitarea ratei NGINX pe scurt
Solicitări acceptate și procesate și solicitări respinse atunci când rafala este utilizată cu nodelay

Jucați-vă cu sandbox-ul limită de rată

Acum puteți să explorați codul, să clonați repo-ul, să vă jucați cu imaginea Docker și să puneți mâna pe el foarte repede pentru a vă consolida mai bine înțelegerea acestor concepte. https://github.com/sportebois/nginx-rate-limit-sandbox

Actualizare (14 iunie 2017)

NGINX a publicat acum câteva zile propria explicație detaliată a mecanismului lor de limitare a ratei. Acum puteți afla mai multe despre asta în Limitarea ratei cu NGINX și NGINX Plus postare pe blog.