目錄
构造限流器" >构造限流器
使用限流器" >使用限流器
Wait/WaitN" >Wait/WaitN
Allow/AllowN" >Allow/AllowN
Reserve/ReserveN" >Reserve/ReserveN
首頁 後端開發 Golang Golang官方限流器的用法詳解

Golang官方限流器的用法詳解

Jul 25, 2023 pm 03:08 PM
golang

限流器是提升服務穩定性的非常重要的元件,可以用來限制請求速率,保護服務,以免服務過載。限流器的實作方法有很多種,常見的限流演算法有固定視窗、滑動視窗、漏桶、令牌桶,我在前面的文章「常用限流演算法的應用場景與實現原理」 中給大家講解了這幾種限流方法自身的特點和應用場景,其中令牌桶在限流的同時還可以應對一定的突發流量,與互聯網應用容易因為熱點事件出現突發流量高峰的特徵較契合。

簡單來說,令牌桶就是想像有一個固定大小的桶,系統會以恆定速率向桶中放 Token,桶滿則暫時不放。在請求比較的少的時候桶可以先"攢"一些Token,應對突發的流量,如果桶中有剩餘 Token 就可以一直取。如果沒有剩餘 Token,則需要等到桶中被放置了 Token 才行。

關於令牌桶限流更詳細的解釋請參考文章:常用限流演算法的應用場景與實作原理

有的同學在看明白令牌桶的原理後就非常想去自己實現一個限流器應用到自己的專案裡,em... 怎麼說呢,造個輪子確實有利於自己水平提高,不過要是應用到商用專案裡的話其實大可不必自己去造輪子,Golang官方已經替我們造好輪子啦......~!

Golang 官方提供的擴充函式庫裡就自帶了限流演算法的實現,即 golang.org/x/time/rate。此限流器也是基於 Token Bucket(令牌桶) 實現的。

限流器的內部結構

time/rate套件的Limiter類型對限流器進行了定義,所有限流功能都是透過基於Limiter類型實現的,其內部結構如下:

type Limiter struct {
 mu     sync.Mutex
 limit  Limit
 burst  int // 令牌桶的大小
 tokens float64
 last time.Time // 上次更新tokens的时间
 lastEvent time.Time // 上次发生限速器事件的时间(通过或者限制都是限速器事件)
}
登入後複製

其主要字段的作用是:

  • limit:limit字段表示往桶里放Token的速率,它的类型是Limit,是int64的类型别名。设置limit时既可以用数字指定每秒向桶中放多少个Token,也可以指定向桶中放Token的时间间隔,其实指定了每秒放Token的个数后就能计算出放每个Token的时间间隔了。
  • burst: 令牌桶的大小。
  • tokens: 桶中的令牌。
  • last: 上次往桶中放 Token 的时间。
  • lastEvent:上次发生限速器事件的时间(通过或者限制都是限速器事件)

可以看到在 timer/rate 的限流器实现中,并没有单独维护一个 Timer 和队列去真的每隔一段时间向桶中放令牌,而是仅仅通过计数的方式表示桶中剩余的令牌。每次消费取 Token 之前会先根据上次更新令牌数的时间差更新桶中Token数

大概了解了time/rate限流器的内部实现后,下面的内容我们会集中介绍下该组件的具体使用方法:

构造限流器

我们可以使用以下方法构造一个限流器对象:

limiter := rate.NewLimiter(10, 100);
登入後複製

这里有两个参数:

  1. 第一个参数是 r Limit,设置的是限流器Limiter的limit字段,代表每秒可以向 Token 桶中产生多少 token。Limit 实际上是 float64 的别名。
  2. 第二个参数是 b int,b 代表 Token 桶的容量大小,也就是设置的限流器 Limiter 的burst字段。

那么,对于以上例子来说,其构造出的限流器的令牌桶大小为 100, 以每秒 10 个 Token 的速率向桶中放置 Token。

除了给r Limit参数直接指定每秒产生的 Token 个数外,还可以用 Every 方法来指定向桶中放置 Token 的间隔,例如:

