【Gin-Web】Bluebell社區項目梳理6:限流策略-漏桶與令牌桶

本文目錄

  • 一、限流
  • 二、漏桶
  • 三、令牌桶算法
  • 四、Gin框架中實現令牌桶限流

一、限流

限流又稱為流量控制,也就是流控,通常是指限制到達系統的并發請求數

限流雖然會影響部分用戶的使用體驗,但是能一定程度上保證系統的穩定性,不至于崩潰。

常見的各種廠商的公開API服務通常也會限制用戶的請求次數,比如百度地圖的API來限制請求數等。

二、漏桶

漏桶是一種比較常見的限流策略。

一句話來概括漏洞的核心就是:數據以任意速率進入漏桶,但是漏桶以固定速率(通常是預先設定的速率)將數據輸出。

有點像削峰填谷,但是它缺點也很明顯,就是不能很好處理有大量突發請求的場景。

import ("fmt""time""go.uber.org/ratelimit"
)func main() {rl := ratelimit.New(100) // per secondprev := time.Now()for i := 0; i < 10; i++ {now := rl.Take()fmt.Println(i, now.Sub(prev))prev = now}// Output:// 0 0// 1 10ms// 2 10ms// 3 10ms// 4 10ms// 5 10ms// 6 10ms// 7 10ms// 8 10ms// 9 10ms
}

上面你的代碼是uber團隊開源的漏桶庫ratelimit,這個庫比較簡單。

ratelimit.New(100) 創建了一個速率限制器(ratelimit.Limiter),限制頻率為每秒 100 次。這里的 100 表示每秒最多可以獲取 100 次令牌(tokens),即每秒最多可以執行 100 次操作。

prev 是一個 time.Time 類型的變量,用于記錄上一次操作的時間。初始值為當前時間。

rl.Take()這是 ratelimit.Limiter 的一個方法,用于獲取一個令牌。如果當前時間距離上一次獲取令牌的時間小于 1/100 秒(因為每秒 100 次),Take() 方法會阻塞,直到下一個令牌可用。now 是獲取令牌時的時間點(time.Time 類型)。

now.Sub(prev) 計算當前時間點 now 和上一次時間點 prev 之間的時間差(返回 time.Duration 類型)。這個時間差表示兩次操作之間的時間間隔。

所以這段代碼的目的是通過 ratelimit.New(100) 創建一個每秒最多執行 100 次操作的速率限制器,并在循環中模擬 10 次操作。每次操作之間的時間間隔會被打印出來,用于觀察速率限制器是否按預期工作。

我們來看看這個限流器的實現源碼。

限制器是一個接口類型,其要求實現一個Take()方法:

type Limiter interface {// Take方法應該阻塞已確保滿足 RPSTake() time.Time
}

實現限制器接口的結構體定義如下,這里可以重點留意下maxSlack字段,它在后面的Take()方法中的處理。

type limiter struct {sync.Mutex                // 鎖last       time.Time      // 上一次的時刻sleepFor   time.Duration  // 需要等待的時間perRequest time.Duration  // 每次的時間間隔maxSlack   time.Duration  // 最大的富余量clock      Clock          // 時鐘
}

limiter結構體實現Limiter接口的Take()方法內容如下:

// Take 會阻塞確保兩次請求之間的時間走完
// Take 調用平均數為 time.Second/rate.
func (t *limiter) Take() time.Time {t.Lock()defer t.Unlock()now := t.clock.Now()// 如果是第一次請求就直接放行if t.last.IsZero() {t.last = nowreturn t.last}// sleepFor 根據 perRequest 和上一次請求的時刻計算應該sleep的時間// 由于每次請求間隔的時間可能會超過perRequest, 所以這個數字可能為負數,并在多個請求之間累加t.sleepFor += t.perRequest - now.Sub(t.last)// 我們不應該讓sleepFor負的太多,因為這意味著一個服務在短時間內慢了很多隨后會得到更高的RPS。if t.sleepFor < t.maxSlack {t.sleepFor = t.maxSlack}// 如果 sleepFor 是正值那么就 sleepif t.sleepFor > 0 {t.clock.Sleep(t.sleepFor)t.last = now.Add(t.sleepFor)t.sleepFor = 0} else {t.last = now}return t.last
}

