# go-ratelimit
**Repository Path**: mirrors_NVIDIA/go-ratelimit
## Basic Information
- **Project Name**: go-ratelimit
- **Description**: High-performance distributed rate limiting library for Go with Redis backend, sliding window algorithm, and dynamic configuration support
- **Primary Language**: Unknown
- **License**: Apache-2.0
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2025-10-24
- **Last Updated**: 2026-04-18
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# go-ratelimit - Redis-based Rate Limiting for Go
NVIDIA® go-ratelimit is a comprehensive, Redis-based rate limiting library for Go HTTP services. It provides high-performance, distributed rate limiting with support for both request count and bandwidth (bytes served) limits, making it ideal for protecting APIs and managing resource consumption in cloud-native applications.
For the latest stable version ensure you are on the `main` branch.
## Features
- **Dual Rate Limiting**: Limits both request count and response size (bytes served)
- **Sliding Window Algorithm**: More accurate than fixed windows, prevents traffic spikes at window boundaries
- **Redis-Based**: Works across multiple server instances in a distributed environment
- **Resilient Fallback**: Maintains rate limits in memory if Redis becomes unavailable
- **Dynamic Per-Client Configuration**: Customize rate limits for each client at runtime
- **RFC Compliant Headers**: Returns standard rate limit headers for client awareness
- **Customizable Key Generation**: Limit by client ID, IP address, route, or any combination
- **Header-Based Rate Limiting**: Rate limit by any HTTP header (API key, tenant ID, custom fields, etc.)
- **Advanced Header Options**: Sanitizers, prefixes, fallbacks, and composite keys from multiple headers
## Installation
```bash
go get github.com/NVIDIA/go-ratelimit/pkg/ratelimit
```
## Quick Start
```go
import (
"github.com/NVIDIA/go-ratelimit/pkg/ratelimit"
"github.com/redis/go-redis/v9"
)
func main() {
// Create Redis client
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
// Create rate limiter with default options
options := ratelimit.DefaultOptions(rdb)
service := ratelimit.New(options)
// Create your HTTP handler
mux := http.NewServeMux()
mux.HandleFunc("/api/resource", handleResource)
// Apply rate limiting middleware
handler := service.Middleware()(mux)
// Start server
http.ListenAndServe(":8080", handler)
}
```
## Configuration Options
```go
// Create with custom options
options := ratelimit.Options{
Redis: redisClient,
Prefix: "myapp",
RequestLimit: 100, // 100 requests per window
ByteLimit: 5 * 1024 * 1024, // 5MB per window
Window: time.Minute, // 1 minute window
KeyFunc: ratelimit.KeyByClientIDAndRoute("client123"),
Logger: myCustomLogger, // Optional: custom logger for rate limit events
}
service := ratelimit.New(options)
```
### Logger Interface
You can provide a custom logger that implements the `middleware.Logger` interface:
```go
type Logger interface {
Info(msg string, keysAndValues ...interface{})
Error(msg string, keysAndValues ...interface{})
}
```
If no logger is provided, the service uses a no-op logger by default.
## Key Functions
The library provides several built-in key functions:
```go
// Limit by client ID
ratelimit.KeyByClientID("client123")
// Limit by client ID and route
ratelimit.KeyByClientIDAndRoute("client123")
// Limit by IP address only
ratelimit.KeyByIP
// Limit by HTTP header (NEW)
ratelimit.KeyByHeader("X-API-Key")
ratelimit.KeyByHeader("X-Tenant-ID")
ratelimit.KeyByHeader("X-Custom-Field")
// Advanced header options (NEW)
ratelimit.KeyByHeaderWithOptions(utils.HeaderKeyOptions{
HeaderName: "X-Tenant-ID",
Prefix: "tenant",
Sanitizer: utils.AlphanumericSanitizer,
DefaultValue: "default-tenant",
CaseSensitive: false,
})
// Multiple headers composite key (NEW)
ratelimit.KeyByMultipleHeaders([]string{"X-Tenant-ID", "X-Region"}, ":")
// Header with fallback chain (NEW)
ratelimit.KeyByHeaderWithFallback([]string{"X-API-Key", "Authorization"})
// Custom key function
myKeyFunc := func(r *http.Request) string {
return "custom:" + r.URL.Path
}
```
### Built-in Sanitizers
The library provides several sanitizer functions for cleaning header values:
```go
// Removes non-alphanumeric characters
utils.AlphanumericSanitizer
// Validates and normalizes UUIDs
utils.UUIDSanitizer
// Truncates strings to a maximum length
utils.MaxLengthSanitizer(100)
```
📖 **[Detailed Header-Based Rate Limiting Guide](docs/HEADER_BASED_RATE_LIMITING.md)**
## Selective Rate Limiting
You can apply rate limiting only to specific endpoints:
```go
// Create a matcher for specific paths
matcher := ratelimit.MatchAny(
ratelimit.MatchPathExact("/api/expensive"),
ratelimit.MatchPathPrefix("/api/users/"),
ratelimit.MatchPathRegex("^/api/.*\\.json$"),
)
// Apply rate limiting only to paths matching the criteria
handler := service.SelectiveMiddleware(matcher)(mux)
```
Available matchers:
- `MatchPathExact(path)` - Matches exact path
- `MatchPathPrefix(prefix)` - Matches paths with a specific prefix
- `MatchPathRegex(pattern)` - Matches paths using a regular expression
- `MatchAny(matchers...)` - Matches if any matcher matches
- `MatchNone(matchers...)` - Matches if no matcher matches
## Service API
The `Service` struct provides the following methods:
```go
// Middleware application
service.Middleware() // Apply rate limiting to all requests
service.SelectiveMiddleware(matcher) // Apply rate limiting to specific paths
// Configuration management
service.SetClientConfig(clientID, reqLimit, byteLimit, windowSecs) // Set client-specific limits
service.ResetClientConfig(clientID) // Reset to defaults
service.RegisterConfigAPI(mux) // Register config endpoints
// Access internals
service.GetLimiter() // Get underlying DynamicLimiter
```
## Dynamic Client Configuration
### Programmatic Configuration
```go
// Set custom limits for a specific client
service.SetClientConfig("premium-client",
1000, // 1000 requests
10 * 1024 * 1024, // 10MB
60) // per minute
// Reset client to default configuration
service.ResetClientConfig("client-id")
```
### REST API Configuration
Register the configuration API endpoints:
```go
// Register the configuration API
service.RegisterConfigAPI(mux)
```
This adds the following endpoints:
- `GET /admin/config/{clientID}` - Get client's configuration
- `PUT /admin/config/{clientID}` - Update client's configuration
- `DELETE /admin/config/{clientID}` - Reset client to default configuration
Example API usage:
```bash
# Get a client's configuration
curl -H "X-Admin-Key: my-admin-key" \
http://localhost:8080/admin/config/client123
# Update a client's configuration
curl -X PUT -H "X-Admin-Key: my-admin-key" \
-H "Content-Type: application/json" \
-d '{"request_limit": 200, "byte_limit": 2097152, "window_secs": 60}' \
http://localhost:8080/admin/config/client123
# Reset a client to default configuration
curl -X DELETE -H "X-Admin-Key: my-admin-key" \
http://localhost:8080/admin/config/client123
```
## Resilient Rate Limiting
The library provides a resilient rate limiter that automatically falls back to in-memory rate limiting when Redis is unavailable:
```go
// Create a resilient rate limiter
resilientLimiter := limiter.NewResilientLimiter(
rdb, // Redis client
"rl", // Key prefix
10, // Default request limit
1048576, // Default byte limit (1MB)
time.Minute, // Window duration
keyBuilderFunction, // Function to extract key from request
)
// Use it with middleware
handler := middleware.DynamicRateLimitMiddleware(resilientLimiter)(nextHandler)
```
📖 **[Detailed Resilient Rate Limiting Guide](docs/RESILIENT_RATE_LIMITING.md)**
## Architecture
The library consists of several modular components:
```
pkg/
├── ratelimit/
│ └── ratelimit.go # Main package API and service
├── limiter/
│ ├── redis.go # Simple request count limiter
│ ├── dual_limiter.go # Combined request count and byte limiter
│ ├── dynamic_limiter.go # Dynamic per-client configurable limiter
│ ├── memory.go # In-memory rate limiter implementation
│ └── resilient_limiter.go # Resilient limiter with Redis fallback
├── middleware/
│ ├── ratelimit.go # HTTP middleware for request count limiting
│ ├── dual_ratelimit.go # HTTP middleware for dual limiting
│ ├── dynamic_ratelimit.go # Dynamic middleware with per-client configs
│ ├── selective_ratelimit.go # Selective path-based rate limiting
│ ├── bytecounter.go # Response writer wrapper to count bytes
│ └── logger.go # Logger interface and no-op implementation
├── config/
│ ├── config.go # Configuration structures
│ └── manager.go # Per-client configuration manager
├── api/
│ ├── config_handler.go # Legacy config API endpoints
│ └── admin_handler.go # Advanced admin API with batch operations
├── utils/
│ ├── request.go # Helper functions for key generation
│ └── header_keys.go # Header-based key extraction and sanitizers
└── logger/
└── logger.go # Structured logging with Zap/OpenTelemetry
```
### Flow Diagram
```mermaid
flowchart TD
subgraph Client
A[HTTP Request with Headers]
end
subgraph "Rate Limiting System"
subgraph "Service Layer - pkg/ratelimit"
S1[Service.Middleware
or SelectiveMiddleware]
end
subgraph "Middleware Layer - pkg/middleware"
M1[DynamicRateLimitMiddleware]
M2[Extract Key from Request
using KeyFunc]
M3[Path Matching
Selective]
M4{Request Count
Limit Check}
M5[Set Rate Limit Headers
X-RateLimit-*]
M6{Rate Limit
Exceeded?}
M7[Return 429
Too Many Requests]
M8[Wrap Response
ByteCounterWriter]
M9[Call Next Handler]
M10[Log Events]
end
subgraph "Limiter Layer - pkg/limiter"
L1[DynamicLimiter]
L2[Get Client Config
from ConfigManager]
L3[AllowRequest
Check Request Count]
L4[RecordBytes
Track Response Size]
end
subgraph "Utils Layer - pkg/utils"
U1[KeyByIP
KeyByHeader
KeyByMultipleHeaders]
U2[Sanitizers
Alphanumeric, UUID, etc.]
end
subgraph "Config Layer - pkg/config"
C1[ClientConfig
Manager]
end
subgraph "API Layer - pkg/api"
API1[ConfigHandler
Legacy API]
API2[AdminHandler
Advanced API]
end
subgraph "Redis Database"
R1[(Request Count
Sorted Sets)]
R2[(Byte Count
Sorted Sets)]
R3[(Client Configs
Hash)]
end
subgraph "Application Handler"
H1[Your HTTP Handler]
H2[Generate Response]
end
end
%% Main request flow
A --> S1
S1 --> M1
M1 --> M3
M3 --> M2
M2 --> U1
U1 --> U2
U2 --> M4
M4 --> L1
L1 --> L2
L2 --> C1
C1 --> R3
C1 --> L3
L3 --> R1
L3 --> M5
M5 --> M6
M6 -->|Yes| M7
M7 --> M10
M10 --> A
M6 -->|No| M8
M8 --> M9
M9 --> H1
H1 --> H2
H2 --> M8
%% After response
M8 --> L4
L4 --> R2
L4 --> M10
M10 --> A
%% Admin API flows
API1 -.->|Manage Configs| C1
API2 -.->|Manage Configs
Batch Operations| C1
style S1 fill:#e1f5ff
style L1 fill:#fff4e1
style C1 fill:#f0e1ff
style R1 fill:#ffe1e1
style R2 fill:#ffe1e1
style R3 fill:#ffe1e1
style M7 fill:#ffcccc
```
**Flow Explanation:**
1. **Request arrives** with headers (API key, tenant ID, etc.)
2. **Service layer** routes to appropriate middleware (all requests or selective)
3. **Middleware** extracts rate limit key using configurable KeyFunc (IP, headers, composite)
4. **Utils layer** applies sanitizers and generates clean rate limit keys
5. **Limiter** checks client-specific config from Redis, then validates request count
6. **Rate limit headers** are set on response (X-RateLimit-Limit, X-RateLimit-Remaining, etc.)
7. **Decision point**:
- If limit exceeded → Return 429 with retry information
- If allowed → Wrap response writer to count bytes
8. **Application handler** processes request and generates response
9. **After response**, byte count is recorded to Redis for bandwidth limiting
10. **Logging** captures all rate limit events
11. **Admin APIs** (shown with dashed lines) allow runtime configuration updates
The system uses Redis sorted sets for sliding window rate limiting and hash storage for per-client configurations.
## Examples
See the [examples](./examples) directory for complete working examples:
- [Basic Example](./cmd/examples/basic/main.go) - Simple rate limiting setup
- [Dynamic Example](./cmd/examples/dynamic/main.go) - Dynamic per-client configuration
- [Resilient Example](./examples/resilient_limiter/main.go) - Resilient rate limiting with Redis fallback
## System Requirements
### Go requirements
* Go >= 1.21
### Redis requirements
* Redis >= 5.0
### OS requirements
* Linux, macOS, and Windows are supported
* x86_64 (64-bit)
* aarch64 (64-bit)
## Development
The project includes a Makefile to simplify common development tasks:
```bash
# Build the main binary
make build
# Run tests
make test
# Run tests with coverage
make test-cover
# Format code
make fmt
# Run linter
make lint
# Show all available commands
make help
```
### Running Tests
The project includes comprehensive tests for all components:
```bash
# Run all tests
make test
# Run tests with coverage report
make test-cover
```
Tests cover:
- **Redis-based limiters**: Request count, byte count, and dual limiting
- **Dynamic configuration**: Per-client configuration management
- **Key generation**: IP-based, client ID-based, header-based, and composite keys
- **Header sanitizers**: Alphanumeric, UUID, and max-length sanitizers
- **Path matching**: Selective rate limiting with various matchers
- **Byte counting**: Response size tracking
- **Middleware**: Integration tests for all middleware types
- **Admin API**: Configuration API endpoints
- **Concurrent operations**: Thread-safe operations under load
The test suite includes both unit tests and integration tests with Redis.
## Resources
- [Documentation](docs/) - Additional documentation and guides
- [Examples](examples/) - Working examples for various use cases
- [Setup Guide](SETUP_GUIDE.md) - Detailed setup instructions
- [Header-Based Rate Limiting Guide](docs/HEADER_BASED_RATE_LIMITING.md) - Advanced header-based limiting
- [Resilient Rate Limiting Guide](docs/RESILIENT_RATE_LIMITING.md) - Building resilient systems
## Contributing
Review the [CONTRIBUTING.md](CONTRIBUTING.md) file for information on how to contribute code and issues to the project.
## License
This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
Copyright 2025 NVIDIA Corporation