Pluggable Architecture
Swap between Token Bucket, Leaky Bucket, Fixed Window, Sliding Window Log, and Sliding Window Counter seamlessly without changing your storage driver.

distlimit?Most rate limiters force you into a single algorithm or lock global mutexes during background cleanups. distlimit solves these flaws with enterprise-grade resilience:
Pluggable Architecture
Swap between Token Bucket, Leaky Bucket, Fixed Window, Sliding Window Log, and Sliding Window Counter seamlessly without changing your storage driver.
Zero Allocation Engine
Memory evaluation runs in sub-100 nanoseconds with 0 B/op overhead to completely eliminate Go Garbage Collection pauses under heavy load.
Half-Open Circuit Breaker
Dual-tier Hybrid Driver uses an atomic single-request probing pattern to prevent Thundering Herd spikes against Redis during system recovery.
Anti-IP Spoofing Shield
Built-in CIDR-validated WithTrustedProxies inspection protects your API against forged X-Forwarded-For and X-Real-IP headers.
Redis Cluster Safe
Enforces Redis Hash Tags {} formatting (distlimit:{key}) to prevent CROSSSLOT cluster routing errors out of the box.
Built-in Observability
Native support for Prometheus metrics and OpenTelemetry tracing observers with zero performance penalty when disabled.
Install the library via Go Modules:
go get github.com/balramadan/distlimit@v1.1.1Initialize and protect your application:
package main
import ( "context" "fmt" "time"
"github.com/balramadan/distlimit" "github.com/balramadan/distlimit/algorithm/slidingcounter" "github.com/balramadan/distlimit/driver/memory")
func main() { // Initialize 64-Sharded Memory Driver (TTL 5 minutes) memDriver := memory.New(5 * time.Minute) defer func() { _ = memDriver.Close(context.Background()) }()
// Create Limiter: 10 requests per 1 minute using Sliding Window Counter limiter, _ := distlimit.New( memDriver, distlimit.WithLimit(10), distlimit.WithWindow(1*time.Minute), distlimit.WithAlgorithm(slidingcounter.New()), )
// Evaluate Rate Limit for a key res, _ := limiter.AllowKey(context.Background(), "user:123")
if res.Allowed { fmt.Printf("Allowed! Remaining quota: %d\n", res.Remaining) } else { fmt.Printf("Blocked! Try again after: %v\n", res.ResetIn) }}Integrate distlimit into your favorite Go web framework with zero boilerplate code:
package main
import ( "time"
"github.com/balramadan/distlimit" "github.com/balramadan/distlimit/algorithm/tokenbucket" "github.com/balramadan/distlimit/driver/memory" distlimitfiber "github.com/balramadan/distlimit/middleware/fiber" "github.com/gofiber/fiber/v3")
func main() { memDriver := memory.New(5 * time.Minute) limiter, _ := distlimit.New(memDriver, distlimit.WithLimit(100), distlimit.WithWindow(1*time.Minute), distlimit.WithAlgorithm(tokenbucket.New()))
app := fiber.New()
// Secure Middleware with CIDR Trusted Proxy Check app.Use(distlimitfiber.New( limiter, distlimitfiber.WithTrustedProxies([]string{"10.0.0.0/8"}), ))
app.Get("/api/ping", func(c fiber.Ctx) error { return c.SendString("pong") })
app.Listen(":3000")}package main
import ( "time"
"github.com/balramadan/distlimit" "github.com/balramadan/distlimit/algorithm/tokenbucket" "github.com/balramadan/distlimit/driver/memory" distlimitgin "github.com/balramadan/distlimit/middleware/gin" "github.com/gin-gonic/gin")
func main() { memDriver := memory.New(5 * time.Minute) limiter, _ := distlimit.New(memDriver, distlimit.WithLimit(100), distlimit.WithWindow(1*time.Minute), distlimit.WithAlgorithm(tokenbucket.New()))
r := gin.Default() r.Use(distlimitgin.New(limiter))
r.GET("/api/ping", func(c *gin.Context) { c.JSON(200, gin.H{"message": "pong"}) })
r.Run(":8080")}package main
import ( "time"
"github.com/balramadan/distlimit" "github.com/balramadan/distlimit/algorithm/tokenbucket" "github.com/balramadan/distlimit/driver/memory" distlimitecho "github.com/balramadan/distlimit/middleware/echo" "github.com/labstack/echo/v4")
func main() { memDriver := memory.New(5 * time.Minute) limiter, _ := distlimit.New(memDriver, distlimit.WithLimit(100), distlimit.WithWindow(1*time.Minute), distlimit.WithAlgorithm(tokenbucket.New()))
e := echo.New() e.Use(distlimitecho.New(limiter))
e.GET("/api/ping", func(c echo.Context) error { return c.String(200, "pong") })
e.Logger.Fatal(e.Start(":8080"))}package main
import ( "net/http" "time"
"github.com/balramadan/distlimit" "github.com/balramadan/distlimit/algorithm/tokenbucket" "github.com/balramadan/distlimit/driver/memory" distlimithttp "github.com/balramadan/distlimit/middleware/nethttp")
func main() { memDriver := memory.New(5 * time.Minute) limiter, _ := distlimit.New(memDriver, distlimit.WithLimit(100), distlimit.WithWindow(1*time.Minute), distlimit.WithAlgorithm(tokenbucket.New()))
mux := http.NewServeMux() mux.HandleFunc("/api/ping", func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("pong")) })
protectedHandler := distlimithttp.New(limiter)(mux) _ = http.ListenAndServe(":8080", protectedHandler)}package main
import ( "net" "time"
"github.com/balramadan/distlimit" "github.com/balramadan/distlimit/algorithm/tokenbucket" "github.com/balramadan/distlimit/driver/memory" distlimitgrpc "github.com/balramadan/distlimit/middleware/grpc" "google.golang.org/grpc")
func main() { memDriver := memory.New(5 * time.Minute) limiter, _ := distlimit.New(memDriver, distlimit.WithLimit(100), distlimit.WithWindow(1*time.Minute), distlimit.WithAlgorithm(tokenbucket.New()))
server := grpc.NewServer( grpc.UnaryInterceptor(distlimitgrpc.UnaryServerInterceptor(limiter)), grpc.StreamInterceptor(distlimitgrpc.StreamServerInterceptor(limiter)), )
lis, _ := net.Listen("tcp", ":50051") _ = server.Serve(lis)}