Limitation du débit au printemps Netflix Zuul

Limitation de taux au printemps Cloud Netflix Zuul

1. introduction

Spring Cloud Netflix Zuul est une passerelle open source qui encapsuleNetflix Zuul. Il ajoute des fonctionnalités spécifiques pour les applications Spring Boot. Malheureusement, la limitation de taux n’est pas fournie en sortie de boîte.

Dans ce didacticiel, nous exploreronsSpring Cloud Zuul RateLimit qui ajoute la prise en charge des demandes de limitation de débit.

2. Configuration Maven

En plus de la dépendance Spring Cloud Netflix Zuul, nous devons ajouterSpring Cloud Zuul RateLimit auxpom.xml de notre application:


    org.springframework.cloud
    spring-cloud-starter-netflix-zuul


    com.marcosbarbero.cloud
    spring-cloud-zuul-ratelimit
    2.2.0.RELEASE

3. Exemple de contrôleur

Tout d'abord, créons quelques points de terminaison REST sur lesquels nous appliquerons les limites de taux.

Vous trouverez ci-dessous une classe Spring Controller simple avec deux points de terminaison:

@Controller
@RequestMapping("/greeting")
public class GreetingController {

    @GetMapping("/simple")
    public ResponseEntity getSimple() {
        return ResponseEntity.ok("Hi!");
    }

    @GetMapping("/advanced")
    public ResponseEntity getAdvanced() {
        return ResponseEntity.ok("Hello, how you doing?");
    }
}

Comme on peut le constater, il n’existe pas de code spécifique pour limiter les extrémités. C'est parce que nous allons configurer cela dans nos propriétés Zuul dans le fichierapplication.yml. Ainsi, en gardant notre code découplé.

4. Propriétés de Zuul

Deuxièmement, ajoutons les propriétés Zuul suivantes dans notre fichierapplication.yml:

zuul:
  routes:
    serviceSimple:
      path: /greeting/simple
      url: forward:/
    serviceAdvanced:
      path: /greeting/advanced
      url: forward:/
  ratelimit:
    enabled: true
    repository: JPA
    policy-list:
      serviceSimple:
        - limit: 5
          refresh-interval: 60
          type:
            - origin
      serviceAdvanced:
        - limit: 1
          refresh-interval: 2
          type:
            - origin
  strip-prefix: true

Souszuul.routes, nous fournissons les détails du point de terminaison. Et souszuul.ratelimit.policy-list,, nous fournissons les configurations de limite de débit pour nos terminaux. La propriétélimit spécifie le nombre de fois où le noeud final peut être appelé dans lesrefresh-interval.

Comme nous pouvons le voir, nous avons ajouté une limite de débit de 5 requêtes par 60 secondes pour le point de terminaisonserviceSimple. En revanche,serviceAdvanced a une limite de débit de 1 demande toutes les 2 secondes.

La configuration detype spécifie l'approche de limite de débit que nous voulons suivre. Voici les valeurs possibles:

  • origin - limite de débit basée sur la demande d'origine de l'utilisateur

  • url - limite de débit basée sur le chemin de demande du service en aval

  • user - limite de taux basée sur le nom d'utilisateur authentifié ou «anonyme»

  • Aucune valeur - agit comme une configuration globale par service. Pour utiliser cette approche, ne définissez pas le paramètre 'type'

5. Test de la limite de débit

5.1. Demande dans la limite de taux

Ensuite, testons la limite de taux:

@Test
public void whenRequestNotExceedingCapacity_thenReturnOkResponse() {
    ResponseEntity response = restTemplate.getForEntity(SIMPLE_GREETING, String.class);
    assertEquals(OK, response.getStatusCode());

    HttpHeaders headers = response.getHeaders();
    String key = "rate-limit-application_serviceSimple_127.0.0.1";

    assertEquals("5", headers.getFirst(HEADER_LIMIT + key));
    assertEquals("4", headers.getFirst(HEADER_REMAINING + key));
    assertEquals("60000", headers.getFirst(HEADER_RESET + key));
}

Ici, nous faisons un seul appel au point final/greeting/simple. La demande aboutit car elle se situe dans la limite du débit.

Un autre point clé est quewith each response we get back headers providing us with further information on the rate limit. Pour la demande ci-dessus, nous obtiendrions les en-têtes suivants:

X-RateLimit-Limit-rate-limit-application_serviceSimple_127.0.0.1: 5
X-RateLimit-Remaining-rate-limit-application_serviceSimple_127.0.0.1: 4
X-RateLimit-Reset-rate-limit-application_serviceSimple_127.0.0.1: 60000

En d'autres termes:

  • X-RateLimit-Limit-[key]: leslimit configurés pour le noeud final

  • X-RateLimit-Remaining-[key]: le nombre restant de tentatives d'appel du point final

  • X-RateLimit-Reset-[key]: le nombre de millisecondes restantes desrefresh-interval configurés pour le noeud final

