NGINX ātruma ierobežojums īsumā

NGINX ir lielisks ... bet es atklāju, ka tā dokumentācija par likmju ierobežošanu ir nedaudz ... ierobežota. Tāpēc esmu uzrakstījis šo rokasgrāmatu par ātruma ierobežošanu un satiksmes veidošanu ar NGINX.

Mēs dodamies uz:

  • aprakstiet NGINX direktīvas
  • izskaidrojiet NGINX pieņemšanas / noraidīšanas loģiku
  • palīdzēs jums vizualizēt, kā tiek apstrādāts reāls datplūsmas pārrāvums, izmantojot dažādus iestatījumus: ātruma ierobežošanu, satiksmes politiku un nelielu sarunu atļaušanu

Kā bonusu esmu iekļāvis GitHub repo un iegūto Docker attēlu, lai jūs varētu eksperimentēt un reproducēt testus. Darot vienmēr ir vieglāk iemācīties!

NGINX likmju ierobežojumu direktīvas un to lomas

Šis ziņojums koncentrējas uz moduli ngx_http_limit_req_module, kas sniedz jums limit_req_zoneun limit_reqdirektīvas. Tas arī nodrošina limit_req_statusun limit_req_level. Kopā tie ļauj kontrolēt HTTP atbildes statusa kodu noraidītajiem pieprasījumiem un to, kā šie noraidījumi tiek reģistrēti.

Lielākā daļa neskaidrību rodas no noraidīšanas loģikas.

Pirmkārt, jums ir jāsaprot limit_reqdirektīva, kurai nepieciešams zoneparametrs, kā arī nodrošina izvēlesburst un nodelayparametrus.

Šeit ir vairāki jēdzieni:

  • zoneļauj definēt kopu, kopīgu “atstarpi”, kurā uzskaitīt ienākošos pieprasījumus. Visi vienā un tajā pašā sūtījumā iekļautie pieprasījumi tiks ieskaitīti vienā likmes ierobežojumā. Tas ļauj ierobežot URL, IP vai kaut ko citu.
  • burstnav obligāta. Ja tas ir iestatīts, tas nosaka, cik pārsniegšanas pieprasījumus varat pieņemt, pārsniedzot pamatlikmi. Šeit jāatzīmē viena svarīga lieta: sērija ir absolūta vērtība, tas nav ātrums .
  • nodelaynav obligāta un ir noderīga tikai tad, kad iestatāt arī burstvērtību, un tālāk redzēsim, kāpēc.

Kā NGINX izlemj, vai pieprasījums tiek pieņemts vai noraidīts?

Iestatot zonu, jūs definējat ātrumu, piemēram 300r/m, atļaut 300 pieprasījumus minūtē vai 5r/s5 pieprasījumus katrā sekundē.

Piemēram:

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

Ir svarīgi saprast, ka šīm 2 zonām ir vienādas robežas. rateIestatījumu izmanto nginx, lai aprēķinātu frekvenci: kāds ir laika intervāls, pirms pieņemt jaunu pieprasījumu? NGINX ar šo marķiera atsvaidzināšanas ātrumu izmantos noplūdušā segmenta algoritmu.

Attiecībā uz NGINX 300r/mun 5r/spret tiem izturas tāpat: ļaujiet šai zonai vienu pieprasījumu ik pēc 0,2 sekundēm. Katru 0,2 sekundi šajā gadījumā NGINX iestatīs karodziņu, lai atcerētos, ka tas var pieņemt pieprasījumu. Kad ienāk pieprasījums, kas atbilst šai zonai, NGINX iestata karodziņu kā nepatiesu un to apstrādā. Ja pirms taimera atzīmēšanas ienāk vēl viens pieprasījums, tas nekavējoties tiks noraidīts ar 503 statusa kodu. Ja taimeris atzīmējas un karodziņš jau ir iestatīts, lai pieņemtu pieprasījumu, nekas nemainās.

Vai jums ir nepieciešams ātruma ierobežojums vai satiksmes veidošana?

Ievadiet burstparametru. Lai to saprastu, iedomājieties, ka iepriekš izskaidrotais karogs vairs nav būla skaitlis, bet gan vesels skaitlis: maksimālais pieprasījumu skaits, ko NGINX var atļaut sērijā.

Tas vairs nav noplūdis spainīšu algoritms, bet gan marķieru spainis. Par ratekontrolē, cik ātri taimeris ērces, bet tas vairs nav patiess / nepatiess marķieris, bet skaitītājs iet no 0līdz 1+burst value. Katru reizi, kad taimeris atzīmējas, skaitītājs tiek palielināts, ja vien tas jau nav tā maksimālajā vērtībā b+1. Tagad jums vajadzētu saprast, kāpēc burstiestatījums ir vērtība, nevis likme.

