国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

gin中使用限流中間件

這篇具有很好參考價(jià)值的文章主要介紹了gin中使用限流中間件。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

限流又稱為流量控制(流控),通常是指限制到達(dá)系統(tǒng)的并發(fā)請(qǐng)求數(shù),本文列舉了常見的限流策略,并以gin框架為例演示了如何為項(xiàng)目添加限流組件。

限流

限流又稱為流量控制(流控),通常是指限制到達(dá)系統(tǒng)的并發(fā)請(qǐng)求數(shù)。

我們生活中也會(huì)經(jīng)常遇到限流的場(chǎng)景,比如:某景區(qū)限制每日進(jìn)入景區(qū)的游客數(shù)量為8萬人;沙河地鐵站早高峰通過站外排隊(duì)逐一放行的方式限制同一時(shí)間進(jìn)入車站的旅客數(shù)量等。

限流雖然會(huì)影響部分用戶的使用體驗(yàn),但是卻能在一定程度上報(bào)障系統(tǒng)的穩(wěn)定性,不至于崩潰(大家都沒了用戶體驗(yàn))。

而互聯(lián)網(wǎng)上類似需要限流的業(yè)務(wù)場(chǎng)景也有很多,比如電商系統(tǒng)的秒殺、微博上突發(fā)熱點(diǎn)新聞、雙十一購物節(jié)、12306搶票等等。這些場(chǎng)景下的用戶請(qǐng)求量通常會(huì)激增,遠(yuǎn)遠(yuǎn)超過平時(shí)正常的請(qǐng)求量,此時(shí)如果不加任何限制很容易就會(huì)將后端服務(wù)打垮,影響服務(wù)的穩(wěn)定性。

此外,一些廠商公開的API服務(wù)通常也會(huì)限制用戶的請(qǐng)求次數(shù),比如百度地圖開放平臺(tái)等會(huì)根據(jù)用戶的付費(fèi)情況來限制用戶的請(qǐng)求數(shù)等。

gin中使用限流中間件,gin從入門到實(shí)踐,gin,中間件

常用的限流策略

漏桶

漏桶法限流很好理解,假設(shè)我們有一個(gè)水桶按固定的速率向下方滴落一滴水,無論有多少請(qǐng)求,請(qǐng)求的速率有多大,都按照固定的速率流出,對(duì)應(yīng)到系統(tǒng)中就是按照固定的速率處理請(qǐng)求。

gin中使用限流中間件,gin從入門到實(shí)踐,gin,中間件

漏桶法的關(guān)鍵點(diǎn)在于漏桶始終按照固定的速率運(yùn)行,但是它并不能很好的處理有大量突發(fā)請(qǐng)求的場(chǎng)景,畢竟在某些場(chǎng)景下我們可能需要提高系統(tǒng)的處理效率,而不是一味的按照固定速率處理請(qǐng)求。

關(guān)于漏桶的實(shí)現(xiàn),uber團(tuán)隊(duì)有一個(gè)開源的github.com/uber-go/ratelimit庫。 這個(gè)庫的使用方法比較簡(jiǎn)單,Take()?方法會(huì)返回漏桶下一次滴水的時(shí)間。

import (
	"fmt"
	"time"

	"go.uber.org/ratelimit"
)

func main() {
    rl := ratelimit.New(100) // per second

    prev := 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
}

它的源碼實(shí)現(xiàn)也比較簡(jiǎn)單,這里大致說一下關(guān)鍵的地方,有興趣的同學(xué)可以自己去看一下完整的源碼。

限制器是一個(gè)接口類型,其要求實(shí)現(xiàn)一個(gè)Take()方法:

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

實(shí)現(xiàn)限制器接口的結(jié)構(gòu)體定義如下,這里可以重點(diǎn)留意下maxSlack字段,它在后面的Take()方法中的處理。

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

limiter結(jié)構(gòu)體實(shí)現(xiàn)Limiter接口的Take()方法內(nèi)容如下:

// Take 會(huì)阻塞確保兩次請(qǐng)求之間的時(shí)間走完
// Take 調(diào)用平均數(shù)為 time.Second/rate.
func (t *limiter) Take() time.Time {
	t.Lock()
	defer t.Unlock()

	now := t.clock.Now()

	// 如果是第一次請(qǐng)求就直接放行
	if t.last.IsZero() {
		t.last = now
		return t.last
	}

	// sleepFor 根據(jù) perRequest 和上一次請(qǐng)求的時(shí)刻計(jì)算應(yīng)該sleep的時(shí)間
	// 由于每次請(qǐng)求間隔的時(shí)間可能會(huì)超過perRequest, 所以這個(gè)數(shù)字可能為負(fù)數(shù),并在多個(gè)請(qǐng)求之間累加
	t.sleepFor += t.perRequest - now.Sub(t.last)

	// 我們不應(yīng)該讓sleepFor負(fù)的太多,因?yàn)檫@意味著一個(gè)服務(wù)在短時(shí)間內(nèi)慢了很多隨后會(huì)得到更高的RPS。
	if t.sleepFor < t.maxSlack {
		t.sleepFor = t.maxSlack
	}

	// 如果 sleepFor 是正值那么就 sleep
	if t.sleepFor > 0 {
		t.clock.Sleep(t.sleepFor)
		t.last = now.Add(t.sleepFor)
		t.sleepFor = 0
	} else {
		t.last = now
	}

	return t.last
}

