|
|
@ -19,7 +19,6 @@ package cmd |
|
|
|
import ( |
|
|
|
import ( |
|
|
|
"errors" |
|
|
|
"errors" |
|
|
|
"net/http" |
|
|
|
"net/http" |
|
|
|
"sync" |
|
|
|
|
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
var errTooManyRequests = errors.New("Too many clients in the waiting list") |
|
|
|
var errTooManyRequests = errors.New("Too many clients in the waiting list") |
|
|
@ -28,7 +27,6 @@ var errTooManyRequests = errors.New("Too many clients in the waiting list") |
|
|
|
// limit the number of concurrent http requests.
|
|
|
|
// limit the number of concurrent http requests.
|
|
|
|
type rateLimit struct { |
|
|
|
type rateLimit struct { |
|
|
|
handler http.Handler |
|
|
|
handler http.Handler |
|
|
|
lock sync.Mutex |
|
|
|
|
|
|
|
workQueue chan struct{} |
|
|
|
workQueue chan struct{} |
|
|
|
waitQueue chan struct{} |
|
|
|
waitQueue chan struct{} |
|
|
|
} |
|
|
|
} |
|
|
@ -37,26 +35,26 @@ type rateLimit struct { |
|
|
|
// channel this is in-turn used to rate limit incoming connections in
|
|
|
|
// channel this is in-turn used to rate limit incoming connections in
|
|
|
|
// ServeHTTP() http.Handler method.
|
|
|
|
// ServeHTTP() http.Handler method.
|
|
|
|
func (c *rateLimit) acquire() error { |
|
|
|
func (c *rateLimit) acquire() error { |
|
|
|
//lock access to enter waitQueue
|
|
|
|
//attempt to enter the waitQueue. If no slot is immediately
|
|
|
|
c.lock.Lock() |
|
|
|
//available return error.
|
|
|
|
// Kick out clients when it is really crowded
|
|
|
|
select { |
|
|
|
if len(c.waitQueue) == cap(c.waitQueue) { |
|
|
|
case c.waitQueue <- struct{}{}: |
|
|
|
defer c.lock.Unlock() //unlock after return
|
|
|
|
//entered wait queue
|
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
//no slot available for waiting
|
|
|
|
return errTooManyRequests |
|
|
|
return errTooManyRequests |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Add new element in waitQueue to keep track of clients
|
|
|
|
//block attempting to enter the workQueue. If the workQueue is
|
|
|
|
// wanting to process their requests
|
|
|
|
//full, there can be at most cap(waitQueue) == 4*globalMaxConn
|
|
|
|
c.waitQueue <- struct{}{} |
|
|
|
//goroutines waiting here because of the select above.
|
|
|
|
|
|
|
|
select { |
|
|
|
// Unlock now. If we unlock before sending to the waitQueue
|
|
|
|
case c.workQueue <- struct{}{}: |
|
|
|
// channel, we can have multiple go-routines blocked on
|
|
|
|
//entered workQueue - so remove one waiter. This step
|
|
|
|
// sending to the waitQueue (and exceeding the max. number of
|
|
|
|
//does not block as the waitQueue cannot be empty.
|
|
|
|
// waiting connections.)
|
|
|
|
<-c.waitQueue |
|
|
|
c.lock.Unlock() |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Block to put a waiting go-routine into processing mode.
|
|
|
|
|
|
|
|
c.workQueue <- <-c.waitQueue |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|