【造個輪子】使用Golang實現簡易令牌桶算法

在這里插入圖片描述

本文目錄

  • 1. 令牌桶算法
  • 2. 調用第三方庫實現令牌桶
  • 3. 手撕令牌桶

前言:之前在Bluebell社區項目中,我們使用了開源的庫來實現令牌桶限流,這次我們試著使用Go來手撕實現下令牌桶算法。

1. 令牌桶算法

為了防止網絡擁塞,需要限制流出或者流入網絡的流量,使流量以比較均勻的速度向外發送。令牌桶算法就實現了這個功能,可控制發送到網絡上數據的數目,并允許突發數據的發送。

令牌桶算法是網絡流量整形和速率限制中最常使用的一種算法。大小固定的令牌桶可自行以恒定的速率源源不斷地產生令牌。如果令牌不被消耗,或者被消耗的速度小于產生的速度,令牌就會不斷地增多,直到把桶填滿。后面再產生的令牌就會從桶中溢出。最后桶中可以保存的最大令牌數永遠不會超過桶的大小。

在這里插入圖片描述

傳送到令牌桶的數據包需要消耗令牌。不同大小的數據包,消耗的令牌數量不一樣

令牌桶中的每一個令牌都代表一次請求或者一個字節。如果令牌桶中存在令牌,則允許發送流量;而如果令牌桶中不存在令牌,則不允許發送流量。因此,如果突發門限被合理地配置并且令牌桶中有足夠的令牌,那么流量就可以以峰值速率發送。

2. 調用第三方庫實現令牌桶

上次我們調用了封裝好的令牌桶算法,代碼如下。

回顧一下關鍵點,fillInterval time.Duration:表示令牌桶的填充間隔時間(例如,每秒填充一次)。cap int64:表示令牌桶的最大容量(即桶中最多可以存儲的令牌數量)。

它返回一個函數,該函數的簽名是 func(c *gin.Context),這符合 gin 中間件的標準形式。也就是接收一個 *gin.Context 的參數,用于處理每個 HTTP 請求。

這里的 func(c *gin.Context) { ... } 是一個匿名函數。它捕獲 bucket 變量(閉包特性),因此可以在每次 HTTP 請求時使用同一個 bucket 實例。