De plus, si nous renvoyons immédiatement le même point de terminaison, nous pourrions obtenir:

X-RateLimit-Limit-rate-limit-application_serviceSimple_127.0.0.1: 5
X-RateLimit-Remaining-rate-limit-application_serviceSimple_127.0.0.1: 3
X-RateLimit-Reset-rate-limit-application_serviceSimple_127.0.0.1: 57031

Notez la diminution du nombre de tentatives restantes et du nombre de millisecondes restantes.

5.2. Demande de dépassement de la limite de taux

Voyons ce qui se passe lorsque nous dépassons la limite de taux:

@Test
public void whenRequestExceedingCapacity_thenReturnTooManyRequestsResponse() throws InterruptedException {
    ResponseEntity response = this.restTemplate.getForEntity(ADVANCED_GREETING, String.class);
    assertEquals(OK, response.getStatusCode());

    for (int i = 0; i < 2; i++) {
        response = this.restTemplate.getForEntity(ADVANCED_GREETING, String.class);
    }

    assertEquals(TOO_MANY_REQUESTS, response.getStatusCode());

    HttpHeaders headers = response.getHeaders();
    String key = "rate-limit-application_serviceAdvanced_127.0.0.1";

    assertEquals("1", headers.getFirst(HEADER_LIMIT + key));
    assertEquals("0", headers.getFirst(HEADER_REMAINING + key));
    assertNotEquals("2000", headers.getFirst(HEADER_RESET + key));

    TimeUnit.SECONDS.sleep(2);

    response = this.restTemplate.getForEntity(ADVANCED_GREETING, String.class);
    assertEquals(OK, response.getStatusCode());
}

Ici, nous appelons le point final/greeting/advanced deux fois par successions rapides. Puisque nous avons configuré la limite de débit comme une demande toutes les 2 secondes,the second call will fail. En conséquence, le code d'erreur429 (Too Many Requests) est renvoyé au client.

Vous trouverez ci-dessous les en-têtes renvoyés lorsque la limite de débit est atteinte:

X-RateLimit-Limit-rate-limit-application_serviceAdvanced_127.0.0.1: 1
X-RateLimit-Remaining-rate-limit-application_serviceAdvanced_127.0.0.1: 0
X-RateLimit-Reset-rate-limit-application_serviceAdvanced_127.0.0.1: 268

Après cela, nous dormons 2 secondes. Il s'agit desrefresh-interval configurés pour le noeud final. Enfin, nous renvoyons le système d'extrémité à nouveau et obtenons une réponse satisfaisante.

6. Générateur de clés personnalisées

We can customize the keys sent in the response header using a custom key generator. Ceci est utile car l'application peut avoir besoin de contrôler la stratégie de clé au-delà des options offertes par la propriététype.

Par exemple, cela peut être fait en créant une implémentationRateLimitKeyGenerator personnalisée. Nous pouvons ajouter d'autres qualificatifs ou quelque chose de complètement différent:

@Bean
public RateLimitKeyGenerator rateLimitKeyGenerator(RateLimitProperties properties,
  RateLimitUtils rateLimitUtils) {
    return new DefaultRateLimitKeyGenerator(properties, rateLimitUtils) {
        @Override
        public String key(HttpServletRequest request, Route route,
          RateLimitProperties.Policy policy) {
            return super.key(request, route, policy) + "_" + request.getMethod();
        }
    };
}

Le code ci-dessus ajoute le nom de la méthode REST à la clé. Par exemple:

X-RateLimit-Limit-rate-limit-application_serviceSimple_127.0.0.1_GET: 5

Un autre point clé est que le beanRateLimitKeyGenerator sera automatiquement configuré parspring-cloud-zuul-ratelimit.

7. Gestion personnalisée des erreurs

La structure prend en charge diverses implémentations pour le stockage de données à limite de débit. Par exemple, Spring Data JPA et Redis sont fournis. Par défaut,failures are just logged as errorsutilise la classeDefaultRateLimiterErrorHandler.

Lorsque nous devons gérer les erreurs différemment, nous pouvons définir un beanRateLimiterErrorHandler personnalisé:

@Bean
public RateLimiterErrorHandler rateLimitErrorHandler() {
    return new DefaultRateLimiterErrorHandler() {
        @Override
        public void handleSaveError(String key, Exception e) {
            // implementation
        }

        @Override
        public void handleFetchError(String key, Exception e) {
            // implementation
        }

        @Override
        public void handleError(String msg, Exception e) {
            // implementation
        }
    };
}

Similaire au beanRateLimitKeyGenerator, le beanRateLimiterErrorHandler sera également configuré automatiquement.

8. Conclusion

Dans cet article, nous avons vu comment évaluer les API de limite avec Spring Cloud Netflix Zuul et Spring Cloud Zuul RateLimit.

Comme toujours, le code complet de cet article se trouveover on GitHub.