Pour dire Rate Limiting en termes plus simples, il s'agit d'une technique dans laquelle nous limitons le nombre de requêtes qu'un utilisateur ou un client peut faire à une API dans un laps de temps donné. Vous avez peut-être déjà rencontré un message « limite de débit dépassée » lorsque vous essayiez d'accéder à une API météo ou à une blague. Il existe de nombreux arguments sur les raisons de limiter le taux d'une API, mais les plus importants sont d'en faire un usage équitable, de la sécuriser, de protéger les ressources contre la surcharge, etc.
Dans ce blog, nous allons créer un serveur HTTP avec Golang en utilisant le framework Gin, appliquer une fonctionnalité de limite de débit à un point de terminaison à l'aide de Redis et stocker le nombre total de requêtes effectuées par une IP adressée au serveur dans un laps de temps. Et si elle dépasse la limite que nous avons fixée, nous donnerons un message d'erreur.
Au cas où vous n'auriez aucune idée de ce que sont Gin et Redis. Gin est un framework web écrit en Golang. Cela permet de créer un serveur simple et rapide sans écrire beaucoup de code. Redis est un magasin de données en mémoire et avec valeurs-clés qui peut être utilisé comme base de données ou pour des capacités de mise en cache.
Maintenant, commençons.
Pour initialiser l'exécution du projet, allez mod init
Ensuite, créons un simple serveur HTTP avec Gin Framework puis nous appliquons la logique pour le limiter. Vous pouvez copier le code ci-dessous. C'est très basique. Le serveur répondra avec un message lorsque nous atteindrons le point de terminaison /message.
Après avoir copié le code ci-dessous, exécutez go mod spice pour installer automatiquement les packages que nous avons importés.
package main import ( "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/message", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "You can make more requests", }) }) r.Run(":8081") //listen and serve on localhost:8081 }
Nous pouvons exécuter le serveur en exécutant go run main.go dans le terminal et voir ce message dans le terminal.
Pour le tester, on peut aller sur localhost:8081/message nous verrons ce message dans le navigateur.
Maintenant que notre serveur est en cours d'exécution, configurons une fonctionnalité de limite de débit pour la route /message. Nous utiliserons le package go-redis/redis_rate. Grâce au créateur de ce package, nous n'avons pas besoin d'écrire la logique de gestion et de vérification de la limite à partir de zéro. Cela fera tout le gros du travail pour nous.
Vous trouverez ci-dessous le code complet après implémentation de la fonctionnalité de limitation de débit. Nous en comprendrons chaque élément. Je viens de donner le code complet plus tôt pour éviter toute confusion et comprendre comment les différentes pièces fonctionnent ensemble.
Une fois que vous avez copié le code, exécutez mod spice pour installer tous les packages importés. Passons maintenant et comprenons le code (sous l'extrait de code).
package main import ( "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/message", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "You can make more requests", }) }) r.Run(":8081") //listen and serve on localhost:8081 }
Commençons d'abord directement par la fonction rateLimiter() et comprenons-la. Cette fonction demande un argument qui est l'adresse IP de la requête que l'on peut obtenir via c.ClientIP() dans la fonction principale. Et nous renvoyons une erreur si la limite est atteinte, sinon gardez-la nulle. La plupart du code est du code passe-partout que nous avons extrait du dépôt officiel GitHub. La fonctionnalité clé à examiner de plus près ici est la fonction limiteur.Allow(). Addr : prend la valeur du chemin URL pour l’instance Redis. J'utilise Docker pour l'exécuter localement. Vous pouvez utiliser n'importe quoi, assurez-vous simplement de remplacer l'URL en conséquence.
package main import ( "context" "errors" "net/http" "github.com/gin-gonic/gin" "github.com/go-redis/redis_rate/v10" "github.com/redis/go-redis/v9" ) func main() { r := gin.Default() r.GET("/message", func(c *gin.Context) { err := rateLimiter(c.ClientIP()) if err != nil { c.JSON(http.StatusTooManyRequests, gin.H{ "message": "you have hit the limit", }) return } c.JSON(http.StatusOK, gin.H{ "message": "You can make more requests", }) }) r.Run(":8081") } func rateLimiter(clientIP string) error { ctx := context.Background() rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) limiter := redis_rate.NewLimiter(rdb) res, err := limiter.Allow(ctx, clientIP, redis_rate.PerMinute(10)) if err != nil { panic(err) } if res.Remaining == 0 { return errors.New("Rate") } return nil }
Il faut trois arguments, le premier est ctx, le deuxième est Key, Key (clé pour une valeur) pour la base de données Redis et le troisième est la limite. Ainsi, la fonction stocke l'adresse clientIP comme clé et la limite par défaut comme valeur et la réduit lorsqu'une demande est faite. La raison de cette structure est que la base de données Redis a besoin d'une identification unique et d'une clé unique pour stocker des données de type paire clé-valeur, et chaque adresse IP est unique à sa manière, c'est pourquoi nous utilisons des adresses IP au lieu de noms d'utilisateur, etc. Le 3ème argument redis_rate.PerMinute(10) peut être modifié selon nos besoins, nous pouvons définir la limite PerSecond, PerHour, etc, et définissez la valeur entre parenthèses pour le nombre de requêtes pouvant être effectuées par minute/seconde/heure. Dans notre cas, c'est 10 par minute. Oui, c'est aussi simple à régler.
Enfin, nous vérifions s'il reste un quota de non par res.Remaining. Si c'est zéro, nous renverrons une erreur avec le message sinon nous renverrons zéro. Par exemple, vous pouvez également faire res.Limit.Rate pour vérifier le taux limite, etc. Vous pouvez jouer et approfondir cela.
Vient maintenant la fonction main() :
res, err := limiter.Allow(ctx, clientIP, redis_rate.PerMinute(10))
Tout est presque pareil. Dans la route /message, désormais, chaque fois que la route est atteinte, nous appelons la fonction rateLimit() et lui transmettons une adresse ClientIP et stockons la valeur de retour (erreur) dans la variable err. S'il y a une erreur, nous renverrons un 429, c'est-à-dire http.StatusTooManyRequests, et un message "message": "Vous avez atteint la limite". Si la personne a une limite restante et que rateLimit() ne renvoie aucune erreur, elle fonctionnera normalement, comme elle l'a fait précédemment et répondra à la demande.
C'était toute l'explication. Testons maintenant le fonctionnement. Réexécutez le serveur en exécutant la même commande. Pour la première fois, nous verrons le même message que nous avons reçu plus tôt. Actualisez maintenant votre navigateur 10 fois (car nous fixons une limite de 10 par minute) et vous verrez le message d'erreur dans le navigateur.
Nous pouvons également le vérifier en consultant les journaux dans le terminal. Gin offre une excellente déconnexion. Après une minute, notre quota limite sera rétabli.
C'est arrivé à la fin de ce blog, j'espère que vous prendrez autant de plaisir à lire que j'aime écrire ceci. Je suis heureux que vous soyez arrivé jusqu'au bout, merci beaucoup pour votre soutien. Je parle aussi régulièrement de Golang et d'autres trucs comme l'Open Source et Docker sur X (Twitter). Vous pouvez me connecter là-bas.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!