github.com/songzhibin97/gkit@v1.2.13/overload/bbr/bbr.go (about) 1 package bbr 2 3 import ( 4 "context" 5 "math" 6 "sync/atomic" 7 "time" 8 9 "github.com/songzhibin97/gkit/container/group" 10 "github.com/songzhibin97/gkit/internal/stat" 11 cupstat "github.com/songzhibin97/gkit/internal/sys/cpu" 12 "github.com/songzhibin97/gkit/log" 13 "github.com/songzhibin97/gkit/options" 14 "github.com/songzhibin97/gkit/overload" 15 ) 16 17 // package bbr: bbr 限流 18 19 var ( 20 cpu int64 21 22 // decay: 衰变周期 23 decay = 0.95 24 25 // initTime: 起始时间 26 initTime = time.Now() 27 ) 28 29 // cpuGetter: 30 type cpuGetter func() int64 31 32 // config: bbr 配置 33 type config struct { 34 debug bool 35 enabled bool 36 winBucket int 37 cPUThreshold int64 38 window time.Duration 39 rule string 40 } 41 42 // Stat bbr 指标信息 43 type Stat struct { 44 Cpu int64 45 InFlight int64 46 MaxInFlight int64 47 MinRt int64 48 MaxPass int64 49 } 50 51 // BBR 实现类似bbr的限制器. 52 type BBR struct { 53 cpu cpuGetter 54 passStat stat.RollingCounter 55 rtStat stat.RollingCounter 56 inFlight int64 57 winBucketPerSec int64 58 conf *config 59 prevDrop atomic.Value 60 prevDropHit int32 61 rawMaxPASS int64 62 rawMinRt int64 63 } 64 65 // Group 表示BBRLimiter的类,并形成其中的命名空间 66 type Group struct { 67 group group.LazyLoadGroup 68 } 69 70 // init 启动后台收集cpu信息 71 func init() { 72 go cpuProc() 73 } 74 75 // cpuProc 定时任务,收集当前服务器CPU信息 76 // cpu = cpuᵗ⁻¹ * decay + cpuᵗ * (1 - decay) 77 func cpuProc() { 78 ticker := time.NewTicker(time.Millisecond * 250) 79 defer func() { 80 ticker.Stop() 81 if err := recover(); err != nil { 82 log.NewHelper(log.DefaultLogger).Errorf("rate.limit.cpuproc() err(%+v)", err) 83 go cpuProc() 84 } 85 }() 86 87 for range ticker.C { 88 stats := &cupstat.Stat{} 89 cupstat.ReadStat(stats) 90 stats.Usage = min(stats.Usage, 1000) 91 prevCpu := atomic.LoadInt64(&cpu) 92 curCpu := int64(float64(prevCpu)*decay + float64(stats.Usage)*(1.0-decay)) 93 atomic.StoreInt64(&cpu, curCpu) 94 } 95 } 96 97 func min(l, r uint64) uint64 { 98 if l < r { 99 return l 100 } 101 return r 102 } 103 104 // maxPASS 最大通过值 105 func (l *BBR) maxPASS() int64 { 106 rawMaxPass := atomic.LoadInt64(&l.rawMaxPASS) 107 if rawMaxPass > 0 && l.passStat.Timespan() < 1 { 108 return rawMaxPass 109 } 110 rawMaxPass = int64(l.passStat.Reduce(func(iterator stat.Iterator) float64 { 111 result := 1.0 112 for i := 1; iterator.Next() && i < l.conf.winBucket; i++ { 113 bucket := iterator.Bucket() 114 count := 0.0 115 for _, p := range bucket.Points { 116 count += p 117 } 118 result = math.Max(result, count) 119 } 120 return result 121 })) 122 if rawMaxPass == 0 { 123 rawMaxPass = 1 124 } 125 atomic.StoreInt64(&l.rawMaxPASS, rawMaxPass) 126 return rawMaxPass 127 } 128 129 // minRT 最小RT 130 func (l *BBR) minRT() int64 { 131 rawMinRT := atomic.LoadInt64(&l.rawMinRt) 132 if rawMinRT > 0 && l.rtStat.Timespan() < 1 { 133 return rawMinRT 134 } 135 rawMinRT = int64(math.Ceil(l.rtStat.Reduce(func(iterator stat.Iterator) float64 { 136 result := math.MaxFloat64 137 for i := 1; iterator.Next() && i < l.conf.winBucket; i++ { 138 bucket := iterator.Bucket() 139 if len(bucket.Points) == 0 { 140 continue 141 } 142 total := 0.0 143 for _, p := range bucket.Points { 144 total += p 145 } 146 avg := total / float64(bucket.Count) 147 result = math.Min(result, avg) 148 } 149 return result 150 }))) 151 if rawMinRT <= 0 { 152 rawMinRT = 1 153 } 154 atomic.StoreInt64(&l.rawMinRt, rawMinRT) 155 return rawMinRT 156 } 157 158 // maxFlight 159 func (l *BBR) maxFlight() int64 { 160 return int64(math.Floor(float64(l.maxPASS()*l.minRT()*l.winBucketPerSec)/1000.0 + 0.5)) 161 } 162 163 // shouldDrop 判断是否应该降低 164 func (l *BBR) shouldDrop() bool { 165 if l.cpu() < l.conf.cPUThreshold { 166 prevDrop, _ := l.prevDrop.Load().(time.Duration) 167 if prevDrop == 0 { 168 return false 169 } 170 if time.Since(initTime)-prevDrop <= time.Second { 171 if atomic.LoadInt32(&l.prevDropHit) == 0 { 172 atomic.StoreInt32(&l.prevDropHit, 1) 173 } 174 inFlight := atomic.LoadInt64(&l.inFlight) 175 return inFlight > 1 && inFlight > l.maxFlight() 176 } 177 l.prevDrop.Store(time.Duration(0)) 178 return false 179 } 180 inFlight := atomic.LoadInt64(&l.inFlight) 181 drop := inFlight > 1 && inFlight > l.maxFlight() 182 if drop { 183 prevDrop, _ := l.prevDrop.Load().(time.Duration) 184 if prevDrop != 0 { 185 return drop 186 } 187 l.prevDrop.Store(time.Since(initTime)) 188 } 189 return drop 190 } 191 192 // Stat 状态信息 193 func (l *BBR) Stat() Stat { 194 return Stat{ 195 Cpu: l.cpu(), 196 InFlight: atomic.LoadInt64(&l.inFlight), 197 MinRt: l.minRT(), 198 MaxPass: l.maxPASS(), 199 MaxInFlight: l.maxFlight(), 200 } 201 } 202 203 // Allow 检查所有入站流量 204 // 一旦检测到过载,它将引发 LimitExceed 错误。 205 func (l *BBR) Allow(ctx context.Context, opts ...overload.AllowOption) (func(info overload.DoneInfo), error) { 206 allowOpts := overload.DefaultAllowOpts() 207 for _, opt := range opts { 208 opt.Apply(&allowOpts) 209 } 210 if l.shouldDrop() { 211 return nil, LimitExceed 212 } 213 atomic.AddInt64(&l.inFlight, 1) 214 sTime := time.Since(initTime) 215 return func(do overload.DoneInfo) { 216 if rt := int64((time.Since(initTime) - sTime) / time.Millisecond); rt > 0 { 217 l.rtStat.Add(rt) 218 } 219 atomic.AddInt64(&l.inFlight, -1) 220 switch do.Op { 221 case overload.Success: 222 l.passStat.Add(1) 223 return 224 default: 225 return 226 } 227 }, nil 228 } 229 230 // defaultConf 默认配置 231 func defaultConf() *config { 232 return &config{ 233 // window: 窗口周期 234 window: time.Second * 10, 235 winBucket: 100, 236 // cPUThreshold: 阈值 237 cPUThreshold: 800, 238 } 239 } 240 241 // Option 242 243 func SetDebug(debug bool) options.Option { 244 return func(c interface{}) { 245 c.(*config).debug = debug 246 } 247 } 248 249 func SetEnabled(enabled bool) options.Option { 250 return func(c interface{}) { 251 c.(*config).enabled = enabled 252 } 253 } 254 255 func SetWinBucket(winBucket int) options.Option { 256 return func(c interface{}) { 257 c.(*config).winBucket = winBucket 258 } 259 } 260 261 func SetCPUThreshold(cPUThreshold int64) options.Option { 262 return func(c interface{}) { 263 c.(*config).cPUThreshold = cPUThreshold 264 } 265 } 266 267 func SetWindow(window time.Duration) options.Option { 268 return func(c interface{}) { 269 c.(*config).window = window 270 } 271 } 272 273 func SetRule(rule string) options.Option { 274 return func(c interface{}) { 275 c.(*config).rule = rule 276 } 277 } 278 279 // newLimiter 实例化限制器 280 func newLimiter(options ...options.Option) overload.Limiter { 281 // 判断传入配置是否为空,否则使用默认配置 282 conf := defaultConf() 283 for _, opt := range options { 284 opt(conf) 285 } 286 287 size := conf.winBucket 288 bucketDuration := conf.window / time.Duration(conf.winBucket) 289 passStat := stat.NewRollingCounter(size, bucketDuration) 290 rtStat := stat.NewRollingCounter(size, bucketDuration) 291 cpu := func() int64 { 292 return atomic.LoadInt64(&cpu) 293 } 294 limiter := &BBR{ 295 cpu: cpu, 296 conf: conf, 297 passStat: passStat, 298 rtStat: rtStat, 299 winBucketPerSec: int64(time.Second) / (int64(conf.window) / int64(conf.winBucket)), 300 } 301 return limiter 302 } 303 304 // NewGroup 实例化限制器容器 305 func NewGroup(options ...options.Option) *Group { 306 // 判断传入配置是否为空,否则使用默认配置 307 _group := group.NewGroup(func() interface{} { 308 return newLimiter(options...) 309 }) 310 return &Group{ 311 group: _group, 312 } 313 } 314 315 // Get 通过指定的键获取一个限制器,如果不存在限制器,则重新创建一个限制器。 316 func (g *Group) Get(key string) overload.Limiter { 317 limiter := g.group.Get(key) 318 return limiter.(overload.Limiter) 319 }