Um Rate Limiting einfacher auszudrücken, handelt es sich um eine Technik, bei der wir die Anzahl der Anfragen begrenzen, die ein Benutzer oder Client innerhalb eines bestimmten Zeitraums an eine API stellen kann. Möglicherweise haben Sie in der Vergangenheit die Meldung „Ratenlimit überschritten“ erhalten, wenn Sie versucht haben, auf eine Wetter- oder eine Witz-API zuzugreifen. Es gibt viele Argumente dafür, warum man eine API begrenzen sollte, aber einige wichtige sind, sie fair zu nutzen, sie sicher zu machen, Ressourcen vor Überlastung zu schützen usw.
In diesem Blog erstellen wir einen HTTP-Server mit Golang unter Verwendung des Gin-Frameworks, wenden mithilfe von Redis eine Ratenbegrenzungsfunktion auf einen Endpunkt an und speichern die Gesamtzahl der von einer IP an den Server gestellten Anforderungen in einem bestimmten Zeitraum. Und wenn es das von uns festgelegte Limit überschreitet, geben wir eine Fehlermeldung aus.
Falls Sie keine Ahnung haben, was Gin und Redis sind. Gin ist ein in Golang geschriebenes Webframework. Es hilft, einen einfachen und schnellen Server zu erstellen, ohne viel Code schreiben zu müssen. Redis ist ein In-Memory- und Schlüsselwert-Datenspeicher, der als Datenbank oder für Caching-Funktionen verwendet werden kann.
Jetzt fangen wir an.
Um das Projekt zu initialisieren, gehen Sie zu mod init
Dann erstellen wir einen einfachen HTTP-Server mit Gin Framework und wenden dann die Logik zur Ratenbegrenzung an. Sie können den Code unten kopieren. Es ist sehr einfach. Der Server antwortet mit einer Nachricht, wenn wir den Endpunkt /message erreichen.
Nachdem Sie den folgenden Code kopiert haben, führen Sie „go mod Tidy“ aus, um die von uns importierten Pakete automatisch zu installieren.
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 }
Wir können den Server ausführen, indem wir go run main.go im Terminal ausführen und diese Meldung im Terminal sehen.
Um es zu testen, können wir zu localhost:8081/message gehen. Wir werden diese Nachricht im Browser sehen.
Jetzt läuft unser Server. Richten wir eine Ratenbegrenzungsfunktion für die /message-Route ein. Wir werden das Paket go-redis/redis_rate verwenden. Dank des Erstellers dieses Pakets müssen wir die Logik für die Handhabung und Überprüfung des Limits nicht von Grund auf neu schreiben. Es wird die ganze schwere Arbeit für uns erledigen.
Unten finden Sie den vollständigen Code nach der Implementierung der Ratenbegrenzungsfunktion. Wir werden jeden Teil davon verstehen. Geben Sie einfach frühzeitig den vollständigen Code an, um Verwirrung zu vermeiden und zu verstehen, wie verschiedene Teile zusammenarbeiten.
Sobald Sie den Code kopiert haben, führen Sie „Mod Tidy“ aus, um alle importierten Pakete zu installieren. Lassen Sie uns nun springen und den Code verstehen (unter dem Codeausschnitt).
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 }
Let's first directly jump to the rateLimiter() function and understand it. This function asks for an argument that is the IP address of the request which we can obtain via c.ClientIP() in the main function. And we return an error if there is limit is hit otherwise keep it nil. Most of the code is boilerplate code which we took from the official GitHub repo. The key functionality to look closer into here is the limiter.Allow() function. Addr: takes the URL path value for the Redis instance. I am using Docker to run it locally. You can use anything, just make sure you replace the URL accordingly.
res, err := limiter.Allow(ctx, clientIP, redis_rate.PerMinute(10))
It takes three arguments, the first is ctx, the second one is Key, Key (key for a value) for the Redis Database, and the third one is the the limit. So, the function stores the clientIP address as a key and the default limit as the value and reduces it when a request is made. The reason for this structure is that the Redis database needs unique identification and a unique key for storing key-value pairs kind of data, and every IP address is unique in its own way, this is why we are using IP addresses instead of usernames, etc. The 3rd argument redis_rate.PerMinute(10) can be modified as per our need, we can set limit PerSecond, PerHour, etc, and set the value inside parentheses for how many requests can be made per minute/second/hour. In our case, it's 10 per minute. Yes, it's that simple to set.
At last, we are checking if there is a remaining quota of not by res.Remaining. If it's zero we will return an error with the message otherwise we'll return nil. For eg, you can also do res.Limit.Rate to check the limit rate, etc. You can play around and dig deeper into that.
Now coming the main() function:
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") }
Everything is almost the same. In the /message route, now every time the route gets hit, we call the rateLimit() function and pass it a ClientIP address and store the return value (error) value in the err variable. If there is an error we will return a 429, that is, http.StatusTooManyRequests, and a message "message": "You have hit the limit". If the person has a remaining limit and the rateLimit() returns no error it will work normally, as it did earlier and serve the request.
That was all the explanation. Let's now test the working. Re-run the server by executing the same command. For the 1st time, we will see the same message we got earlier. Now refresh your browser 10 times (As we set a limit of 10 per minute), and you will see the error message in the browser.
We can also verify this by seeing the logs in the terminal. Gin offers great logging out of the box. After a minute it will restore our limit quota.
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.
Das obige ist der detaillierte Inhalt vonRatenbegrenzung einer Golang-API mithilfe von Redis. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!