Introduction
If the traffic is generally too large and the downstream system cannot react, it will need to limit the current at this time. In fact, it is the same as going to the subway, which is to slow down the upstream access to the downstream.
Limit the frequency or frequency of access to services, prevent service overload, blasting, etc.
In Golang's official expansion package time(/x/time/rate), a current limiter implementation based on token buckets and other current limiter is provided.
Overview of the principles
- Token: You can access it every time you get the token
- Bucket , the maximum capacity of the bucket is fixed, and the token is added to the bucket at a fixed frequency until it is full
- Each request consumes one token.
- When the current limiter is initialized, the token bucket is usually full.
Specific use
package limiter import ( "fmt" "testing" "time" "/x/time/rate" ) func TestLimter(t *) { limiter := ((*31), 2) //() for i := 0; i < 10; i++ { var ok bool if () { ok = true } ( * 20) (ok, ()) } }
Execution results:
=== RUN TestLimter
true 2
true 2
true 2
false 2
true 2
true 2
false 2
true 2
true 2
false 2
--- PASS: TestLimter (0.21s)
Through the execution results, we can see that the token bucket is full at the beginning. Since the interval of tokens is 11ms (31-20) more than the interval of requests, every two requests will fail once.
Specific implementation principle
First look at the creation method of the lower current limiter: NewLimiter
func NewLimiter(r Limit, b int) *Limiter { return &Limiter{ limit: r, burst: b, } }
View the current limiter data structure Limiter
// The methods AllowN, ReserveN, and WaitN consume n tokens. type Limiter struct { mu limit Limit burst int tokens float64 // last is the last time the limiter's tokens field was updated last // lastEvent is the latest time of a rate-limited event (past or future) lastEvent }
- burst indicates the size of the bucket
- limit indicates the frequency of the bucket
- tokens indicates the number of remaining tokens
- Last Recently I took the token
- lastEvent Time of the latest current limit event
When the token bucket is issued, it will be retained in the Reservation object. The definition is as follows. The Reservation object describes the number of tokens that can be obtained after the timeToAct time is reached.
type Reservation struct { ok bool // Whether the conditions are met are assigned tokens lim *Limiter // Current limiter for sending tokens tokens int // The number of tokens timeToAct // Meet the time of issuance of tokens limit Limit // Token issuance speed}
How to limit current
The official current limiter provides blocking and waiting, and there are also direct judgment methods, as well as maintenance reservations.
How to implement stream limiting code, in reserveN.
When used, the Allow() method is called every time
// Allow is shorthand for AllowN((), 1). func (lim *Limiter) Allow() bool { return ((), 1) } // AllowN reports whether n events may happen at time now. // Use this method if you intend to drop / skip events that exceed the rate limit. // Otherwise use Reserve or Wait. func (lim *Limiter) AllowN(now , n int) bool { return (now, n, 0).ok }
Continue to view the reservoirN algorithm
Method description:
- Three parameters: now, n, maxFutureReserve
- At now time, n tokens are needed, and the maximum waiting time is maxFutureReserve
- The result will return an object with a reserved token.
// maxFutureReserve specifies the maximum reservation wait duration allowed. // reserveN returns Reservation, not *Reservation, to avoid allocation in AllowN and WaitN. func (lim *Limiter) reserveN(now , n int, maxFutureReserve ) Reservation { () // First, determine whether the frequency of the input is infinite. If it is infinite, it means that the current limit is not available. if == Inf { () return Reservation{ ok: true, lim: lim, tokens: n, timeToAct: now, } } // When you get the deadline, the number of tokens that can be obtained, the last time you took the token was last now, last, tokens := (now) // Calculate the remaining number of tokens resulting from the request. // Update the number of tokens and remove what needs to be taken tokens -= float64(n) // Calculate the wait duration // If the number of tokens is negative, it means that you need to wait and calculate the waiting time. var waitDuration if tokens < 0 { waitDuration = (-tokens) } // Decide result // Calculate whether the allocation requirements are met // 1. The size required to be allocated shall not exceed the bucket capacity // 2. The waiting time does not exceed the set waiting time ok := n <= && waitDuration <= maxFutureReserve // Prepare reservation // Finally construct a Resvervation object r := Reservation{ ok: ok, lim: lim, limit: , } if ok { = n = (waitDuration) } // Update state // The current limit value needs to be updated if ok { = now = tokens = } else { = last } () return r }
From an implementation perspective, limiter does not update the number of current buckets every once in a while, but records the number of tokens in the last access and the current bucket. When accessed again, the number of current tokens is calculated by the last access time to decide whether to issue the tokens.
This is the end of this article about Go's current limiter implementation based on token bucket. For more related contents of Go token bucket current limiter, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!