Warum Rate Limiting? Weil Teilen wichtig ist (aber nicht zu viel)

Bevor wir uns in den Code stürzen, sprechen wir darüber, warum Rate Limiting der Superheld ist, den Ihre API braucht:

  • Verhindert, dass übermäßig eifrige Clients Ressourcen überbeanspruchen
  • Schützt vor unbeabsichtigten DoS-Angriffen
  • Gewährleistet eine faire Nutzung für alle Benutzer
  • Hilft bei der Kosten- und Skalierungsverwaltung

Stellen Sie es sich wie einen Türsteher für Ihren API-Club vor. Er sorgt dafür, dass die Party weitergeht, ohne dass ein Gast die Tanzfläche für sich beansprucht.

Das dynamische Duo: Spring Boot und Redis

Wir kombinieren Spring Boot mit Redis für dieses Abenteuer. Warum? Weil sie wie Erdnussbutter und Marmelade sind – sie funktionieren einfach gut zusammen. Spring Boot bietet uns die Flexibilität des Frameworks, während Redis die Geschwindigkeit und verteilte Natur bringt, die wir für effektives Rate Limiting benötigen.

Die Bühne vorbereiten

Zuallererst, lassen Sie uns unsere Abhängigkeiten in Ordnung bringen. Fügen Sie diese zu Ihrer pom.xml hinzu:


<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson-spring-boot-starter</artifactId>
        <version>3.16.0</version>
    </dependency>
</dependencies>

Jetzt konfigurieren wir Redis in unserer application.properties:


spring.redis.host=localhost
spring.redis.port=6379

Der Rate Limiter: Der neue beste Freund Ihrer API

Es ist Zeit, unseren Rate Limiter zu erstellen. Wir verwenden einen einfachen Sliding-Window-Algorithmus, der mit Redis implementiert wird.


import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

@Component
public class RedisRateLimiter {

    private final RedissonClient redissonClient;

    public RedisRateLimiter(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    public boolean tryAcquire(String key) {
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
        rateLimiter.trySetRate(RateType.OVERALL, 10, 1, RateIntervalUnit.MINUTES);
        return rateLimiter.tryAcquire();
    }
}

Diese Konfiguration erlaubt 10 Anfragen pro Minute. Passen Sie sie nach Bedarf an Ihren Anwendungsfall an.

Der Torwächter: Eine benutzerdefinierte Annotation

Lassen Sie uns eine benutzerdefinierte Annotation erstellen, um Rate Limiting einfach auf unsere Endpunkte anzuwenden:


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    String key() default "";
}

Der Interceptor: Wo die Magie passiert

Jetzt kommt das Herzstück – unser Interceptor:


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Aspect
@Component
public class RateLimitInterceptor {

    private final RedisRateLimiter rateLimiter;

    public RateLimitInterceptor(RedisRateLimiter rateLimiter) {
        this.rateLimiter = rateLimiter;
    }

    @Around("@annotation(rateLimit)")
    public Object interceptRateLimit(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        String key = rateLimit.key() + ":" + request.getRemoteAddr();

        if (!rateLimiter.tryAcquire(key)) {
            throw new RateLimitExceededException("Rate limit überschritten. Versuchen Sie es später erneut.");
        }

        return joinPoint.proceed();
    }
}

Alles zusammenfügen

Sehen wir uns nun unseren Rate Limiter in Aktion an:


@RestController
@RequestMapping("/api")
public class ApiController {

    @GetMapping("/greet")
    @RateLimit(key = "greeting")
    public String greet() {
        return "Hallo, Welt!";
    }
}

Der Haken: Ausnahmebehandlung

Vergessen Sie nicht, die lästigen Rate-Limit-Ausnahmen zu behandeln:


@ControllerAdvice
public class RateLimitExceptionHandler {

    @ExceptionHandler(RateLimitExceededException.class)
    public ResponseEntity<String> handleRateLimitExceededException(RateLimitExceededException ex) {
        return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(ex.getMessage());
    }
}

Die Nachwirkungen: Überwachung und Anpassung

Herzlichen Glückwunsch! Sie haben gerade ein robustes Rate-Limiting-System implementiert. Aber die Reise endet hier nicht. Behalten Sie Ihre Protokolle und Metriken im Auge. Möglicherweise müssen Sie Ihre Rate-Limits basierend auf realen Nutzungsmustern anpassen.

Profi-Tipp: Redis Insights

Verwenden Sie Redis Insights, um Ihr Rate Limiting in Aktion zu visualisieren. Es ist, als hätten Sie Röntgenblick für Ihre Redis-Daten!

Zusammenfassung: Die Kraft der Zurückhaltung

Da haben Sie es – eine elegante, Redis-gestützte Rate-Limiting-Lösung für Ihre Spring Boot API. Sie haben Ihrer API die Fähigkeit gegeben, "Langsam, Freund!" zu sagen, wenn es zu hektisch wird.

Denken Sie daran, das Ziel ist nicht, Ihre Benutzer zu frustrieren, sondern sicherzustellen, dass jeder ein faires Stück vom API-Kuchen bekommt. Passen Sie Ihre Limits weise an, und mögen Ihre Server immer cool bleiben!

"Mit großer Macht kommt große Verantwortung" - Onkel Ben (und jeder API-Entwickler jemals)

Denkanstöße

Während Sie Rate Limiting implementieren, überlegen Sie sich diese Fragen:

  • Wie werden Sie die Rate-Limits Ihren API-Nutzern kommunizieren?
  • Was ist Ihre Strategie für den Umgang mit plötzlichem Traffic?
  • Wie wird Rate Limiting die SLAs Ihrer API beeinflussen?

Viel Spaß beim Programmieren, und mögen Ihre APIs immer reaktionsschnell und missbrauchsfrei sein!