Queue 2

16. Apr 2023

Optimer dit Laravel-køsystem med Redis Throttle til API-throttling

Casper Panduro

Casper Panduro

Freelance web-entusiast | React, React Native og Laravel 🚀

For nylig blev jeg udfordret i et projekt, hvor jeg skulle hente data fra en server, der kun tillod 60 anmodninger pr. minut. Projektets kunde havde omkring 150.000 brugere i deres app, og hver af disse brugere skulle have adgang til at foretage anmodninger til det eksterne API. Da jeg skulle hente en stor mængde data, var jeg nødt til at foretage omkring 25 anmodninger pr. bruger, før alle de nødvendige data var hentet.

For at løse dette problem skulle jeg optimere mit køsystem, så det kun foretog 60 anmodninger pr. minut til det eksterne API. Hvis der var andre jobs i køen, skulle de stadig kunne udføres, da de ikke var begrænset af samme throttling som det eksterne API.

Hvordan løser man så det?

Først: forståelse af API-throttling! Hvordan det fungerer, og hvorfor det er vigtigt at tage hensyn til?

API-throttling er en teknik, som bruges til at begrænse antallet af anmodninger, som en klient kan foretage over en given tidsperiode til et API. API-throttling er en måde at beskytte API'et mod overbelastning og for at forhindre, at API'et bliver nedbrudt eller oplever betydelige nedture i ydeevnen. Det lyder jo meget smart ikk?

I kø-systemer er API-throttling også vigtig, fordi køerne kan skabe en stor belastning på API'et, hvis der er for mange anmodninger på én gang. Hvis køsystemet ikke er optimeret til API-throttling, kan det føre til at køsystemet overbelaster API'et og derved skaber en langsom responstid eller endda kan resultere i nedetid.

Derfor er det vigtigt at tage hensyn til API-throttling, når du arbejder med køsystemer, for at sikre at systemet kører stabilt og optimalt.

API-throttling begrænser antallet af anmodninger, som en klient kan foretage over en given tidsperiode, og der er typisk tre hovedmetoder, der anvendes: burst rate, rate limit, og concurrent connections.

Burst rate begrænser antallet af anmodninger, der kan foretages på én gang, mens rate limit begrænser det samlede antal anmodninger, der kan foretages i løbet af en given tidsperiode. Concurrent connections begrænser det samtidige antal forbindelser til API'et. Disse metoder er typisk konfigureret af API-udbydere og kan variere fra API til API.

For at arbejde med API-throttling kan du, med fordel, integrere Redis Throttle i dit køsystem. Dette vil give dig mulighed for at styre anmodningshastigheden og undgå overbelastning. Redis Throttle bruger Redis-cache til at holde styr på antallet af anmodninger, der er foretaget inden for en bestemt tidsperiode, og kan konfigureres til at begrænse antallet af anmodninger til et bestemt antal pr. sekund. Det kan også konfigureres til at opretholde burst rate ved hjælp af en midlertidig liste, der holder anmodningerne, før de frigives på API'et.

Implementering af Redis Throttle i Laravel: Trin-for-trin-guide

Lad mig præsentere: "RedisThrottle", en PHP-klasse med navnet det meget sigende navn, som du kan anvende til at håndtere throttling i Laravel, når du forbinder med et eksternt API med en hård throttling begrænsning. Klassen implementerer et interface, der gør det muligt at angive et navn, så den kan bruges som en parent klasse til andre klasser, der henvender sig til et specifikt API. Funktionen "throttle" i klassen indeholder Redis-throttling, som giver mulighed for at angive antallet af anmodninger, der er tilladt inden for en bestemt tidsperiode.

Lad os se på sagerne.

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Redis;
use Illuminate\Cache\RateLimiter;
use Illuminate\Http\Exceptions\ThrottleRequestsException;

class RedisThrottle implements ThrottleInterface
{
    protected $name;

    public function __construct($name)
    {
        $this->name = $name;
    }

    public static function request($callback, $allow = 120, $every = 60, $block = 60, $release = 60)
    {
        Redis::throttle(self::$name)->allow($allow)->every($every)->block($block)->then(function () use ($callback) {
            $callback();
        }, function () use ($release) {
            return $this->release($release);
        });
    }
    protected function release($release)
    {
        throw new ThrottleRequestsException('Too many requests. Please try again later.', null, $release);
    }
}

Så langt så godt.

Du kan nu implementere denne klasse som en parent på klasser, der henvender sig til specifikke API'er. Det er i hvert fald sådan jeg ville foretrække at gøre det.

Her er et eksempel på en child-klasse med navnet "InstagramThrottle", der arver fra "RedisThrottle" klassen. Denne klasse kan anvendes som en specifik throttling-mekanisme til Instagram API'et (selvom de måske ikke har den mest aggressive throttling begrænsning):

<?php

namespace App\Http\Middleware;

class InstagramThrottle extends RedisThrottle
{
    public function __construct()
    {
        parent::__construct('instagram');
    }
}

Hvis du forventer at skulle foretage mange kald til Instagram API'et på kort tid i fremtiden, kan du bruge InstagramThrottle::request() metoden til at omringe dine API-kald.

Dette vil begrænse antallet af anmodninger, der kan sendes til API'et, så du undgår at blive blokeret eller få fejlmeddelelser. Dette kan gøre dit Laravel-projekt mere pålideligt og reducere risikoen for fejl.

Her er et eksempel på brug af metoden:

use App\Http\Middleware\InstagramThrottle;

InstagramThrottle::request(function () {
    // Connect med Instagram's API
});

That's it folks. Nu er du (også) ekspert i at håndtere aggressiv throttling fra eksterne API'er i Laravel Queue's system!

Hvis du sidder fast i et Laravel-projekt og har brug for hjælp, så er jeg mere end villig til at tage udfordringen op og hjælpe dig med at løse dine problemer. Du er velkommen til at kontakte mig via e-mail på cp@codebypanduro.dk eller ringe til mig på +45 22 62 34 62.