上面的代碼根據記錄每次請求的間隔時間和上一次請求的時刻來計算當次請求需要阻塞的時間——sleepFor,這里需要留意的是sleepFor的值可能為負,在經過間隔時間長的兩次訪問之后會導致隨后大量的請求被放行,所以代碼中針對這個場景有專門的優化處理。創建限制器的New()函數中會為maxSlack設置初始值,也可以通過WithoutSlack這個Option取消這個默認值。

func New(rate int, opts ...Option) Limiter {l := &limiter{perRequest: time.Second / time.Duration(rate),maxSlack:   -10 * time.Second / time.Duration(rate),}for _, opt := range opts {opt(l)}if l.clock == nil {l.clock = clock.New()}return l
}

三、令牌桶算法

令牌桶按固定的速率往桶里放入令牌,并且只要能從桶里取出令牌就能通過,令牌桶支持突發流量的快速處理。

如果放令牌的速率很快,那么令牌桶里邊有很多令牌了,這個時候當有大量的客戶端請求過來了,那么就可以直接取出令牌處理請求。

也就是令牌以固定的速率(例如每秒生成若干個令牌)被添加到桶中,桶有一個最大容量,當桶滿時,多余的令牌會被丟棄。

次發送數據(如網絡請求或數據包)時,需要從桶中取出一定數量的令牌。如果桶中有足夠的令牌,則允許發送數據,并從桶中扣除相應的令牌;如果令牌不足,則請求被拒絕或排隊等待。

由于桶中的令牌可以積累,因此在短時間內允許突發流量(bursty traffic),只要平均速率符合限制。

對于從桶里取不到令牌的場景,我們可以選擇等待也可以直接拒絕并返回。這個需要根據業務場景來處理,如果業務場景不同,那么相對應的處理也就需要不一樣。

可以參照github上的開源庫:github.com/juju/ratelimit庫。

創建令牌桶的方法如下:

// 創建指定填充速率和容量大小的令牌桶
func NewBucket(fillInterval time.Duration, capacity int64) *Bucket// 創建指定填充速率、容量大小和每次填充的令牌數的令牌桶
func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket// 創建填充速度為指定速率和容量大小的令牌桶
// NewBucketWithRate(0.1, 200) 表示每秒填充20個令牌
func NewBucketWithRate(rate float64, capacity int64) *Bucket

取出令牌的方法如下:

// 取token(非阻塞)
func (tb *Bucket) Take(count int64) time.Duration
func (tb *Bucket) TakeAvailable(count int64) int64// 最多等maxWait時間取token
func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool)// 取token(阻塞)
func (tb *Bucket) Wait(count int64)
func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool

(注意這個令牌桶跟JWT算法中的Token并不是一個概念。)

雖說是令牌桶但沒有必要真的去生成令牌放到桶里,我們只需要每次來取令牌的時候計算一下,當前是否有足夠的令牌就可以了,具體的計算方式可以總結為下面的公式:

當前令牌數 = 上一次剩余的令牌數 + (本次取令牌的時刻-上一次取令牌的時刻)/放置令牌的時間間隔 * 每次放置的令牌數

四、Gin框架中實現令牌桶限流

這里我們可以將限流組件定義成中間件,因為中間件是在所有請求的必經之路上。按照需要限流的策略將中間件添加到不同的地方,如果要對全站進行限流就可以添加成全局中間件。如果某一組路由需要限流,就添加到對應的路由組就行。

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()}
}

cap是填充速率,比如一秒鐘填一個等。

然后我們在路由中生成即可,這樣全網站就限速了。
在這里插入圖片描述

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

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

相關文章

Linux高并發服務器開發 第十九天(線程 進程)

目錄 1.進程組和會話 2.守護進程 2.1守護進程daemon概念 2.2創建守護進程 3.線程 3.1線程的概念 3.2線程內核三級映射 3.3線程共享 3.4線程優缺點 4.線程控制原語 4.1獲取線程id 4.2創建線程 4.3循環創建N個子線 4.4子線程傳參地址&#xff0c;錯誤示例 4.5線程…

