Home  >  Article  >  Backend Development  >  Golang implements IP current limiting

Golang implements IP current limiting

王林
王林Original
2023-05-10 09:21:06577browse

With the rapid development of the Internet and mobile Internet, servers are facing increasing pressure. How to limit client requests to the server and avoid server crashes has become a big problem. In actual projects, we often need to limit the number of IP requests to ensure the availability of the website.

Here, we will introduce how to use Golang to implement IP current limiting. In general, we will use the token bucket algorithm to implement current limiting based on IP address. The token bucket algorithm is a flow control algorithm that allows a certain number of requests to pass within a certain period of time and limits the request flow within a period of time.

Implementation details

The token bucket algorithm puts tokens into the bucket at a constant rate. The token bucket has a capacity limit, which means that the number of tokens in the bucket will not exceed the capacity. For each request, a token is removed from the bucket. If there is no token in the bucket, the request cannot go through.

In order to implement current limiting based on IP address, we need to create a token bucket for each IP address. Each token bucket has a maximum capacity and a constant speed. When a request comes, we remove a token from the token bucket and reject the request if there is no token in the bucket.

Based on this, we can define an IP current limiter:

type IPRateLimiter struct {
    limiterBuckets map[string]*rate.Limiter
    mu             *sync.Mutex
    r              rate.Limit
    b              int
}

Among them, limiterBuckets is a mapping that maps string IP addresses to token buckets. mu is a mutex, r is the rate at which the rate limiter puts tokens per second, and b is the capacity of the token bucket.

In order to create a token bucket for each IP address, we define a function NewIPRateLimiter:

func NewIPRateLimiter(r rate.Limit, b int) *IPRateLimiter {
    return &IPRateLimiter{
        limiterBuckets: make(map[string]*rate.Limiter),
        mu:             &sync.Mutex{},
        r:              r,
        b:              b,
    }
}

func (i *IPRateLimiter) AddIP(ip string) *rate.Limiter {
    i.mu.Lock()
    defer i.mu.Unlock()

    limiter := rate.NewLimiter(i.r, i.b)
    i.limiterBuckets[ip] = limiter

    return limiter
}

The AddIP function is used to create a token bucket for the IP address. If a token bucket was created for this IP address, the existing token bucket is returned, otherwise a new token bucket is created and returned.

Finally, we can implement HTTP middleware to limit the number of requests to an IP address:

func (i *IPRateLimiter) Limit(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ip := r.RemoteAddr
        limiter, ok := i.limiterBuckets[ip]
        if !ok {
            limiter = i.AddIP(ip)
        }

        if !limiter.Allow() {
            http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
            return
        }

        next.ServeHTTP(w, r)
    })
}

This middleware allows requests to pass at a specified rate and capacity. If the number of requests exceeds capacity, HTTP error code 429 (Too Many Requests) is returned.

The complete code is as follows:

package main

import (
    "net/http"
    "strconv"
    "sync"

    "golang.org/x/time/rate"
)

type IPRateLimiter struct {
    limiterBuckets map[string]*rate.Limiter
    mu             *sync.Mutex
    r              rate.Limit
    b              int
}

func NewIPRateLimiter(r rate.Limit, b int) *IPRateLimiter {
    return &IPRateLimiter{
        limiterBuckets: make(map[string]*rate.Limiter),
        mu:             &sync.Mutex{},
        r:              r,
        b:              b,
    }
}

func (i *IPRateLimiter) AddIP(ip string) *rate.Limiter {
    i.mu.Lock()
    defer i.mu.Unlock()

    limiter := rate.NewLimiter(i.r, i.b)
    i.limiterBuckets[ip] = limiter

    return limiter
}

func (i *IPRateLimiter) Limit(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ip := r.RemoteAddr
        limiter, ok := i.limiterBuckets[ip]
        if !ok {
            limiter = i.AddIP(ip)
        }

        if !limiter.Allow() {
            http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
            return
        }

        next.ServeHTTP(w, r)
    })
}

func IndexHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("welcome."))
}

func main() {
    limit := rate.Limit(10) // 速率,每秒放入令牌的数量
    capacity := 100        // 容量,桶的大小

    ipRateLimiter := NewIPRateLimiter(limit, capacity)

    http.Handle("/", ipRateLimiter.Limit(http.HandlerFunc(IndexHandler)))

    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        panic(err)
    }
}

In this example, we allow 10 tokens per second and limit the bucket capacity to 100. This means that the limiter can handle up to 10 requests per second, but will fail if the requests to the same IP address reach 100. At the same time, we define a simple handler that will respond to "Welcome".

Conclusion

In this article, we use Golang to implement IP current limiting and use the token bucket algorithm to limit the request rate of each IP address. This method can implement a simple and effective current limiting mechanism, and can be easily implemented in Golang. This can be a very useful technique when you are writing highly concurrent network applications.

The above is the detailed content of Golang implements IP current limiting. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn