Configuring the Rate Limiters.
This commit is contained in:
@@ -28,17 +28,23 @@ For now, the only configuration settings will be the network port that we want t
|
||||
and the name of the current operating environment for the application (development, staging, production, etc.)
|
||||
We'll read in the configuration settings from command-line flags when the application starts.
|
||||
*/
|
||||
// Add a db struct field to hold the configuration settings for our database connection pool. For now, this only holds the DSN, which we will read in from a command-line flag.
|
||||
// Add maxOpenConns, maxIdleConns and maxIdleTime fields to hold the configuration settings for the connection pool
|
||||
type config struct {
|
||||
port int
|
||||
env string
|
||||
// Add a db struct field to hold the configuration settings for our database connection pool. For now, this only holds the DSN, which we will read in from a command-line flag.
|
||||
db struct {
|
||||
dsn string
|
||||
// Add maxOpenConns, maxIdleConns and maxIdleTime fields to hold the configuration settings for the connection pool
|
||||
maxOpenConns int
|
||||
maxIdleConns int
|
||||
maxIdleTime time.Duration
|
||||
}
|
||||
// Add a new limiter struct containing fields for the requests-per-second and burst values, and a boolean field which we can use to enable/disable rate limiting altogether
|
||||
limiter struct {
|
||||
rps float64
|
||||
burst int
|
||||
enabled bool
|
||||
}
|
||||
}
|
||||
|
||||
// application struct hold the dependencies for our HTTP handlers, helpers, and middleware.
|
||||
@@ -68,6 +74,11 @@ func main() {
|
||||
flag.IntVar(&cfg.db.maxIdleConns, "db-max-idle-conns", 25, "PostgreSQL max idle connections")
|
||||
flag.DurationVar(&cfg.db.maxIdleTime, "db-max-idle-time", 15*time.Minute, "PostgreSQL max connection idle time")
|
||||
|
||||
// Create command line flags to read the setting values into the config struct. Notice taht we use true as the default for the 'enabled' setting
|
||||
flag.Float64Var(&cfg.limiter.rps, "limiter-rps", 2, "Rate limiter maximum requests per second")
|
||||
flag.IntVar(&cfg.limiter.burst, "limiter-burst", 4, "Rate limiter maximum burst")
|
||||
flag.BoolVar(&cfg.limiter.enabled, "limiter-enabled", true, "Enable rate limiter")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
// Initialize a new structured logger which writes log entries to standard out stream.
|
||||
|
||||
@@ -62,6 +62,8 @@ func (app *application) rateLimit(next http.Handler) http.Handler {
|
||||
|
||||
// The function we are returning is a closure, which 'closes over' the limiter variable
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Only carry out the check if rate limiting is enabled
|
||||
if app.config.limiter.enabled {
|
||||
// Extract the client's IP address from request.
|
||||
ip, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
@@ -72,9 +74,12 @@ func (app *application) rateLimit(next http.Handler) http.Handler {
|
||||
// Lock the mutex to prevent this code from being executed concurrently
|
||||
mu.Lock()
|
||||
|
||||
// Check to see if the IP address already exists in the map. If it doesn't, then initialize a new rate limiter and add the IP address and limiter to he map.
|
||||
// Check to see if the IP address already exists in the map. If it doesn't, then initialize a new rate limiter and add the IP address and limiter to the map.
|
||||
if _, found := clients[ip]; !found {
|
||||
clients[ip] = &client{limiter: rate.NewLimiter(2, 4)}
|
||||
// Use the requests-per-second and burst values from the config struct
|
||||
rps := rate.Limit(app.config.limiter.rps)
|
||||
burst := app.config.limiter.burst
|
||||
clients[ip] = &client{limiter: rate.NewLimiter(rps, burst)}
|
||||
}
|
||||
|
||||
// Update the last see time for the client.
|
||||
@@ -89,6 +94,7 @@ func (app *application) rateLimit(next http.Handler) http.Handler {
|
||||
|
||||
// Very importantly, unlock the mutex before calling the next handler in the chain. Notice that we DON'T use defer to unlock the mutex, as that would mean that the mutex isn't unlocked until all the handlers downstream of this middleware have also returned
|
||||
mu.Unlock()
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user