上面的代碼根據(jù)記錄每次請(qǐng)求的間隔時(shí)間和上一次請(qǐng)求的時(shí)刻來計(jì)算當(dāng)次請(qǐng)求需要阻塞的時(shí)間——sleepFor,這里需要留意的是sleepFor的值可能為負(fù),在經(jīng)過間隔時(shí)間長(zhǎng)的兩次訪問之后會(huì)導(dǎo)致隨后大量的請(qǐng)求被放行,所以代碼中針對(duì)這個(gè)場(chǎng)景有專門的優(yōu)化處理。創(chuàng)建限制器的New()函數(shù)中會(huì)為maxSlack設(shè)置初始值,也可以通過WithoutSlack這個(gè)Option取消這個(gè)默認(rèn)值。

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
}

令牌桶

令牌桶其實(shí)和漏桶的原理類似,令牌桶按固定的速率往桶里放入令牌,并且只要能從桶里取出令牌就能通過,令牌桶支持突發(fā)流量的快速處理。

gin中使用限流中間件,gin從入門到實(shí)踐,gin,中間件

對(duì)于從桶里取不到令牌的場(chǎng)景,我們可以選擇等待也可以直接拒絕并返回。

對(duì)于令牌桶的Go語言實(shí)現(xiàn),大家可以參照github.com/juju/ratelimit庫。這個(gè)庫支持多種令牌桶模式,并且使用起來也比較簡(jiǎn)單。

創(chuàng)建令牌桶的方法:

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

取出令牌的方法如下:

// 取token(非阻塞)
func (tb *Bucket) Take(count int64) time.Duration
func (tb *Bucket) TakeAvailable(count int64) int64

// 最多等maxWait時(shí)間取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

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

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

github.com/juju/ratelimit這個(gè)庫中關(guān)于令牌數(shù)計(jì)算的源代碼如下:

func (tb *Bucket) currentTick(now time.Time) int64 {
	return int64(now.Sub(tb.startTime) / tb.fillInterval)
}
func (tb *Bucket) adjustavailableTokens(tick int64) {
	if tb.availableTokens >= tb.capacity {
		return
	}
	tb.availableTokens += (tick - tb.latestTick) * tb.quantum
	if tb.availableTokens > tb.capacity {
		tb.availableTokens = tb.capacity
	}
	tb.latestTick = tick
	return
}

獲取令牌的TakeAvailable()函數(shù)關(guān)鍵部分的源代碼如下:

func (tb *Bucket) takeAvailable(now time.Time, count int64) int64 {
	if count <= 0 {
		return 0
	}
	tb.adjustavailableTokens(tb.currentTick(now))
	if tb.availableTokens <= 0 {
		return 0
	}
	if count > tb.availableTokens {
		count = tb.availableTokens
	}
	tb.availableTokens -= count
	return count
}

大家從代碼中也可以看到其實(shí)令牌桶的實(shí)現(xiàn)并沒有很復(fù)雜。

gin框架中使用限流中間件

在gin框架構(gòu)建的項(xiàng)目中,我們可以將限流組件定義成中間件。

這里使用令牌桶作為限流策略,編寫一個(gè)限流中間件如下:

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

對(duì)于該限流中間件的注冊(cè)位置,我們可以按照不同的限流策略將其注冊(cè)到不同的位置,例如:

  1. 如果要對(duì)全站限流就可以注冊(cè)成全局的中間件。
  2. 如果是某一組路由需要限流,那么就只需將該限流中間件注冊(cè)到對(duì)應(yīng)的路由組即可。

參考文章:

https://www.fansimao.com/937511.html文章來源地址http://www.zghlxwxcb.cn/news/detail-823227.html