軟件工程和系統分析與設計

軟件工程 1、軟件危機 2、軟件過程模型 2.1 瀑布模型 2.2原型模型 2.3螺旋模型 2.4敏捷模型 2.5軟件統一過程 3、軟件能力成熟度模型 CMM 4、軟件能力成熟度模型集成 CMMI 系統分析與設計 1、結構化方法SASD 1.1結構化分析 DFD 1.2結構化設計 SD-是一種面向數據流的設計…

Qt/C++面試【速通筆記一】

Qt 信號與槽機制 什么是信號&#xff08;Signal&#xff09;和槽&#xff08;Slot&#xff09;&#xff1f; 在Qt中&#xff0c;信號&#xff08;Signal&#xff09;和槽&#xff08;Slot&#xff09;是實現對象之間通信的一種機制。信號是對象在某些事件發生時發出的通知&…

LangChain大模型應用開發:構建Agent智能體

介紹 大家好&#xff0c;博主又來給大家分享知識了。今天要給大家分享的內容是使用LangChain進行大模型應用開發中的構建Agent智能體。 在LangChain中&#xff0c;Agent智能體是一種能夠根據輸入的任務或問題&#xff0c;動態地決定使用哪些工具(如搜索引擎、數據庫查詢等)來…

微服務架構概述及創建父子項目

目錄 一&#xff0c;什么是單體架構 二&#xff0c;什么是集群和分布式架構 三&#xff0c;什么是微服務架構 四&#xff0c;解決微服務難題的方案Spring-cloud Spring Cloud Alibaba是阿里巴實現的方案&#xff0c;基于SpringCloud的規范。如果說Spring Cloud Netflix 是…

C/C++跳動的愛心

系列文章 序號直達鏈接1C/C李峋同款跳動的愛心2C/C跳動的愛心3C/C經典愛心4C/C滿屏飄字5C/C大雪紛飛6C/C炫酷煙花7C/C黑客帝國同款字母雨8C/C櫻花樹9C/C奧特曼10C/C精美圣誕樹11C/C俄羅斯方塊小游戲12C/C貪吃蛇小游戲13C/C孤單又燦爛的神14C/C閃爍的愛心15C/C哆啦A夢16C/C簡單…

量子計算的威脅,以及企業可以采取的措施

當谷歌、IBM、Honeywell和微軟等科技巨頭紛紛投身量子計算領域時&#xff0c;一場技術軍備競賽已然拉開帷幕。 量子計算雖能為全球數字經濟帶來巨大價值&#xff0c;但也有可能對相互關聯的系統、設備和數據造成損害。這一潛在影響在全球網絡安全領域引起了強烈關注。也正因如…

Unity制作游戲——前期準備:Unity2023和VS2022下載和安裝配置——附安裝包

1.Unity2023的下載和安裝配置 &#xff08;1&#xff09;Unity官網下載地址&#xff08;國際如果進不去&#xff0c;進國內的官網&#xff0c;下面以國內官網流程為例子&#xff09; unity中國官網&#xff1a;Unity中國官網 - 實時內容開發平臺 | 3D、2D、VR & AR可視化 …

23貪心算法

