1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
|
package gou
import (
"time"
)
type Throttler struct {
// Limit to this events/per
maxPer float64
per float64
count int32
// Last Event
last time.Time
// How many events are allowed left to happen?
// Starts at limit, decrements down
allowance float64
}
// new Throttler that will tell you to limit or not based
// on given @max events @per duration
func NewThrottler(max int, per time.Duration) *Throttler {
return &Throttler{
maxPer: float64(max),
allowance: float64(max),
count: int32(0),
last: time.Now(),
per: per.Seconds(),
}
}
// Should we limit this because we are above rate?
// Returns a bool of whether to throttle the message, and a count
// of previous log messages throttled since last log message.
func (r *Throttler) ThrottleAdd(ct int32) (bool, int32) {
if r.maxPer == 0 {
return false, 0
}
// http://stackoverflow.com/questions/667508/whats-a-good-rate-limiting-algorithm
now := time.Now()
elapsed := float64(now.Sub(r.last).Nanoseconds()) / 1e9 // seconds
r.last = now
r.allowance += elapsed * (r.maxPer / r.per)
//Infof("maxRate: %v cur: %v elapsed:%-6.6f incr: %v", r.maxPer, int(r.allowance), elapsed, elapsed*float64(r.maxPer))
if r.allowance > r.maxPer {
r.allowance = r.maxPer
}
if r.allowance < 1.0 {
r.count += ct // increment throttled log count
return true, r.count // do throttle/limit
}
tmpCount := r.count
r.count = 0 // reset count
r.allowance -= 1.0
return false, tmpCount // dont throttle, return previous throttle count
}
// Should we limit this because we are above rate?
// Returns a bool of whether to throttle the message, and a count
// of previous log messages throttled since last log message.
func (r *Throttler) Throttle() (bool, int32) {
return r.ThrottleAdd(1)
}
func (r *Throttler) ThrottleCount() int32 {
return r.count
}
|