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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
   | package main
  import (     "os"     "sync"     "time"
      logger "github.com/sirupsen/logrus"     "github.com/x-cray/logrus-prefixed-formatter" )
  func init() {     logger.SetFormatter(&prefixed.TextFormatter{         TimestampFormat: "2006-01-02 15:04:05",         FullTimestamp:   true,         ForceFormatting: true,         DisableColors:   true,     })     logger.SetOutput(os.Stdout)     logger.SetLevel(logger.DebugLevel) }
  type TokenBucket struct {     mu              sync.Mutex     startTime       time.Time     availableTokens int64     capacity        int64     fillInterval    time.Duration     lastTick        int64 }
  func NewTokenBucket(qps, capacity int64) *TokenBucket {     return &TokenBucket{         startTime:       time.Now(),         availableTokens: capacity,         capacity:        capacity,         lastTick:        0,         fillInterval:    time.Duration(int64(time.Second) / qps),     } }
  func (tb *TokenBucket) adjust() {     if (tb.availableTokens) >= tb.capacity {         return     }     now := time.Now()     tick := int64(now.Sub(tb.startTime) / tb.fillInterval)     tb.availableTokens += tick - tb.lastTick     if tb.availableTokens > tb.capacity {         tb.availableTokens = tb.capacity     }     tb.lastTick = tick }
  func (tb *TokenBucket) TakeAvailable(count int64) int64 {     tb.mu.Lock()     defer tb.mu.Unlock()
      tb.adjust()
      if tb.availableTokens <= 0 {         return 0     }
      if count >= tb.availableTokens {         count = tb.availableTokens     }
      tb.availableTokens -= count     return count }
  func task(qps, num int64, timeNeed time.Duration) {     logger.Infof("task qps: %v, num: %v, timeNeed: %.3fs", qps, num, timeNeed.Seconds())     bucket := NewTokenBucket(qps, qps)     startTime := time.Now()     lastTime := startTime
      for i := int64(1); i <= num; {         if bucket.TakeAvailable(1) == 1 {             time.Sleep(timeNeed)             i++             if i%1000 == 0 {                 now := time.Now()                 logger.Infof("rate: %.3f", 1000/now.Sub(lastTime).Seconds())                 lastTime = now             }         }     }
      logger.Infof("task over, used: %.3fs", time.Now().Sub(startTime).Seconds()) }
  func main() {     task(10000, 100000, 0)     task(10000, 15000, 2*time.Millisecond)     task(500, 10000, 0) }
   |