分發餅干 class Solution { public:int findContentChildren(vector<int>& g, vector<int>& s) {int i0,j0;int count0;sort(s.begin(),s.end());sort(g.begin(),g.end());while(i<g.size()&&j<s.size()){if(g[i]<s[j]){i;j;count;}else…

Spark 和 Flink

Spark 和 Flink 都是目前流行的大數據處理引擎&#xff0c;但它們在架構設計、應用場景、性能和生態方面有較大區別。以下是詳細對比&#xff1a; 1. 架構與核心概念 方面Apache SparkApache Flink計算模型微批&#xff08;Micro-Batch&#xff09;為主&#xff0c;但支持結構…

Android 串口通信

引言 在iot項目中&#xff0c;Android 端總會有和硬件通信。 通信這里&#xff1a;串口通信&#xff0c;藍牙通信或者局域網通信。 這里講一下串口通信。 什么是串口&#xff1f; “串口”&#xff08;Serial Port&#xff09;通常是指一種用于與外部設備進行串行通信的接口。…

【計算機網絡】OSI模型、TCP/IP模型、路由器、集線器、交換機

一、計算機網絡分層結構 計算機網絡分層結構 指將計算機網絡的功能劃分為多個層次&#xff0c;每個層次都有其特定的功能和協議&#xff0c;并且層次之間通過接口進行通信。 分層設計的優勢&#xff1a; 模塊化&#xff1a;各層獨立發展&#xff08;如IPv4→IPv6&#xff0c…

從人機環境系統智能角度看傳統IP的全球化二次創作法則

從人機環境系統智能的視角看&#xff0c;傳統IP的全球化二次創作法則需結合技術、文化、倫理與環境的復雜協同。這一過程不僅是內容的本土化改編&#xff0c;更是人、機器與環境在動態交互中實現價值共創的體現。 一、人機環境系統智能的底層邏輯與IP二次創作的融合 1、感知層&…

實現 INFINI Console 與 GitHub 的單點登錄集成:一站式身份驗證解決方案

本文將為您詳細解析如何通過 GitHub OAuth 2.0 協議&#xff0c;為 INFINI Console 實現高效、安全的單點登錄&#xff08;Single Sign-On, SSO&#xff09;集成。通過此方案&#xff0c;用戶可直接使用 GitHub 賬戶無縫登錄 INFINI Console&#xff0c;簡化身份驗證流程&#…

記一次復雜分頁查詢的優化歷程:從臨時表到普通表的架構演進

1. 問題背景 在項目開發中&#xff0c;我們需要實現一個復雜的分頁查詢功能&#xff0c;涉及大量 IP 地址數據的處理和多表關聯。在我接手這個項目的時候,代碼是這樣的 要知道代碼里面的 ipsList 數據可能幾萬條甚至更多,這樣拼接的sql,必然是要內存溢出的,一味地擴大jvm參數不…

C++關鍵字之mutable

1.介紹 在C中&#xff0c;mutable是一個關鍵字&#xff0c;用于修飾類的成員變量。它的主要作用是允許在常量成員函數或常量對象中修改被標記為mutable的成員變量。通常情況下&#xff0c;常量成員函數不能修改類的成員變量&#xff0c;但有些情況下&#xff0c;某些成員變量的…

云計算中的API網關是什么?為什么它很重要?

在云計算架構中&#xff0c;API網關&#xff08;API Gateway&#xff09;是一個重要的組件&#xff0c;主要用于管理、保護和優化不同服務之間的接口&#xff08;API&#xff09;通信。簡單來說&#xff0c;API網關就像是一個中介&#xff0c;它充當客戶端和后端服務之間的“橋…

深搜專題2:組合問題

描述 組合問題就是從n個元素中抽出r個元素(不分順序且r < &#xff1d; n)&#xff0c; 我們可以簡單地將n個元素理解為自然數1&#xff0c;2&#xff0c;…&#xff0c;n&#xff0c;從中任取r個數。 例如n &#xff1d; 5 &#xff0c;r &#xff1d; 3 &#xff0c;所…

uniapp多端適配

UniApp是一個基于Vue.js開發多端應用的框架&#xff0c;它可以讓開發者編寫一次代碼&#xff0c;同時適配iOS、Android、Web等多個平臺。 環境搭建&#xff1a; UniApp基于Vue.js開發&#xff0c;所以需要先安裝Vue CLI npm install -g vue/cli 創建一個新的UniApp項目&…

Error [ERR_REQUIRE_ESM]: require() of ES Module

報錯信息&#xff1a; 【報錯】Message.js 導入方式不對&#xff0c;用的是 ES Moudle 的語法&#xff0c;提示使用 import 引入文件 項目開發沒有用到 js-message 依賴&#xff0c;是 node-ipc 依賴中用到的 js-message 依賴&#xff0c; node-ipc 中限制 js-message 版本&a…