Az NGINX sebességkorlátozása dióhéjban

Az NGINX fantasztikus ... de a dokumentációját az árkorlátozásról kissé korlátozottnak találtam. Szóval írtam ezt az útmutatót a sebességkorlátozáshoz és a forgalom alakításához az NGINX segítségével.

Mi fogunk:

  • ismertesse az NGINX irányelveket
  • magyarázza el az NGINX elfogadási / elutasítási logikáját
  • különféle beállítások segítségével vizualizálhatja, hogyan dolgozza fel a valódi forgalomrobbantást: sebességkorlátozó, forgalmi házirend és kis sorozatok engedélyezése

Bónuszként belefoglaltam a GitHub repót és az így kapott Docker képet, így kísérletezhet és reprodukálhatja a teszteket. Mindig könnyebb megtanulni ezzel!

Az NGINX sebességhatár-irányelvei és szerepeik

Ez a bejegyzés az ngx_http_limit_req_modulra összpontosít, amely a limit_req_zoneés limit_reqirányelveket tartalmazza. Ezenkívül biztosítja a limit_req_statusés limit_req_level. Ezek együttesen lehetővé teszik az elutasított kérelmek HTTP-válaszállapot-kódjának és az elutasítások naplózásának szabályozását.

A legtöbb zavart az elutasítási logika okozza.

Először meg kell értenie az limit_reqirányelvet, amelynek szüksége van egy zoneparaméterre, valamint opcionálisburst és nodelayparamétereket is megad.

Itt többféle koncepció játszik szerepet:

  • zonesegítségével meghatározhat egy vödröt, egy megosztott „helyet”, amelyben a beérkező kéréseket számolni lehet. Minden, ugyanabba a csoportba érkező kérelem ugyanabba az aránykorlátba kerül. Ez lehetővé teszi, hogy korlátozzon URL-t, IP-t vagy bármi mást.
  • burstopcionális. Ha be van állítva, akkor meghatározza, hogy hány túllépő kérést fogadhat el az alapkamat felett. Itt fontos megjegyezni: a sorozatfelvétel abszolút érték, nem pedig sebesség .
  • nodelayopcionális is, és csak akkor hasznos, ha burstértéket is beállít , és az alábbiakban megtudjuk, miért.

Hogyan dönt az NGINX, ha egy kérést elfogadnak vagy elutasítanak?

Zóna beállításakor megad egy sebességet, például 300r/mpercenként 300 kérést vagy 5r/smásodpercenként 5 kérést engedélyez.

Például:

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

Fontos megérteni, hogy ennek a 2 zónának ugyanazok a határai vannak. Az rateNGINX ezt a beállítást használja a frekvencia kiszámításához: mennyi az az időtartam, amely egy új kérés elfogadásához szükséges? Az NGINX ezzel a token frissítési gyakorisággal alkalmazza a szivárgó csoport algoritmust.

NGINX esetén, 300r/més 5r/sugyanúgy kezelik őket: engedélyezzen 0,2 másodpercenként egy kérést ehhez a zónához. Ebben az esetben az NGINX 0,2 másodpercenként beállít egy zászlót, hogy emlékezzen arra, hogy elfogadhat egy kérést. Amikor egy olyan kérés érkezik, amely megfelel ennek a zónának, az NGINX hamisra állítja a zászlót és feldolgozza azt. Ha az időzítő ketyegése előtt érkezik egy másik kérés, akkor azt egy 503-as állapotkóddal azonnal elutasítják. Ha az időzítő ketyeg, és a zászló már be van állítva egy kérés elfogadására, akkor semmi sem változik.

Szüksége van sebességkorlátozásra vagy forgalomformálásra?

Írja be a burstparamétert. Annak érdekében, hogy megértsük, képzeljük el, hogy a fentebb kifejtett zászló már nem logikai érték, hanem egész szám: az NGINX által engedélyezett kérések maximális száma egy sorozatban engedélyezhető.

Ez már nem egy szivárgó vödör algoritmus, hanem egy token vödör. Az rate, hogy milyen gyorsan az időzítő ketyeg, de ez már nem igaz / hamis token, de a pult felé haladva 0az 1+burst value. Minden alkalommal, amikor az időzítő ketyeg, a számláló növekszik, kivéve, ha az már a maximális értékén van b+1. Most meg kell értenie, hogy a burstbeállítás miért érték, és nem arány.

