NGINX normos ribojimas trumpai

NGINX yra nuostabi ... bet manau, kad jo dokumentai apie normos ribojimą yra šiek tiek ... riboti. Taigi aš parašiau šį greitį ribojančio ir eismo formavimo su NGINX vadovą.
Ketino:
- apibūdinkite NGINX direktyvas
- paaiškinkite NGINX sutikimo / atmetimo logiką
- padės jums įsivaizduoti, kaip apdorojamas tikras srauto pliūpsnis, naudojant įvairius nustatymus: ribojantį greitį, eismo politiką ir leidžiant nedidelius pliūpsnius
Kaip premiją įtraukiau „GitHub“ atpirkimą ir gautą „Docker“ vaizdą, kad galėtumėte eksperimentuoti ir atkurti bandymus. Tai padaryti visada lengviau išmokti!
NGINX normos ribų direktyvos ir jų vaidmuo

Šiame įraše daugiausia dėmesio skiriama moduliui „ngx_http_limit_req_module“, kuriame pateikiami limit_req_zone
ir limit_req
nurodymai. Ji taip pat teikia limit_req_status
ir limit_req_level
. Kartu tai leidžia valdyti atmestų užklausų HTTP atsakymo būsenos kodą ir tai, kaip šie atmetimai registruojami.
Daugiausia painiavos kyla iš atmetimo logikos.
Pirma, jūs turite suprasti limit_req
direktyvą, kuriai reikalingas zone
parametras, taip pat pateikiami neprivalomiburst
ir nodelay
parametrai.
Čia yra kelios sąvokos:
zone
leidžia apibrėžti grupę, bendrą „erdvę“, kurioje bus skaičiuojamos gaunamos užklausos. Visos užklausos, patenkančios į tą patį segmentą, bus įskaitytos į tą patį tarifų limitą. Būtent tai leidžia apriboti URL, IP ar bet ką kitą.burst
yra neprivaloma. Jei nustatyta, tai apibrėžia, kiek viršijančių užklausų galite priimti virš bazinės normos. Čia reikia atkreipti dėmesį į vieną svarbų dalyką: serija yra absoliuti vertė, tai nėra greitis .nodelay
taip pat yra neprivaloma ir yra naudinga tik tada, kai taip pat nustatoteburst
vertę, o kodėl, pamatysime toliau.
Kaip NGINX nusprendžia, ar prašymas priimamas, ar atmetamas?
Kai nustatote zoną, nustatote greitį, pvz., 300r/m
Norite leisti 300 užklausų per minutę arba 5r/s
leisti 5 užklausas kiekvieną sekundę.
Pavyzdžiui:
limit_req_zone $request_uri zone=zone1:10m rate=300r/m;
limit_req_zone $request_uri zone=zone2:10m rate=5r/s;
Svarbu suprasti, kad šios 2 zonos turi tas pačias ribas. rate
Nustatymas naudojamas Nginx apskaičiuoti dažnį: kas yra laiko tarpas iki priimti naują prašymą? „NGINX“ naudos nutekėjusio segmento algoritmą su šiuo žetonų atnaujinimo dažniu.
„NGINX“ atveju 300r/m
ir su 5r/s
jais elgiamasi taip pat: leisti šiai zonai vieną užklausą kas 0,2 sekundės. Šiuo atveju kas 0,2 sekundę NGINX nustatys žymą, kad prisimintų, jog ji gali priimti užklausą. Kai gaunama užklausa, tinkanti šiai zonai, NGINX nustato vėliavą kaip klaidingą ir ją apdoroja. Jei dar viena užklausa pateikiama prieš laikmatį, ji bus nedelsiant atmesta su 503 būsenos kodu. Jei laikmatis pažymėjo, o vėliava jau buvo nustatyta priimti užklausą, niekas nepasikeis.
Ar jums reikia riboti tarifą ar keisti eismą?
Įveskite burst
parametrą. Norėdami jį suprasti, įsivaizduokite, kad vėliava, kurią paaiškinome aukščiau, yra ne loginė, o sveikasis skaičius: maksimalus užklausų skaičius, kurį NGINX gali leisti per seriją.
Tai jau ne nutekėjęs kibirų algoritmas, o ženklų kibiras. Į rate
kontrolė, kaip greitai laikmatis tiksi, bet tai jau nebe true / false raktas, bet skaitiklis vyksta nuo 0
iki 1+burst value
. Kiekvieną kartą, kai laikmatis tiksi, skaitiklis yra didinamas, nebent jis jau yra didžiausias b+1
. Dabar turėtumėte suprasti, kodėl burst
nustatymas yra vertė, o ne norma.
Kai ateina nauja užklausa, NGINX patikrina, ar prieinamas prieigos raktas (ty skaitiklis yra> 0), jei ne, užklausa atmetama. Jei yra prieigos raktas, užklausa priimama ir bus apdorojama, ir tas ženklas bus sunaudotas (skaitiklis mažinamas).
Gerai, todėl NGINX priims užklausą, jei bus pateiktas serijos atpažinimo ženklas. Bet kada NGINX apdoros šį prašymą?
Paprašėte „NGINX“ taikyti didžiausią normą. „ 5r/s
NGINX“ priima viršijančias užklausas, jei yra sprogimo žetonų, tačiau laukia, kol tam tikras kambarys juos apdoros neviršydamas šios maksimalios normos ribos. Taigi šios serijos užklausos bus apdorojamos šiek tiek vėluojant , arba jos pasibaigs.
Kitaip tariant, „NGINX“ neperžengs zonos deklaracijoje nustatytos normos ribos, todėl papildomų užklausų eilėje ir jas apdoros su tam tikru vėlavimu, nes žetonų laikmatis pažymi ir gaunama mažiau užklausų.
Jei norite naudoti paprastą pavyzdį, tarkime, kad turite greitį 1r/s
ir sprogo 3
. „NGINX“ vienu metu gauna 5 užklausas:
- Pirmasis priimamas ir apdorojamas
- Kadangi leidžiate 1 + 3, nedelsiant atmetama 1 užklausa su 503 būsenos kodu
- Trys kiti bus gydomi po vieną, bet ne iš karto. Jiems bus taikoma norma,
1r/s
kad jie neviršytų jūsų nustatytos ribos. Jei nėra kitos užklausos, ši kvota jau sunaudojama. Kai eilė bus tuščia, serijos skaitiklis vėl pradės didėti (ženklo grupė vėl bus pildoma)
Jei naudosite „NGINX“ kaip tarpinį serverį, tiekėjas gaus užklausą maksimaliu greičiu 1r/s
ir jai nebus žinoma apie gaunamų užklausų sprogimą, viskas bus apribota tokiu tarifu.
Jūs ką tik padarėte tam tikrą eismo formavimą, įvedę tam tikrą vėlavimą reguliuoti serijas ir sukurti reguliaresnį srautą už NGINX ribų.
Įveskite mazgą
nodelay
nurodo NGINX, kad prašymai, kuriuos ji priima serijos lange, turėtų būti nedelsiant apdorojami, kaip ir įprasti prašymai.
Dėl to šuoliai plis į NGINX aukštupius, tačiau su tam tikra riba, apibrėžta burst
verte.
Vizualizuoti normos ribas
Kadangi manau, kad geriausias būdas tai atsiminti yra patirti tai praktiškai, aš sukūriau mažą „Docker“ vaizdą su NGINX konfigūracija, atskleisdamas įvairius greičio ribojimo nustatymus, kad matyčiau atsakymus į riboto tarifo vietą, į ribotą greitį. burst
įgalinta norma ribotos vietą ir burst
su nodelay
greičio ribotas vietą, Pažaiskime su tuo.
Šiuose pavyzdžiuose naudojama ši paprasta NGINX konfigūracija (kurią pateiksime „Docker“ vaizdą šio įrašo pabaigoje, kad galėtumėte lengviau tai išbandyti):
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; } }
Pradedant šia konfigūracija, visi toliau pateikti pavyzdžiai vienu metu išsiųs 10 vienu metu pateikiamų užklausų. Pažiūrėkime:
- kiek atmeta normos riba?
- koks yra priimtųjų apdorojimo greitis?
Sending 10 parallel requests to a rate-limited endpoint