到了這里,關(guān)于gin中使用限流中間件的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • [每周一更]-(第83期):Go新項(xiàng)目-Gin中間件的使用和案例(10)

    [每周一更]-(第83期):Go新項(xiàng)目-Gin中間件的使用和案例(10)

    在 Gin 中,中間件是一種用于處理 HTTP 請(qǐng)求和響應(yīng)的功能強(qiáng)大的機(jī)制。中間件是一段位于請(qǐng)求處理鏈和最終處理器之間的代碼, 它可以截獲請(qǐng)求、執(zhí)行預(yù)處理操作,修改請(qǐng)求或響應(yīng),然后將控制權(quán)傳遞給下一個(gè)中間件或最終的請(qǐng)求處理器。 中間件在業(yè)務(wù)使用中,方便注入一些

    2024年01月20日
    瀏覽(26)
  • Go Gin中間件

    Go Gin中間件

    Gin是一個(gè)用Go語言編寫的Web框架,它提供了一種簡(jiǎn)單的方式來創(chuàng)建HTTP路由和處理HTTP請(qǐng)求。中間件是Gin框架中的一個(gè)重要概念,它可以用來處理HTTP請(qǐng)求和響應(yīng),或者在處理請(qǐng)求之前和之后執(zhí)行一些操作。 以下是關(guān)于Gin中間件開發(fā)的一些基本信息: 中間件的定義 :在Gin中,中

    2024年02月05日
    瀏覽(28)
  • gin框架內(nèi)容(三)--中間件

    gin框架內(nèi)容(三)--中間件

    gin框架內(nèi)容(三)--中間件 Gin框架允許開發(fā)者在處理請(qǐng)求的過程中,加入用戶自己的函數(shù)。這個(gè)函數(shù)就叫中間件,中間件適合處理一些公共的業(yè)務(wù)邏輯,比 如登錄認(rèn)證、權(quán)限校驗(yàn)、數(shù)據(jù)分頁、記錄日志、耗時(shí)統(tǒng) 計(jì)等 即比如,如果訪問一個(gè)網(wǎng)頁的話,不管訪問什么路徑都需要

    2024年02月15日
    瀏覽(20)
  • GO——gin中間件和路由

    GO——gin中間件和路由

    中間件 參考:https://learnku.com/articles/66234 結(jié)構(gòu) 中間件是函數(shù) 中間件函數(shù)被放在調(diào)用鏈上 調(diào)用鏈的末尾是路由path對(duì)應(yīng)的函數(shù) 執(zhí)行過程 net/http包調(diào)用到gin的serverHTTP 參考:go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/gin.go:506 通過path找到路由對(duì)應(yīng)的處理鏈,賦值給context 參考:go/pkg/mod/git

    2024年01月17日
    瀏覽(22)
  • gin框架39--重構(gòu) BasicAuth 中間件

    gin框架39--重構(gòu) BasicAuth 中間件

    每當(dāng)我們打開一個(gè)網(wǎng)址的時(shí)候,會(huì)自動(dòng)彈出一個(gè)認(rèn)證界面,要求我們輸入用戶名和密碼,這種BasicAuth是最基礎(chǔ)、最常見的認(rèn)證方式,gin框架中提供了一種內(nèi)置的方式,但它只能用內(nèi)置的用戶和密碼,無法使用外部db中的用戶和密碼,這種方式很多時(shí)候是不友好的。 為此,本文

    2024年02月08日
    瀏覽(24)
  • gin 中間件流程控制:Next()、 Abort()

    gin 中間件流程控制:Next()、 Abort()

    源碼注釋: Next 應(yīng)該只在中間件內(nèi)部使用。它執(zhí)行調(diào)用處理程序內(nèi)部鏈中的掛起處理程序。 通俗的說,就是中間件放行,當(dāng)一個(gè)中間件代碼執(zhí)行到 Next() ,會(huì)先執(zhí)行它之后的函數(shù),最后再來執(zhí)行完本函數(shù)。 eg: 如果其中一個(gè)中間件響應(yīng)了c.Abort(),后續(xù)中間件將不再執(zhí)行,直接按

    2024年02月15日
    瀏覽(36)
  • Gin框架: 控制器, 中間件的分層設(shè)計(jì)案例

    對(duì)控制器的分組與繼承 1 )設(shè)計(jì)項(xiàng)目目錄結(jié)構(gòu) 2 )主程序 main.go 3 ) HTML模板目錄配置 tpls/web/index.html 4 ) routers 配置 4.1 webRouters.go 4.2 apiRouters.go 4.2 adminRouters.go 5 ) controller 配置 5.1 web/webCtrl.go 5.2 api/apiCtrl.go 5.3 admin/indexCtrl.go 5.4 admin/baseCtrl.go 5.4 admin/userCtrl.go 以上就是對(duì)控制器的一

    2024年02月21日
    瀏覽(24)
  • Golang中Gin 參數(shù)綁定和驗(yàn)證的中間件

    1. 學(xué)習(xí)在Golang中使用Gin參數(shù)綁定和驗(yàn)證的中間件,了解不同參數(shù)類型的綁定和驗(yàn)證方式。 Gin框架提供了很多常用的中間件,其中就包括參數(shù)綁定和驗(yàn)證的中間件。在使用Gin框架中進(jìn)行數(shù)據(jù)綁定和驗(yàn)證時(shí),可以使用Gin內(nèi)置的Binding、Validating和Uri中間件。 1. Binding Binding中間件用于

    2024年02月08日
    瀏覽(37)
  • gin自定義中間件解決requestBody不可重復(fù)讀問題

    gin自定義中間件解決requestBody不可重復(fù)讀問題

    先直接上代碼 注意,上面的中間件,需要在第一個(gè)執(zhí)行。 在gin中,context.Request.Body 是一個(gè)io.ReadCloser的接口,如下圖 查看io.ReadCloser接口定義 我們發(fā)現(xiàn)io.ReaderCloser接口的本質(zhì)就是 Read(p []byte) (n int, err error) 和 Close() error 的組合。 所以我們只需要自己編寫實(shí)現(xiàn) Read(p []byte) (n in

    2024年02月01日
    瀏覽(18)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包