亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

分布式出站 http 速率限制器

分布式出站 http 速率限制器

Go
HUWWW 2023-06-26 17:48:51
我有一個微服務架構應用程序,其中多個服務輪詢外部 API。外部 API 的速率限制為每分鐘 600 個請求。如何讓所有實例一起保持在共享 600 速率限制以下?Google只給我帶來了3個解決方案,最有希望的是:myntra/golimit是這三個中最有前途的,但我實際上不知道如何設置它。wallstreetcn/rate似乎僅在達到限制時才拒絕(我的應用程序需要等到它可以發出請求)并且funcEvery中的函數rate.NewLimiter似乎是不同的導入/依賴項,我無法弄清楚它是什么manavo/go-rate-limiter有一個&ldquo;軟&rdquo;限制,顯然,這可能會讓我超過限制。有些端點如果幾秒鐘內無法訪問我并不介意,但其他端點請求應該盡可能有效。目前我有一個業余解決方案。下面的代碼允許我設置每分鐘的限制,并且它在請求之間休眠以將請求分散到一分鐘內。此客戶端速率限制是針對每個實例的,因此我必須對 600 個請求除以實例數量進行硬編碼。var semaphore = make(chan struct{}, 5)var rate = make(chan struct{}, 10)func init(){? ? // leaky bucket? ? go func() {? ? ? ? ticker := time.NewTicker(100 * time.Millisecond)? ? ? ? defer ticker.Stop()? ? ? ? for range ticker.C {? ? ? ? ? ? _, ok := <-rate? ? ? ? ? ? // if this isn't going to run indefinitely, signal? ? ? ? ? ? // this to return by closing the rate channel.? ? ? ? ? ? if !ok {? ? ? ? ? ? ? ? return? ? ? ? ? ? }? ? ? ? }}()以及發出 http API 請求的函數內部。rate <- struct{}{}? ? // check the concurrency semaphore? ? semaphore <- struct{}{}? ? defer func() {? ? ? ? <-semaphore}()如何讓所有實例一起保持在共享 600 速率限制以下?首選項: - 基于密鑰的速率限制計數器,因此可以設置多個計數器。- 在設定的持續時間內分散請求,以便在前 30 秒內而不是在整分鐘持續時間內發送 600 個請求。
查看完整描述

2 回答

?
拉風的咖菲貓

TA貢獻1995條經驗 獲得超2個贊

我無法與您找到的庫交談,但漏桶速率限制器非常簡單。您需要某種共享事務存儲。每個存儲桶(或速率限制器)只是一個整數和一個時間值。該整數是特定時間桶中的滴數。每次必須應用速率限制時,減去自上次更新以來泄漏的滴數,然后加一,然后檢查滴數是否在桶的容量范圍內。

我們正在使用 Redis 來完成這類事情。要在 Redis 中實現此事務性,需要一個腳本(。例如,在 SQL 數據庫中,aSELECT FOR UPDATE后跟一條語句可以實現相同的效果。UPDATE這是我們的 Redis 腳本:

-- replicate_commands allows us to use the TIME command. We depend on accurate

-- (and reasonably consistent) timestamps. Multiple clients may have

-- inacceptable clock drift.

redis.replicate_commands()


local rate = tonumber(ARGV[1]) -- how many drops leak away in one second

local cap = tonumber(ARGV[2]) -- how many drops fit in the bucket

local now, _ = unpack(redis.call('TIME'))


-- A bucket is represented by a hash with two keys, n and t. n is the number of

-- drops in the bucket at time t (seconds since epoch).

local xs = redis.call('HMGET', KEYS[1], 'n', 't')

local n = tonumber(xs[1])

local t = tonumber(xs[2])


if type(n) ~= "number" or type(t) ~= "number" then

? ? -- The bucket doesn't exist yet (n and t are false), or someone messed with

? ? -- our hash values. Either way, pretend the bucket is empty.

? ? n, t = 0, now

end


-- remove drops that leaked since t

n = n - (now-t)*rate

if n < 0 then

? ? n = 0

end


-- add one drop if it fits

if n < cap then

? ? n = n + 1

else

? ? n = cap

end


redis.call('HMSET', KEYS[1], 'n', n, 't', now)

redis.call('EXPIRE', KEYS[1], math.floor(n/rate) + 1)


return n

每秒 10 滴的調用示例,容量為 10 滴:


EVALSHA <SHA_IN_HEX> 1 rate-limit:my-bucket 10 10?

該腳本返回桶中的滴數。如果該數字等于容量,您可以短暫休眠并重試,或者完全拒絕該請求,具體取決于您的要求。


請注意,腳本永遠不會返回大于容量的值,因此在您的情況下恢復時間不會超過十分之一秒。這可能不是您所需要的,因為您正在嘗試匹配第三方速率限制器。也就是說,您可能可以接受桶溢出,從而導致突發請求后恢復時間更長。


查看完整回答
反對 回復 2023-06-26
?
慕森王

TA貢獻1777條經驗 獲得超3個贊

如果你想要一個全局速率限制器,你需要一個地方來維護分布式狀態,比如zookeeper。通常,我們不想支付管理費用?;蛘?,您可以設置轉發代理,在其中進行速率限制。



查看完整回答
反對 回復 2023-06-26
  • 2 回答
  • 0 關注
  • 225 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號