limit := rate.Every(100 * time.Millisecond);
limiter := rate.NewLimiter(limit, 100);
登入後複製

以上就表示每 100ms 往桶中放一个 Token。本质上也是一秒钟往桶里放 10 个。

使用限流器

Limiter 提供了三类方法供程序消费 Token,可以每次消费一个 Token,也可以一次性消费多个 Token。每种方法代表了当 Token 不足时,各自不同的对应手段,可以阻塞等待桶中Token补充,也可以直接返回取Token失败。

Wait/WaitN

func (lim *Limiter) Wait(ctx context.Context) (err error)
func (lim *Limiter) WaitN(ctx context.Context, n int) (err error)
登入後複製

Wait 实际上就是 WaitN(ctx,1)

当使用 Wait 方法消费 Token 时,如果此时桶内 Token 数组不足 (小于 N),那么 Wait 方法将会阻塞一段时间,直至 Token 满足条件。如果充足则直接返回。

这里可以看到,Wait 方法有一个 context 参数。我们可以设置 context 的 Deadline 或者 Timeout,来决定此次 Wait 的最长时间。

// 一直等到获取到桶中的令牌
err := limiter.Wait(context.Background())
if err != nil {
 fmt.Println("Error: ", err)
}

// 设置一秒的等待超时时间
ctx, _ := context.WithTimeout(context.Background(), time.Second * 1)
err := limiter.Wait(ctx)
if err != nil {
 fmt.Println("Error: ", err)
}
登入後複製

Allow/AllowN

func (lim *Limiter) Allow() bool
func (lim *Limiter) AllowN(now time.Time, n int) bool
登入後複製

Allow 实际上就是对 AllowN(time.Now(),1) 进行简化的函数。

AllowN 方法表示,截止到某一时刻,目前桶中数目是否至少为 n 个,满足则返回 true,同时从桶中消费 n 个 token。反之不消费桶中的Token,返回false。

对应线上的使用场景是,如果请求速率超过限制,就直接丢弃超频后的请求。

if limiter.AllowN(time.Now(), 2) {
    fmt.Println("event allowed")
} else {
    fmt.Println("event not allowed")
}
登入後複製

Reserve/ReserveN

func (lim *Limiter) Reserve() *Reservation
func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation
登入後複製

Reserve 相当于 ReserveN(time.Now(), 1)

ReserveN 的用法就相对来说复杂一些,当调用完成后,无论 Token 是否充足,都会返回一个 *Reservation 对象。你可以调用该对象的Delay()方法,该方法返回的参数类型为time.Duration,反映了需要等待的时间,必须等到等待时间之后,才能进行接下来的工作。如果不想等待,可以调用Cancel()方法,该方法会将 Token 归还。

举一个简单的例子,我们可以这么使用 Reserve 方法。

r := limiter.Reserve()
f !r.OK() {
    // Not allowed to act! Did you remember to set lim.burst to be > 0 ?
    return
}
time.Sleep(r.Delay())
Act() // 执行相关逻辑
登入後複製

动态调整速率和桶大小

Limiter 支持创建后动态调整速率和桶大小:

  1. SetLimit(Limit) 改变放入 Token 的速率
  2. SetBurst(int) 改变 Token 桶大小

有了这两个方法,可以根据现有环境和条件以及我们的需求,动态地改变 Token 桶大小和速率。

总结

今天我们总结了 Golang 官方限流器的使用方法,它是一种令牌桶算实现的限流器。其中 Wait/WaitNAllow/AllowN 这两组方法在平时用的比较多,前者是消费Token时如果桶中Token不足可以让程序等待桶中新Token的放入(最好设置上等待时长)后者则是在桶中的Token不足时选择直接丢弃请求。

除了Golang官方提供的限流器實現,Uber公司開源的限流器uber-go/ratelimit也是一個很好的選擇,與Golang官方限流器不同的是Uber的限流器是透過漏桶演算法實現的,不過對傳統的漏桶演算法進行了改良,有興趣的同學可以自行去體驗一下。

