github.com/tickstep/library-go@v0.1.1/requester/rio/speeds/ratelimit.go (about)

     1  package speeds
     2  
     3  import (
     4  	"sync"
     5  	"sync/atomic"
     6  	"time"
     7  )
     8  
     9  type (
    10  	RateLimit struct {
    11  		MaxRate int64
    12  
    13  		starTimestamp    int64
    14  		count           int64
    15  		interval        time.Duration
    16  		ticker          *time.Ticker
    17  		muChan          chan struct{}
    18  		closeChan       chan struct{}
    19  		backServiceOnce sync.Once
    20  	}
    21  
    22  	// AddCountFunc func() (count int64)
    23  )
    24  
    25  func NewRateLimit(maxRate int64) *RateLimit {
    26  	return &RateLimit{
    27  		MaxRate: maxRate,
    28  	}
    29  }
    30  
    31  func (rl *RateLimit) SetInterval(i time.Duration) {
    32  	if i <= 0 {
    33  		i = 1 * time.Second
    34  	}
    35  	rl.interval = i
    36  	if rl.ticker != nil {
    37  		rl.ticker.Stop()
    38  		rl.ticker = time.NewTicker(i)
    39  	}
    40  }
    41  
    42  func (rl *RateLimit) Stop() {
    43  	if rl.ticker != nil {
    44  		rl.ticker.Stop()
    45  	}
    46  	if rl.closeChan != nil {
    47  		close(rl.closeChan)
    48  	}
    49  	return
    50  }
    51  
    52  func (rl *RateLimit) resetChan() {
    53  	if rl.muChan != nil {
    54  		close(rl.muChan)
    55  	}
    56  	rl.muChan = make(chan struct{})
    57  }
    58  
    59  func (rl *RateLimit) backService() {
    60  	if rl.interval <= 0 {
    61  		rl.interval = 200 * time.Millisecond
    62  	}
    63  	rl.ticker = time.NewTicker(rl.interval)
    64  	rl.closeChan = make(chan struct{})
    65  	rl.resetChan()
    66  	rl.starTimestamp = time.Now().UnixNano()
    67  	go func() {
    68  		for {
    69  			select {
    70  			case <-rl.ticker.C:
    71  				if rl.rate() <= rl.MaxRate {
    72  					rl.resetChan()
    73  				}
    74  			case <-rl.closeChan:
    75  				return
    76  			}
    77  		}
    78  	}()
    79  }
    80  
    81  func (rl *RateLimit) Add(count int64) {
    82  	rl.backServiceOnce.Do(rl.backService)
    83  	for {
    84  		if rl.rate() >= rl.MaxRate { // 超出最大限额
    85  			// 阻塞
    86  			<-rl.muChan
    87  			continue
    88  		}
    89  		atomic.AddInt64(&rl.count, count)
    90  		if atomic.LoadInt64(&rl.count) < 0 {
    91  			// reach the max value
    92  			atomic.StoreInt64(&rl.count, 0)
    93  			rl.starTimestamp = time.Now().Unix()
    94  		}
    95  		break
    96  	}
    97  }
    98  
    99  func (rl *RateLimit) rate() int64 {
   100  	timeElapseSecond := (time.Now().UnixNano() - rl.starTimestamp) / 1e9
   101  	if timeElapseSecond <= 0 {
   102  		timeElapseSecond = 1
   103  	}
   104  	return atomic.LoadInt64(&rl.count) / (timeElapseSecond)
   105  }