Amikor új kérés érkezik, az NGINX ellenőrzi, hogy van-e jogkivonat (azaz a számláló> 0), ha nem, akkor a kérelmet elutasítják. Ha van token, akkor a kérést elfogadják, és kezelni fogják, és a tokent elfogyasztják (a számlálót csökkentik).

Ok, tehát az NGINX elfogadja a kérést, ha rendelkezésre áll egy sorozatképes token. De mikor fogja az NGINX feldolgozni ezt a kérést?

Megkérte az NGINX-et, hogy alkalmazza a maximális sebességet 5r/s, az NGINX elfogadja a túllépő kéréseket, ha rendelkezésre állnak sorozatképes tokenek, de várni fog, amíg egy helyiség feldolgozza azokat a maximális sebességhatáron belül. Ezért ezeket a sorozatfelvételi kérelmeket némi késéssel feldolgozzuk , különben késnek .

Más szavakkal, az NGINX nem lépi túl a zónadeklarációban meghatározott sebességhatárt, ezért sorba állítja az extra kéréseket, és némi késéssel feldolgozza őket, mivel a tokenidőzítő ketyeg és kevesebb kérés érkezik.

Egyszerű példa szerint tegyük fel, hogy van egy sebessége 1r/sés egy sorozat 3. Az NGINX egyszerre 5 kérést kap:

  • Az elsőt elfogadják és feldolgozzák
  • Mivel engedélyez 1 + 3 értéket, 1 kérés azonnal elutasításra kerül, 503 állapotkóddal
  • A másik 3-at egyenként kezeljük, de nem azonnal. Azokat a díjakat kezeljük, 1r/shogy az Ön által megadott korlátokon belül maradjanak. Ha más kérelem nem érkezik, akkor már elfogyasztja ezt a kvótát. Amint a sor kiürült, a sorozatfelvétel számlálója újra növekszik (a token vödör újból megkezdődik)

Ha az NGINX-t használja proxyként, akkor az upstream a kérést maximális sebességgel kapja meg 1r/s, és nem lesz tudomása a beérkező kérések töréséről , minden ezzel a sebességgel lesz korlátozva.

Éppen most alakítottál ki valamilyen forgalmat, némi késleltetést vezettél be a törések szabályozására és az NGINX-en kívül egy szabályosabb folyam létrehozására.

Írja be a csomópontot

nodelay közli az NGINX-szel, hogy a burst ablakban elfogadott kéréseket azonnal fel kell dolgozni, mint a rendszeres kéréseket.

Ennek következtében a tüskék az NGINX upstreamjeire terjednek, de az burstérték által meghatározott bizonyos korlátokkal .

A sebességkorlátok megjelenítése

Mivel úgy gondolom, hogy a legjobb módja ennek emlékezni a gyakorlati tapasztalatokra, létrehoztam egy kis Docker képet egy NGINX konfigurációval, amely különféle sebességkorlátozási beállításokat tett közzé, hogy láthassam a válaszokat egy korlátozott alapsebességű helyre, egy burst-képes ütemétől helyét, hogy bursta nodelaysebesség-korlátozott hely, játsszunk vele.

Ezek a minták ezt az egyszerű NGINX konfigurációt használják (amihez a bejegyzés végén egy Docker képet adunk, hogy könnyebben tesztelhesse ezt):

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

Ezzel a konfigurációval kezdődően az összes alábbi minta egyszerre 10 egyidejű kérést küld. Lássuk:

  • hányat utasít el a kamatkorlát?
  • mekkora az elfogadottak feldolgozási aránya?

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.

Finally, we saw that NGINX can be used to either do some traffic-policy or to limit the size of the burst, but still propagates some of these bursts to the processing workers (upstreams or local), which, in the end, does generate less stable outgoing rate, but with a better latency, if you can process these extra requests:

Playing with the rate limit sandbox yourself

Now you can go explore the code, clone the repo, play with the Docker image, and get your hands on it real quick to better solidify your understanding of these concepts. //github.com/sportebois/nginx-rate-limit-sandbox

Update (June 14th, 2017)

NGINX published a few days ago their own detailed explanation of their rate-limiting mechanism. You can now learn more about it in their Rate Limiting with NGINX and NGINX Plus blog post.