Kad ienāk jauns pieprasījums, NGINX pārbauda, ​​vai ir pieejams marķieris (ti, skaitītājs ir> 0), ja nē, pieprasījums tiek noraidīts. Ja ir marķieris, pieprasījums tiek pieņemts un tiks apstrādāts, un šis marķieris tiks patērēts (skaitītājs tiek samazināts).

Labi, tāpēc NGINX pieņems pieprasījumu, ja ir pieejams sērijveida marķieris. Bet kad NGINX apstrādās šo pieprasījumu?

Jūs lūdzāt NGINX piemērot maksimālo likmi. 5r/sNGINX pieņem pārsniegtos pieprasījumus, ja ir pieejami sērijveida žetoni, bet gaidīs, kamēr kāda vieta tos apstrādās maksimālās likmes ierobežojuma ietvaros. Tādējādi šie sērijas pieprasījumi tiks apstrādāti ar nelielu kavēšanos , vai arī tie tiks noildzināti.

Citiem vārdiem sakot, NGINX nepārsniegs tarifa ierobežojumu, kas noteikts zonas deklarācijā, un tāpēc rindā liek papildu pieprasījumus un tos apstrādās ar nelielu kavēšanos, jo marķiera taimeris atzīmējas un tiek saņemts mazāk pieprasījumu.

Lai izmantotu vienkāršu piemēru, pieņemsim, ka jums ir ātrums 1r/sun sprādziens 3. NGINX vienlaikus saņem 5 pieprasījumus:

  • Pirmais tiek pieņemts un apstrādāts
  • Tā kā jūs atļaujat 1 + 3, uzreiz tiek noraidīts viens pieprasījums ar 503 statusa kodu
  • Pārējie 3 tiks ārstēti pa vienam, bet ne uzreiz. Viņiem tiks piemērota likme, 1r/slai tie nepārsniegtu jūsu iestatīto limitu. Ja netiek saņemts neviens cits pieprasījums, šī kvota jau tiek patērēta. Kad rinda ir tukša, sērijveida skaitītāju atkal sāks palielināt (marķiera kauss atkal tiks aizpildīts)

Ja kā starpniekserveri izmantojat NGINX, augšupējā daļa saņems pieprasījumu ar maksimālo ātrumu 1r/s, un tas nezinās par ienākošo pieprasījumu pārsprāgušo, viss tiks ierobežots ar šo ātrumu.

Jūs tikko veicāt satiksmes veidošanu, ieviešot zināmu kavēšanos, lai regulētu pārrāvumus un izveidotu regulārāku straumi ārpus NGINX.

Ievadiet mezglu

nodelay stāsta NGINX, ka pieprasījumi, kurus tā pieņem sērijveida logā, ir jāapstrādā nekavējoties, tāpat kā regulāri pieprasījumi.

Tā rezultātā tapas izplatīsies NGINX augšpusē, bet ar zināmu ierobežojumu, ko nosaka burstvērtība.

Likmes robežu vizualizēšana

Tā kā es uzskatu, ka labākais veids, kā to atcerēties, ir piedzīvot to praktiskā veidā, es izveidoju nelielu Docker attēlu ar NGINX konfigurāciju, pakļaujot dažādus ātruma ierobežojuma iestatījumus, lai redzētu atbildes uz pamata likmes ierobežotu vietu, uz burst- iespējota ar ātrumu ierobežota atrašanās vieta, un, burstja nodelayatrašanās vieta ir ierobežota, spēlēsim ar to.

Šie paraugi izmanto šo vienkāršo NGINX konfigurāciju (kurai mēs šīs ziņas beigās sniegsim Docker attēlu, lai jūs to varētu vieglāk pārbaudīt):

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; } }

Sākot ar šo konfigurāciju, visi tālāk minētie paraugi vienlaikus nosūtīs 10 vienlaikus pieprasījumus. Paskatīsimies:

  • cik daudz noraida ar likmes ierobežojumu?
  • kāds ir pieņemto apstrādes ātrums?

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.

Visbeidzot, mēs redzējām, ka NGINX var izmantot vai nu kādas satiksmes politikas veikšanai, vai arī lai ierobežotu sērijas lielumu, taču daži no šiem pārrāvumiem joprojām tiek izplatīti apstrādes darbiniekiem (augšpusē vai lokāli), kas galu galā rada mazāk stabils izejošais ātrums, bet ar labāku latentumu, ja varat apstrādāt šos papildu pieprasījumus:

Pats spēlējaties ar ātruma ierobežojuma smilškasti

Tagad jūs varat doties izpētīt kodu, klonēt repo, spēlēt ar Docker attēlu un ātri piekļūt tam, lai labāk nostiprinātu izpratni par šiem jēdzieniem. //github.com/sportebois/nginx-rate-limit-sandbox

Atjaunināt (2017. gada 14. jūnijs)

NGINX pirms dažām dienām publicēja savu detalizētu skaidrojumu par viņu likmju ierobežošanas mehānismu. Tagad jūs varat uzzināt vairāk par to likmju ierobežošanā ar NGINX un NGINX Plus emuāra ziņā.