func RateLimitMiddleware(fillInterval time.Duration, cap int64) func(c *gin.Context) {bucket := ratelimit.NewBucket(fillInterval, cap)return func(c *gin.Context) {// 如果取不到令牌就中斷本次請求返回 rate limit...if bucket.TakeAvailable(1) == 0 {c.String(http.StatusOK, "rate limit...")c.Abort()return}// 取到令牌就放行c.Next()}
}

我們在路由組中使用Use調用中間件,代碼如下。

在這里插入圖片描述

r.Use(Logger.GinLogger(), Logger.GinRecovery(stack: true), middlewares.RateLimitMiddleware(2*time.Second, cap: 1))

Logger.GinLogger():添加了一個日志記錄中間件,用于記錄 HTTP 請求的日志信息。

Logger.GinRecovery(stack: true):添加了一個錯誤恢復中間件,用于捕獲和處理在請求處理過程中發生的任何 panic,并可選地記錄堆棧跟蹤。

middlewares.RateLimitMiddleware(2*time.Second, cap: 1):添加了速率限制中間件,配置為每兩秒鐘向令牌桶中添加一個令牌,桶的容量為 1。這意味著在任何給定的兩秒時間內,最多只能處理一個請求。

3. 手撕令牌桶

直接上代碼吧,邏輯比較簡單。咱們來看看總體代碼,再進行部分講解。

package awesomeProjectimport ("sync""time"
)// 定義令牌桶結構
type tokenBucket struct {limitRate int           // 限制頻率,即每分鐘加入多少個令牌tokenChan chan struct{} // 令牌通道,可以理解為桶cap       int           // 令牌桶的容量muLock    *sync.Mutex   // 令牌桶鎖,保證線程安全stop      bool          // 停止標記,結束令牌桶
}// NewTokenBucket 創建令牌桶
func NewTokenBucket(limitRate, cap int) *tokenBucket {if cap < 1 {panic("token bucket cap must be large 1")}return &tokenBucket{tokenChan: make(chan struct{}, cap),limitRate: limitRate,muLock:    new(sync.Mutex),cap:       cap,}
}// Start 開啟令牌桶
func (b *tokenBucket) Start() {go b.produce()
}// 生產令牌
func (b *tokenBucket) produce() {for {b.muLock.Lock()if b.stop {close(b.tokenChan)b.muLock.Unlock()return}b.tokenChan <- struct{}d := time.Minute / time.Duration(b.limitRate)b.muLock.Unlock()time.Sleep(d)}
}// Consume 消費令牌
func (b *tokenBucket) Consume() {<-b.tokenChan
}// Stop 停止令牌桶
func (b *tokenBucket) Stop() {b.muLock.Lock()defer b.muLock.Unlock()b.stop = true
}

func NewTokenBucket(limitRate, cap int) *tokenBucket {if cap < 1 {panic("token bucket cap must be large 1")}return &tokenBucket{tokenChan: make(chan struct{}, cap),limitRate: limitRate,muLock:    new(sync.Mutex),cap:       cap,}
}

tokenChan chan struct{}tokenChan 是一個類型為 chan struct{} 的通道,用于存儲令牌。

make(chan struct{}, cap) 創建了一個容量為 cap 的緩沖通道。這里 struct{} 表示通道中存儲的是空結構體,僅用于占位,因為我們關心的是通道中元素的數量,而不是元素的值。這個通道模擬了令牌桶中令牌的數量,通道中的每個空結構體代表桶中的一個令牌。

func (b *tokenBucket) produce() {for {b.muLock.Lock()if b.stop {close(b.tokenChan)b.muLock.Unlock()return}b.tokenChan <- struct{}d := time.Minute / time.Duration(b.limitRate)b.muLock.Unlock()time.Sleep(d)}
}

time.Duration 是 Go 語言中用于表示時間間隔的類型,可以用于 time 包中的各種時間計算。
time.Minute 是一個常量,表示一分鐘的時間間隔。b.limitRate 是一個整數,表示每分鐘生成的令牌數量。


這里的通道chanstruct{}類型的,所以可以是別的。這里我們用struc{}空結構體,只是因為空結構體占用的內存空間非常小,適合用作通道中的占位符。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/72075.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/72075.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/72075.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C#開發的Base64編碼及解碼完整源碼及注意事項

在軟件開發時&#xff0c;經常用Base64編碼和解碼功能。本文介紹一個簡單易用的Base64 編碼和解碼工具&#xff0c;顧名思義&#xff0c;就是簡單快捷地進行 Base64 代碼的解碼或編碼操作。您的數據可以輕松地編碼為 Base64 編碼&#xff0c;也可以解碼為可讀的格式。傳輸數據時…

【Linux第一彈】Linux基礎指令(上)

目錄 1.ls指令 1.1 ls使用實例 2.pwd指令 3.cd指令 3.1 cd使用實例 4.touch指令 4.1touch使用實例 5.mkdir指令 5.1mkdir使用實例 6.rmdir指令和rm指令 6.1 rmdir指令使用實例->: 6.2 rm指令使用實例 7.man指令 8.cp指令 8.1 cp 使用實例 9.mv指令 9.1mv使用…

RabbitMQ系列(七)基本概念之Channel

RabbitMQ 中的 Channel&#xff08;信道&#xff09; 是客戶端與 RabbitMQ 服務器通信的虛擬會話通道&#xff0c;其核心作用在于優化資源利用并提升消息處理效率。以下是其核心機制與功能的詳細解析&#xff1a; 一、Channel 的核心定義 虛擬通信鏈路 Channel 是建立在 TCP 連…

Zookeeper(80)Zookeeper的常見問題有哪些?

Zookeeper作為分布式系統的協調服務&#xff0c;常見的問題主要集中在配置、性能、連接管理、數據一致性和節點故障等方面。以下是一些常見問題及其詳細解決方法和代碼示例。 1. 配置問題 問題描述 配置不當可能導致 Zookeeper 集群無法正常啟動或運行效率低下。 解決方法 …

如何管理路由器

一、管理路由器的必要性 1、需要修改撥號上網的密碼。 2、需要修改WIFI的SSID名字和密碼。 3、設置DHCP協議信息。 4、設置IP地址的過濾規則。 5、給某個設備連接設置網絡限速。 二、常見的方式 (一)web網頁方式 1、計算機用雙絞線或者WIFI的方式連接路由器。 2、在計算機中打開…

linux vim 撤銷 回退操作

在Linux的vim編輯器中&#xff0c;撤銷和回退操作是非常基本的&#xff0c;但它們可以通過不同的方式實現&#xff0c;具體取決于你想要的精確效果。下面是一些常用的方法&#xff1a; 1. 撤銷&#xff08;Undo&#xff09; 單個撤銷&#xff1a; 你可以通過按下u鍵來撤銷上一…

淺談流媒體協議以及視頻編解碼

流媒體協議介紹 流媒體協議用于傳輸視頻、音頻等多媒體數據&#xff0c;確保數據流暢地傳輸到用戶設備。常見的流媒體協議包括 RTMP、HLS、DASH、WebRTC 等&#xff0c;每種協議具有不同的特點和適用場景。 1. RTMP (Real-Time Messaging Protocol) 定義&#xff1a;由 Adob…

AF3 DataPipeline類process_multiseq_fasta 方法解讀

AlphaFold3 data_pipeline 模塊DataPipeline類的 process_multiseq_fasta 方法用于處理多序列 FASTA 文件,生成 AlphaFold3 結構預測所需的特征,適用于多鏈復合物的預測。它結合了 Minkyung Baek 在 Twitter 上提出的“AlphaFold-Gap”策略,即通過在多鏈 MSA 中插入固定長度…

圖片爬取案例

修改前的代碼 但是總顯示“失敗” 原因是 修改之后的代碼 import requests import os from urllib.parse import unquote# 原始URL url https://cn.bing.com/images/search?viewdetailV2&ccidTnImuvQ0&id5AE65CE4BE05EE7A79A73EEFA37578E87AE19421&thidOIP.TnI…

使用自動化運維工具 Ansible 集中化管理服務器

一、概述 Ansible 是一款為類 Unix 系統開發的自由開源的配置和自動化工具 官方網站:https://www.ansible.com/ Ansible 成立于 2013 年,總部設在北卡羅來納州達勒姆,聯合創始人 ad Ziouani 和高級副總裁 Todd Barr都是紅帽的老員工。Ansible 旗下的開源軟件 Ansible 十分…

CMU15445(2023fall) Project #2 - Extendible Hash Index 匠心分析

胡未滅&#xff0c;鬢已秋&#xff0c;淚空流 此生誰料 心在天山 身老滄州 ——訴衷情 完整代碼見&#xff1a; SnowLegend-star/CMU15445-2023fall: Having Conquered the Loftiest Peak, We Stand But a Step Away from Victory in This Stage. With unwavering determinati…

P1706 全排列問題

題目描述 按照字典序輸出自然數 1 到 n 所有不重復的排列&#xff0c;即 n 的全排列&#xff0c;要求所產生的任一數字序列中不允許出現重復的數字。 輸入格式 一個整數 n。 輸出格式 由 1~n 組成的所有不重復的數字序列&#xff0c;每行一個序列。 每個數字保留 5 個場寬。…

會話與會話管理:Cookie與Session的深度解析

一、什么是會話&#xff1f; 二、Cookie&#xff1a;客戶端存儲技術 1. Cookie的工作原理 2、在后端設置cookie 3、在前端設置cookie 三、瀏覽器開啟了cookie禁用怎么辦&#xff1f; 一、什么是會話&#xff1f; 會話&#xff08;Session&#xff09;是指一個用戶與服務器之間…

【Linux系統】—— 馮諾依曼體系結構與操作系統初理解

【Linux系統】—— 馮諾依曼體系結構與操作系統初理解 1 馮諾依曼體系結構1.1 基本概念理解1.2 CPU只和內存打交道1.3 為什么馮諾依曼是這種結構1.4 理解數據流動 2 操作系統2.1 什么是操作系統2.2 設計OS的目的2.3 操作系統小知識點2.4 如何理解"管理"2.5 系統調用和…

算法-二叉樹篇15-最大二叉樹

最大二叉樹 力扣題目鏈接 題目描述 給定一個不重復的整數數組 nums 。 最大二叉樹 可以用下面的算法從 nums 遞歸地構建: 創建一個根節點&#xff0c;其值為 nums 中的最大值。 遞歸地在最大值 左邊 的 子數組前綴上 構建左子樹。 遞歸地在最大值 右邊 的 子數組后綴上 構建…

運維Apache面試題及參考答案

目錄 簡述 Apache Web 服務器的主要特點及適用場景 Apache 的默認監聽端口是什么?如何修改為其他端口? Apache 的主配置文件名稱及路徑是什么?不同 Linux 發行版的默認路徑有何差異? 解釋 Apache 的 MPM(Multi-Processing Module)機制,列舉常見的工作模式(如 prefor…

51c自動駕駛~合集52

我自己的原文哦~ https://blog.51cto.com/whaosoft/13383340 #世界模型如何推演未來的千萬種可能 駕駛世界模型&#xff08;DWM&#xff09;&#xff0c;專注于預測駕駛過程中的場景演變&#xff0c;已經成為追求自動駕駛的一種有前景的范式。這些方法使自動駕駛系統能夠更…

用大白話解釋緩存Redis +MongoDB是什么有什么用怎么用

Redis和MongoDB是什么&#xff1f; Redis&#xff1a;像你家的“小冰箱”&#xff0c;專門存高頻使用的食物&#xff08;數據&#xff09;。它是基于內存的鍵值數據庫&#xff0c;讀寫速度極快&#xff08;每秒超10萬次操作&#xff09;。比如你每次打開手機App&#xff0c;用…

自然語言處理:詞頻-逆文檔頻率

介紹 大家好&#xff0c;博主又來給大家分享知識了。本來博主計劃完成稠密向量表示的內容分享后&#xff0c;就開啟自然語言處理中文本表示的講解。可在整理分享資料的時候&#xff0c;博主發現還有個知識點&#xff0c;必須得單獨拎出來好好說道說道。 這就是TF-IDF&#xf…

架構思維:架構的演進之路

文章目錄 引言為什么架構思維如此重要架構師的特點軟件架構的知識體系如何提升架構思維大型互聯網系統架構的演進之路一、大型互聯網系統的特點二、系統處理能力提升的兩種途徑三、大型互聯網系統架構演化過程四、總結 引言 在軟件開發行業中&#xff0c;有很多技術人可能會問…