Skip to content

Commit 077650b

Browse files
Merge pull request #4 from devfullcycle/devin/1739039928-redis-storage
feat: add Redis storage implementation
2 parents 8add240 + 8f54cca commit 077650b

File tree

10 files changed

+706
-3
lines changed

10 files changed

+706
-3
lines changed

.env.example

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Redis Configuration
2+
REDIS_HOST=localhost # Redis server hostname
3+
REDIS_PORT=6379 # Redis server port
4+
REDIS_PASSWORD= # Optional Redis password
5+
REDIS_DB=0 # Redis database number (0-15)

README.md

+48
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,54 @@ Retry-After: 60
9898
}
9999
```
100100

101+
## Storage Backends
102+
103+
### Memory Storage (Default)
104+
The default memory storage is suitable for single-instance deployments. See the Quick Start section for usage.
105+
106+
### Redis Storage
107+
For distributed environments, Redis storage backend is available. To use Redis:
108+
109+
1. Configure Redis environment variables:
110+
```bash
111+
REDIS_HOST=localhost # Redis server hostname
112+
REDIS_PORT=6379 # Redis server port
113+
REDIS_PASSWORD= # Optional Redis password
114+
REDIS_DB=0 # Redis database number
115+
```
116+
117+
2. Example code:
118+
```go
119+
package main
120+
121+
import (
122+
"github.com/devfullcycle/ratelimiter/middleware"
123+
"github.com/devfullcycle/ratelimiter/ratelimiter"
124+
"github.com/devfullcycle/ratelimiter/storage"
125+
)
126+
127+
func main() {
128+
// Initialize Redis storage
129+
cfg := storage.DefaultRedisConfig()
130+
client := storage.NewRedisClient(cfg)
131+
store := storage.NewRedisStorage(client)
132+
133+
// Create rate limiter with Redis storage
134+
limiter := ratelimiter.New(store)
135+
136+
// ... rest of your application setup
137+
}
138+
```
139+
140+
3. Running with Docker Compose:
141+
```bash
142+
# Start Redis and the application
143+
docker compose up -d
144+
145+
# Run the Redis example
146+
docker compose exec app sh -c "cd /app && go run examples/redis/redis.go"
147+
```
148+
101149
## Development and Testing
102150

103151
### Prerequisites

docker-compose.yaml

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,27 @@
11
services:
2+
redis:
3+
image: redis:7.2
4+
ports:
5+
- "6379:6379"
6+
networks:
7+
- ratelimiter
8+
29
app:
310
build: .
411
volumes:
512
- .:/app
6-
network_mode: "host"
713
working_dir: /app
814
tty: true
15+
environment:
16+
- REDIS_HOST=redis
17+
- REDIS_PORT=6379
18+
- REDIS_PASSWORD=
19+
- REDIS_DB=0
20+
depends_on:
21+
- redis
22+
networks:
23+
- ratelimiter
24+
25+
networks:
26+
ratelimiter:
27+
driver: bridge

examples/redis/redis.go

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package main
2+
3+
import (
4+
"log/slog"
5+
"net/http"
6+
"os"
7+
8+
"github.com/devfullcycle/ratelimiter/middleware"
9+
"github.com/devfullcycle/ratelimiter/ratelimiter"
10+
"github.com/devfullcycle/ratelimiter/storage"
11+
)
12+
13+
func main() {
14+
// Initialize logger
15+
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
16+
Level: slog.LevelInfo,
17+
}))
18+
19+
// Create Redis storage with default config
20+
cfg := storage.DefaultRedisConfig()
21+
client := storage.NewRedisClient(cfg)
22+
store := storage.NewRedisStorage(client)
23+
24+
// Create rate limiter with default options
25+
limiter := ratelimiter.New(store)
26+
27+
// Create middleware
28+
rateLimitMiddleware := middleware.NewRateLimitMiddleware(limiter, logger)
29+
30+
// Create a simple handler
31+
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
32+
w.Header().Set("Content-Type", "application/json")
33+
w.Write([]byte(`{"message": "Hello, World!"}`))
34+
})
35+
36+
// Wrap handler with rate limiting middleware
37+
http.Handle("/", rateLimitMiddleware.Handler(handler))
38+
39+
// Start server
40+
logger.Info("starting server on :8080",
41+
"redis_host", cfg.Host,
42+
"redis_port", cfg.Port,
43+
)
44+
if err := http.ListenAndServe(":8080", nil); err != nil {
45+
logger.Error("server error", "error", err)
46+
os.Exit(1)
47+
}
48+
}

go.mod

+53-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,55 @@
11
module github.com/devfullcycle/ratelimiter
22

3-
go 1.23.6
3+
go 1.21
4+
5+
require (
6+
github.com/redis/go-redis/v9 v9.4.0
7+
github.com/testcontainers/testcontainers-go v0.27.0
8+
)
9+
10+
require (
11+
dario.cat/mergo v1.0.0 // indirect
12+
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
13+
github.com/Microsoft/go-winio v0.6.1 // indirect
14+
github.com/Microsoft/hcsshim v0.11.4 // indirect
15+
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
16+
github.com/cespare/xxhash/v2 v2.2.0 // indirect
17+
github.com/containerd/containerd v1.7.11 // indirect
18+
github.com/containerd/log v0.1.0 // indirect
19+
github.com/cpuguy83/dockercfg v0.3.1 // indirect
20+
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
21+
github.com/docker/distribution v2.8.2+incompatible // indirect
22+
github.com/docker/docker v24.0.7+incompatible // indirect
23+
github.com/docker/go-connections v0.4.0 // indirect
24+
github.com/docker/go-units v0.5.0 // indirect
25+
github.com/go-ole/go-ole v1.2.6 // indirect
26+
github.com/gogo/protobuf v1.3.2 // indirect
27+
github.com/golang/protobuf v1.5.3 // indirect
28+
github.com/google/uuid v1.4.0 // indirect
29+
github.com/klauspost/compress v1.16.0 // indirect
30+
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
31+
github.com/magiconair/properties v1.8.7 // indirect
32+
github.com/moby/patternmatcher v0.6.0 // indirect
33+
github.com/moby/sys/sequential v0.5.0 // indirect
34+
github.com/moby/term v0.5.0 // indirect
35+
github.com/morikuni/aec v1.0.0 // indirect
36+
github.com/opencontainers/go-digest v1.0.0 // indirect
37+
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
38+
github.com/opencontainers/runc v1.1.5 // indirect
39+
github.com/pkg/errors v0.9.1 // indirect
40+
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
41+
github.com/shirou/gopsutil/v3 v3.23.11 // indirect
42+
github.com/shoenig/go-m1cpu v0.1.6 // indirect
43+
github.com/sirupsen/logrus v1.9.3 // indirect
44+
github.com/tklauser/go-sysconf v0.3.12 // indirect
45+
github.com/tklauser/numcpus v0.6.1 // indirect
46+
github.com/yusufpapurcu/wmi v1.2.3 // indirect
47+
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect
48+
golang.org/x/mod v0.11.0 // indirect
49+
golang.org/x/net v0.17.0 // indirect
50+
golang.org/x/sys v0.15.0 // indirect
51+
golang.org/x/tools v0.10.0 // indirect
52+
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
53+
google.golang.org/grpc v1.58.3 // indirect
54+
google.golang.org/protobuf v1.31.0 // indirect
55+
)

0 commit comments

Comments
 (0)