If ❤️ my article is helpful, welcome to like, follow. This is the biggest encouragement for me to continue my technical creation.

This article is a series of articles to deal with sudden traffic, and the following are related to gay friends

  • In the face of burst traffic: system and software layer traffic limiting methods
  • Program level flow limiting – necessity
  • Program Level Current Limiting – Graphic realization of four current limiting methods (Golang version)

Counter current limiting

package main

import (
	"fmt"
	"sync"
	"time"
)

type RequestLimiter struct {
	Interval time.Duration // recount the time
	MaxCount int           // Maximum count
	Lock     sync.Mutex
	ReqCount int // Current number of requests
}

func (reqLimiter *RequestLimiter) IsAvailable(a) bool {
	reqLimiter.Lock.Lock()
	defer reqLimiter.Lock.Unlock()

	return reqLimiter.ReqCount < reqLimiter.MaxCount
}

/ / non-blocking
func (reqLimiter *RequestLimiter) AddRequestCount(a) bool {
	reqLimiter.Lock.Lock()
	defer reqLimiter.Lock.Unlock()
	if reqLimiter.ReqCount < reqLimiter.MaxCount {
		reqLimiter.ReqCount += 1
		return true
	}
	return false
}

func NewRequestLimitService(interval time.Duration, maxCount int) *RequestLimiter {
	reqLimit := &RequestLimiter{
		Interval: interval,
		MaxCount: maxCount,
	}
	go func(a) {
		ticker := time.NewTicker(interval)
		for true {
			<-ticker.C
			reqLimit.Lock.Lock()
			fmt.Println("reset Count ...")
			reqLimit.ReqCount = 0
			reqLimit.Lock.Unlock()
		}
	}()

	return reqLimit
}

/ * * principle: Ticker := time.newticker (2* time.second) for {currentTime := < -ticker.c FMT.Println(" currentTime :", currentTime)} output: currentTime: 2021-05-19 11:02:12.3603475 +0800 CST m=+2.002147201 currentTime: 2021-05-19 11:02:14.3611806 +0800 CST m=+4.002980301 Current time: 2021-05-19 11:02:16.360625 +0800 CST m=+6.002424701 */
func main(a) {
	service := NewRequestLimitService(time.Second, 2)
	for true {
		hasToken := service.AddRequestCount()
		if hasToken {
			fmt.Println(time.Now())
		}
	}
}
Copy the code

The sliding window

package main

import (
	"fmt"
	"sync"
	"time"
)

type WindowLimiter struct {
	Interval    time.Duration // Total time
	WinCount    []int         // The number of accesses per window
	TicketSize  int           // Maximum window capacity
	TicketCount int           // Number of Windows
	Lock        sync.Mutex
	CurIndex    int // Which window is currently in use
}

func (reqLimiter *WindowLimiter) IsAvailable(a) bool {
	reqLimiter.Lock.Lock()
	defer reqLimiter.Lock.Unlock()
	return reqLimiter.WinCount[reqLimiter.CurIndex] < reqLimiter.TicketSize
}

func (reqLimiter *WindowLimiter) AddRequestCount(a) bool {
	reqLimiter.Lock.Lock()
	defer reqLimiter.Lock.Unlock()
	if reqLimiter.WinCount[reqLimiter.CurIndex] < reqLimiter.TicketSize {
		reqLimiter.WinCount[reqLimiter.CurIndex]++
		return true
	}
	return false
}

func NewRequestLimitService(interval time.Duration, ticketCount int, ticketSize int) *WindowLimiter {
	reqLimit := &WindowLimiter{
		Interval:    interval,
		WinCount:    make([]int, ticketCount, ticketCount),
		TicketSize:  ticketSize,
		TicketCount: ticketCount,
		CurIndex:    0,}go func(a) {
		ticker := time.NewTicker(time.Duration(interval.Nanoseconds() / int64(ticketCount)))
		for true {
			<-ticker.C
			reqLimit.Lock.Lock()
			reqLimit.CurIndex = (reqLimit.CurIndex + 1) % reqLimit.TicketCount
			reqLimit.WinCount[reqLimit.CurIndex] = 0
			fmt.Println("reset Count ...")
			reqLimit.Lock.Unlock()
		}
	}()

	return reqLimit
}

func main(a) {
	service := NewRequestLimitService(time.Second, 2.1)
	for true {
		hasToken := service.AddRequestCount()
		if hasToken {
			fmt.Println(time.Now())
		}
	}
}
Copy the code

Bucket algorithm

package main

import (
	"fmt"
	"math"
	"sync"
	"time"
)

type BucketLimiter struct {
	Timestamp time.Time // The timestamp of the current water injection
	Capacity  float64   // The capacity of a bucket
	Rate      float64   / / speed
	Water     float64   // The current amount of water
	Lock      sync.Mutex
}

func AddWater(bucket *BucketLimiter) bool {
	now := time.Now()
	leftWater := math.Max(0, bucket.Water-now.Sub(bucket.Timestamp).Seconds()*bucket.Rate)
	bucket.Lock.Lock()
	defer bucket.Lock.Unlock()
	if leftWater+1 < bucket.Capacity {
		// Try adding water, the bucket is not full
		bucket.Timestamp = now
		bucket.Water = leftWater + 1
		return true
	} else {
		// Access denied when water is full
		return false}}func main(a) {
	service := &BucketLimiter{
		Timestamp: time.Now(),
		Capacity:  2,
		Rate:      1,
		Water:     0,}for true {
		hasToken := AddWater(service)
		if hasToken {
			fmt.Println(time.Now())
		}

	}
}
Copy the code

Token bucket algorithm

package main

import (
	"math"
	"sync"
	"time"
)

// Define the token bucket structure
type tokenBucket struct {
	timestamp time.Time // The current timestamp
	capacity  float64   // Bucket capacity (maximum number of tokens)
	rate      float64   // Token loading speed
	tokens    float64   // Total number of current tokens
	lock      sync.Mutex
}

// Determine whether to obtain the token (if so, process the request)
func getToken(bucket tokenBucket) bool {
	now := time.Now()
	bucket.lock.Lock()
	defer bucket.lock.Unlock()
	// Add the token first
	leftTokens := math.Max(bucket.capacity, bucket.tokens+now.Sub(bucket.timestamp).Seconds()*bucket.rate)
	if leftTokens < 1 {
		// If there is no token in the bucket, reject it
		return false
	} else {
		// There is a token in the bucket
		bucket.tokens -= 1
		bucket.timestamp = now
		return true}}Copy the code