That config allows 30 requests per minute. But 9 out of 10 requests are rejected in that case. If you followed the previous steps, this should make sense: The 30r/m
means that a new request is allowed every 2 seconds. Here 10 requests arrive at the same time, one is allowed, the 9 other ones are seen by NGINX before the token-timer ticks, and are therefore all rejected.
But I’m OK to tolerate some burst for some client/endpoints
Ok, so let’s add the burst=5
argument to let NGINX handle small bursts for this endpoint of the rate-limited zone:

What’s going on here? As expected with the burst
argument, 5 more requests are accepted, so we went from 1 /10 to 6/10 success (and the rest is rejected). But the way NGINX refreshed its token and processed the accepted requests is quite visible here: the outgoing rate is capped at 30r/m
which is equivalent to 1 request every 2 seconds.
The first one is returned after 0.2 seconds. The timer ticks after 2 seconds, and one of the pending requests is processed and returned, with a total roundtrip time of 2.02 seconds. 2 seconds later, the timer ticks again, processing another pending request, which is returned with a total roundtrip time of 4.02 seconds. And so on and so forth…
The burst
argument just lets you turn NGINX rate-limit from some basic threshold filter to a traffic shaping policy gateway.
My server has some extra capacity. I want to use a rate-limit to prevent it from going over this capacity.
In this case, the nodelay
argument will be helpful. Let’s send the same 10 requests to a burst=5 nodelay
endpoint:

As expected with the burst=5
we still have the same number of status 200 and 503. But now the outgoing rate is no longer strictly constrained to the rate of 1 requests every 2 seconds. As long as some burst tokens are available, any incoming request is accepted and processed immediately. The timer tick rate is still as important as before to control the refresh/refill rate of these burst tokens, but accepted requests no longer suffer any additional delay.
Note: in this case, the zone
uses the $request_uri
but all the following tests work exactly the same way for a $binary_remote_addr
config which would rate-limit by client IP. You’ll be able to play with this in the Docker image.
Let’s recap
If we try to visualize how NGINX accepts the incoming requests, then processes them depending on the rate
, burst
, and nodelay
parameter, here’s a synthetic view.
To keep things simple, we’ll show the number of incoming requests (then accepted or rejected, and processed) per time step, the value of the time step depending on the zone-defined rate limit. But the actual duration of that step doesn’t matter in the end. What is meaningful is the number of requests NGINX has to process within each of these steps.
So here is the traffic we’ll send through various rate limit settings:


Without using the burst (i.e. burst=0
), we saw that NGINX acts as a pure rate-limit/traffic-policy actor. All requests are either immediately processed, if the rate timer has ticked, or immediately rejected otherwise.
Now if we want to allow the small burst to use the unused capacity under the rate-limit, we saw that adding a burst
argument lets use do that, which implies some additional delay in processing the requests consuming the burst tokens:

We can see that the overall number of rejected requests is lower, and NGINX processes more requests. Only the extra requests when no burst tokens are available are rejected. In this setup, NGINX performs some real traffic-shaping.
Galiausiai pamatėme, kad NGINX gali būti naudojama atliekant tam tikrą eismo politiką arba ribojant serijos dydį, tačiau kai kurie iš šių sprogimų vis tiek skleidžiami perdirbimo darbuotojams (aukštesniam srautui ar vietiniams), o tai galų gale sukuria mažiau stabilus išeinantis rodiklis, bet su geresniu vėlavimu, jei galite apdoroti šias papildomas užklausas:

Žaisk su normos ribos smėlio dėže pats
Dabar galite pradėti tyrinėti kodą, klonuoti atpirkimą, žaisti su „Docker“ atvaizdu ir greitai įsitraukti į tai, kad geriau įtvirtintumėte šių sąvokų supratimą. //github.com/sportebois/nginx-rate-limit-sandbox
Atnaujinimas (2017 m. Birželio 14 d.)
Prieš kelias dienas „NGINX“ paskelbė išsamų savo tarifų ribojimo mechanizmo paaiškinimą. Dabar apie tai galite sužinoti daugiau apie jų tarifų ribojimą naudojant „NGINX“ ir „NGINX Plus“ tinklaraščio įrašus.