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_zoneir limit_reqnurodymai. Ji taip pat teikia limit_req_statusir 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_reqdirektyvą, kuriai reikalingas zoneparametras, taip pat pateikiami neprivalomiburst ir nodelayparametrai.

Čia yra kelios sąvokos:

  • zoneleidž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ą.
  • burstyra 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 .
  • nodelaytaip pat yra neprivaloma ir yra naudinga tik tada, kai taip pat nustatote burstvertę, o kodėl, pamatysime toliau.

Kaip NGINX nusprendžia, ar prašymas priimamas, ar atmetamas?

Kai nustatote zoną, nustatote greitį, pvz., 300r/mNorite leisti 300 užklausų per minutę arba 5r/sleisti 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. rateNustatymas 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/mir su 5r/sjais 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 burstparametrą. 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. Į ratekontrolė, kaip greitai laikmatis tiksi, bet tai jau nebe true / false raktas, bet skaitiklis vyksta nuo 0iki 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 burstnustatymas 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/sNGINX“ 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/sir 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/skad 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/sir 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 burstverte.

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 burstsu nodelaygreič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.