Limitation de débit dans la gestion des API
September 16, 2022
Avec le développement d'Internet, de plus en plus d'entreprises ont commencé à adopter le cloud-native et les microservices. Cependant, en raison des caractéristiques techniques du cloud-native et des microservices, nous devons gérer simultanément des centaines de services différents. Par conséquent, non seulement nous devons considérer le bon fonctionnement de l'ensemble du système, mais nous devons également veiller à la sécurité et à la stabilité de chaque service basé sur des API.
La limitation de débit (rate limiting) est l'une des solutions les plus critiques pour garantir la stabilité des services basés sur des API. Cependant, l'application deviendrait extrêmement encombrée si chaque service nécessitait une limitation de débit. En tant que point d'entrée et de sortie de tout le trafic dans le monde numérique, la passerelle API (API gateway) permet de réaliser une gestion unifiée des API pour tous les services. Elle protège le fonctionnement stable des services du système. Cet article montre comment nous mettons en œuvre la limitation de débit via la passerelle API Apache APISIX et décrit les stratégies et techniques de limitation de débit.
Qu'est-ce que la limitation de débit ?
La limitation de débit est une stratégie pour contrôler le trafic Internet et maximiser le débit. En utilisant la limitation de débit, seules les requêtes respectant une contrainte spécifique sont autorisées à accéder au système ; les requêtes supplémentaires qui dépassent cette contrainte sont mises en file d'attente, voire refusées ou abandonnées. La limitation de débit protège également le système contre des incidents inattendus tels que des pics de trafic ou des attaques malveillantes, et permet au système de fournir des services cohérents et stables.
Par exemple, lorsqu'un tweet viral crée un pic de trafic, Twitter doit mettre en place une limitation de débit pour éviter que les serveurs ne tombent en panne en raison d'une surcharge de trafic.
Pourquoi en avez-vous besoin ?
Tout d'abord, examinons quelques cas d'utilisation simples dans la vie réelle où la limitation de débit est appliquée. Par exemple, les attractions touristiques ne peuvent vendre qu'un certain nombre de billets pour les vacances. De même, nous devons souvent réserver à l'avance ou attendre longtemps avant de pouvoir profiter de la nourriture dans les restaurants populaires.
Dans la passerelle API, la limitation de débit présente également de nombreux avantages. Elle impose certaines contraintes sur nos services basés sur des API, garantit leur bon fonctionnement et évite les pertes inutiles causées par des pannes de serveur dues à des pics de trafic. Voici cinq contraintes pratiques différentes :
- Limiter le taux de requêtes.
- Limiter le nombre de requêtes par unité de temps.
- Retarder les requêtes.
- Refuser les requêtes des clients.
- Limiter le taux de réponse.
Quand en avez-vous besoin ?
Associée à l'identification et à l'authentification, la limitation de débit peut maximiser ses avantages et améliorer la disponibilité du système de plusieurs manières :
- Éviter les attaques malveillantes.
- Garantir le fonctionnement stable du système et éviter les pannes de serveur dues à des pics de trafic.
- Empêcher les pannes de serveur dues à des pics de requêtes causés par des bugs générés par les services en amont ou en aval.
- Éviter des appels API coûteux trop fréquents.
- Réduire le gaspillage inutile de ressources en limitant la fréquence des appels API.
La théorie derrière la limitation de débit
Dans les sections précédentes, nous avons présenté les avantages de la limitation de débit. Dans cette section, découvrons les théories qui la sous-tendent ! La mise en œuvre de bas niveau de la limitation de débit est réalisée par des algorithmes spécifiques. Les plus couramment utilisés incluent :
- Algorithme du compteur
- Fenêtre fixe
- Fenêtre glissante
- Algorithme du seau percé
- Algorithme du seau à jetons
Algorithme du compteur
Un algorithme de compteur est relativement facile à comprendre, et il en existe deux types :
Le premier type est l'algorithme de fenêtre fixe, qui maintient un compteur dans une unité de temps fixe et réinitialise le compteur à zéro si l'unité de temps est écoulée.
Le second type est l'algorithme de fenêtre glissante, qui est une amélioration du premier ; il comprend les étapes suivantes :
- Diviser les unités de temps en plusieurs intervalles (appelés blocs).
- Chaque bloc a un compteur ; toute requête entrante incrémente le compteur de 1.
- Après un temps fixe, cette fenêtre de temps avance d'un bloc.
- Il calcule le total des requêtes dans cette fenêtre de temps en additionnant tous les compteurs des blocs dans la fenêtre de temps ; si le total des requêtes dépasse la contrainte, il abandonne toutes les requêtes dans cette fenêtre de temps.
Algorithme du seau percé
Imaginez un seau percé ; toutes les requêtes seraient d'abord mises en file d'attente, puis le seau percé les enverrait à un taux constant.
Si les requêtes dépassent la capacité du seau, le système abandonnerait et refuserait toute requête excédentaire. L'algorithme du seau percé peut limiter le taux de requêtes et garantir que toutes les requêtes sont envoyées à un taux constant, créant ainsi un mode facile à entrer mais difficile à sortir.
Les étapes clés de cet algorithme :
- Toutes les requêtes sont stockées dans un seau de taille fixe.
- Le seau enverrait les requêtes à un taux constant jusqu'à ce qu'il soit vide.
- Lorsque le seau est plein, le système abandonne toute requête supplémentaire.
Algorithme du seau à jetons
L'algorithme du seau à jetons se compose de deux parties : la génération de jetons et leur suppression. Le seau à jetons génère des jetons à un taux constant et les stocke dans un seau de stockage fixe. Lorsqu'une requête passe par le seau à jetons, elle prend un ou plusieurs jetons. Lorsque le nombre de jetons dans le seau atteint la capacité maximale, le seau à jetons abandonne les nouveaux jetons générés. De plus, le seau de stockage rejettera les requêtes entrantes s'il ne reste plus de jetons.
Les étapes clés de l'algorithme du seau à jetons :
- Le seau à jetons génère des jetons à un taux constant et les place dans le seau de stockage.
- Si le seau à jetons est plein, les nouveaux jetons générés sont directement abandonnés. Lorsqu'une requête arrive, elle prend un ou plusieurs jetons du seau de stockage.
- S'il ne reste plus de jetons dans le seau à jetons, le système rejette toute requête entrante.
Mettre en œuvre la limitation de débit via la passerelle API
Si seulement quelques services basés sur des API doivent être maintenus, nous pourrions directement utiliser des algorithmes de limitation de débit dans le service. Par exemple, si vous utilisez Go pour développer votre système, il utilisera tollbooth
ou golang.org/x/time/rate
pour implémenter les algorithmes. Si vous utilisez Lua, vous pourriez utiliser les modules limit_req
, limit_conn
et Lua-resty-limit-traffic
de NGINX pour implémenter les algorithmes.
Si la limitation de débit est implémentée sur un service basé sur des API, les contraintes de limitation de débit seraient définies par le service lui-même, et chaque service pourrait avoir des contraintes différentes. Ces contraintes et différences poseraient des problèmes de gestion si le nombre de services basés sur des API augmentait considérablement. À ce stade, nous ne pourrions pas utiliser la passerelle API pour gérer tous les services API de manière uniforme. Vous pourriez également implémenter des fonctionnalités métier non liées sur la passerelle, comme l'identification, l'authentification, les logs, l'observabilité, etc., lorsque vous utilisez la passerelle API pour résoudre les problèmes de limitation de débit.
Apache APISIX est une passerelle cloud-native dynamique, en temps réel et haute performance. APISIX prend actuellement en charge plus de 80 plugins différents et a déjà construit un écosystème riche. Nous pouvons gérer le trafic des services basés sur des API en utilisant les plugins d'APISIX, notamment limit-req
, limit-conn
et limit-count
. Ici, je vais partager un cas d'utilisation pour démontrer l'utilisation des plugins de limitation de débit d'APISIX.
Supposons qu'il existe un service basé sur des API (/user/login
) qui aide les utilisateurs à se connecter. Pour éviter les attaques malveillantes et l'épuisement des ressources, nous devons activer la fonction de limitation de débit pour garantir la stabilité du système.
Limiter les requêtes
Le plugin limit-req
limite le taux de requêtes, il utilise l'algorithme du seau percé, et nous le lions à des routes spécifiques ou à des clients spécifiques.
Nous pouvons directement utiliser l'API Admin d'APISIX pour créer une telle route :
X-API-Key
est la admin_key dans la configuration d'APISIX.
curl http://127.0.0.1:9080/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"methods": ["POST"],
"uri": "/user/login",
"plugins": {
"limit-req": {
"rate": 3,
"burst": 2,
"rejected_code": 503,
"key": "remote_addr"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}'
Signification de cet extrait de code : Nous utilisons l'adresse IP du client comme condition pour limiter le taux de requêtes.
- Si le taux de requêtes est inférieur à 3 requêtes par seconde (
rate
), alors la requête est normale ; - Si le taux de requêtes est supérieur à 3 requêtes par seconde mais inférieur à 5 requêtes par seconde (
rate+burst
), nous ralentirons ces requêtes excédentaires ; - Si le taux de requêtes est supérieur à 5 requêtes par seconde (
rate+burst
), toute requête dépassant la contrainte maximale retournera le code HTTP 503.
Si vous souhaitez en savoir plus sur limit-req
, consultez ce document : APISIX limit-req
Limiter les connexions
Le plugin limit-conn
limite les requêtes parallèles (ou connexions parallèles). Voici un exemple de code pour activer ce plugin pour /user/login
:
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"methods": ["POST"],
"uri": "/user/login",
"id": 1,
"plugins": {
"limit-conn": {
"conn": 3,
"burst": 2,
"default_conn_delay": 0.1,
"rejected_code": 503,
"key": "remote_addr"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}'
Signification de cet extrait de code : Nous utilisons l'adresse IP du client comme condition pour limiter les connexions parallèles.
- Si les connexions parallèles du même client sont inférieures à 3 (
conn
), alors il répond avec un statut normal 200 ; - Si les connexions parallèles sont supérieures à 3 (
conn
) mais inférieures à 5 (conn+burst
), nous ralentirons les requêtes excédentaires et augmenterons le temps de délai de 0,1 seconde ; - Si les connexions parallèles sont supérieures à 5 (
conn+burst
), cette requête sera refusée et retournera le code HTTP 503.
Si vous souhaitez en savoir plus sur limit-conn
, consultez ce document : APISIX limit-conn
Limiter le nombre
Le plugin limit-count
est similaire à la limitation de débit de l'API de GitHub ; il limite le nombre total de requêtes dans un intervalle de temps spécifique et renvoie le nombre de requêtes restantes dans l'en-tête HTTP. Voici un exemple de code pour activer ce plugin pour /user/login
:
curl -i http://127.0.0.1:9080/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/user/login",
"plugins": {
"limit-count": {
"count": 3,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr",
"policy": "local"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:9001": 1
}
}
}'
Signification de cet extrait de code : Nous utilisons l'adresse IP du client comme condition pour limiter le nombre de requêtes ; le compteur est sauvegardé localement en mémoire.
S'il y a plus de 3 (count
) requêtes dans un intervalle de 60 secondes (time_window
), les requêtes envoyées au-delà de 3 fois retourneront le code HTTP 503 (rejected_code
).
Si vous souhaitez en savoir plus sur limit-count
, consultez ce document : APISIX limit-count
Les avantages de la limitation de débit d'Apache APISIX
Lorsque nous utilisons NGINX pour gérer le trafic, si le nombre de requêtes API crée un pic de trafic, NGINX expose ses limites, et l'une de ces limites est qu'il ne peut pas charger dynamiquement les configurations. D'un autre côté, les services d'APISIX (comme Route et Service) peuvent supporter le rechargement à chaud des configurations. Même en cas de pic de trafic, APISIX peut instantanément modifier la limitation de débit et les autres configurations de plugins de sécurité. Grâce au mécanisme de surveillance d'etcd, APISIX peut mettre à jour la couche de données en quelques millisecondes sans recharger les services.
En plus de cela, APISIX prend également en charge la limitation de débit au niveau du cluster. Par exemple, nous pouvons utiliser limit-count
pour modifier la configuration de policy
en redis
ou redis-cluster
. Ainsi, nous pouvons limiter les taux au niveau du cluster en partageant les résultats de calcul entre différents nœuds APISIX.
En tant que DevOps, utiliser un tableau de bord graphique pour gérer tous les services API augmenterait la productivité. APISIX fournit un tableau de bord de gestion visuel pour rendre la modification des configurations API beaucoup plus pratique.
Conclusion
La limitation de débit est un besoin courant dans les scénarios métier réels, et c'est un moyen crucial de protéger le système contre les pics de trafic et d'assurer son bon fonctionnement. La limitation de débit n'est qu'une partie de la gestion des services API ; nous pouvons également utiliser de nombreuses autres technologies pour fournir un support de sécurité vital et améliorer l'expérience utilisateur.