以上是Golang官方限流器的用法詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何使用 Golang 安全地讀取和寫入檔案? 如何使用 Golang 安全地讀取和寫入檔案? Jun 06, 2024 pm 05:14 PM

在Go中安全地讀取和寫入檔案至關重要。指南包括:檢查檔案權限使用defer關閉檔案驗證檔案路徑使用上下文逾時遵循這些準則可確保資料的安全性和應用程式的健全性。

如何為 Golang 資料庫連線配置連線池? 如何為 Golang 資料庫連線配置連線池? Jun 06, 2024 am 11:21 AM

如何為Go資料庫連線配置連線池?使用database/sql包中的DB類型建立資料庫連線;設定MaxOpenConns以控制最大並發連線數;設定MaxIdleConns以設定最大空閒連線數;設定ConnMaxLifetime以控制連線的最大生命週期。

如何在 Golang 中將 JSON 資料保存到資料庫中? 如何在 Golang 中將 JSON 資料保存到資料庫中? Jun 06, 2024 am 11:24 AM

可以透過使用gjson函式庫或json.Unmarshal函數將JSON資料儲存到MySQL資料庫中。 gjson函式庫提供了方便的方法來解析JSON字段,而json.Unmarshal函數需要一個目標類型指標來解組JSON資料。這兩種方法都需要準備SQL語句和執行插入操作來將資料持久化到資料庫中。

Golang框架與Go框架:內部架構與外部特性對比 Golang框架與Go框架:內部架構與外部特性對比 Jun 06, 2024 pm 12:37 PM

GoLang框架與Go框架的差異體現在內部架構與外部特性。 GoLang框架基於Go標準函式庫,擴充其功能,而Go框架由獨立函式庫組成,以實現特定目的。 GoLang框架更靈活,Go框架更容易上手。 GoLang框架在效能上稍有優勢,Go框架的可擴充性更高。案例:gin-gonic(Go框架)用於建立RESTAPI,而Echo(GoLang框架)用於建立Web應用程式。

從前端轉型後端開發,學習Java還是Golang更有前景? 從前端轉型後端開發,學習Java還是Golang更有前景? Apr 02, 2025 am 09:12 AM

後端學習路徑:從前端轉型到後端的探索之旅作為一名從前端開發轉型的後端初學者,你已經有了nodejs的基礎,...

如何找出 Golang 正規表示式符合的第一個子字串? 如何找出 Golang 正規表示式符合的第一個子字串? Jun 06, 2024 am 10:51 AM

FindStringSubmatch函數可找出正規表示式匹配的第一個子字串:此函數傳回包含匹配子字串的切片,第一個元素為整個匹配字串,後續元素為各個子字串。程式碼範例:regexp.FindStringSubmatch(text,pattern)傳回符合子字串的切片。實戰案例:可用於匹配電子郵件地址中的域名,例如:email:="user@example.com",pattern:=@([^\s]+)$獲取域名match[1]。

golang框架開發實戰教學:常見疑問解答 golang框架開發實戰教學:常見疑問解答 Jun 06, 2024 am 11:02 AM

Go框架開發常見問題:框架選擇:取決於應用需求和開發者偏好,如Gin(API)、Echo(可擴展)、Beego(ORM)、Iris(效能)。安裝和使用:使用gomod指令安裝,導入框架並使用。資料庫互動:使用ORM庫,如gorm,建立資料庫連線和操作。身份驗證和授權:使用會話管理和身份驗證中間件,如gin-contrib/sessions。實戰案例:使用Gin框架建立一個簡單的部落格API,提供POST、GET等功能。

如何用 Golang 使用預先定義時區? 如何用 Golang 使用預先定義時區? Jun 06, 2024 pm 01:02 PM

Go語言中使用預先定義時區包含下列步驟:匯入"time"套件。透過LoadLocation函數載入特定時區。在建立Time物件、解析時間字串等操作中使用已載入的時區,進行日期和時間轉換。使用不同時區的日期進行比較,以說明預先定義時區功能的應用。

See all articles