A distributed rate limiter written in Go, built to understand how real-world systems handle traffic at scale.
- Token Bucket algorithm (time-based)
- Sliding Window Log
- Per-user rate limiting
- Distributed state using Redis
- Atomic updates via Lua scripting
- Redis server
- Refer
cmd/demo/main.gofor implementation reference
go get github.com/Santhoshkumar044/distributedRatelimiterimport (
"github.com/Santhoshkumar044/distributedRatelimiter/ratelimiter"
)func RateLimitMiddleware(limiter ratelimiter.RateLimiter) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := r.RemoteAddr // identify user (IP / API key)
res, err := limiter.Allow(user)
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// optional headers
w.Header().Set("X-RateLimit-Remaining", fmt.Sprintf("%.2f", res.TokensLeft))
w.Header().Set("Retry-After", fmt.Sprintf("%.0f", res.TryAfter))
if !res.Allowed {
w.WriteHeader(http.StatusTooManyRequests)
w.Write([]byte("Too Many Requests\n"))
return
}
next.ServeHTTP(w, r)
})
}
}rds := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
// Token Bucket
limiter := ratelimiter.NewRedisTokenBucket(rds, 5, 1)
// Sliding Window
limiter := ratelimiter.NewSlidingWindowLog(rds,5,10000) // 5 request per 10 seconds
res, err := limiter.Allow("user-123")
if err != nil {
panic(err)
}
if res.Allowed {
fmt.Println("Request allowed")
} else {
fmt.Printf("Rejected. Try after %.2f seconds\n", res.TryAfter)
}MIT