摘要:關于如何限速,有兩個比較出名的算法,漏桶算法與令牌桶算法,這里對其簡單介紹一下,最后再實踐在我發(fā)郵件的中以下是發(fā)送郵件的,已限制為一分鐘兩次,你可以通過修改進行試驗。
前段時間,我使用了 jwt 來實現(xiàn)郵箱驗證碼的校驗與用戶認證與登錄,還特別寫了一篇文章作為總結。
在那篇文章中,提到了一個點,如何限速。
在短信驗證碼和郵箱驗證碼,如果不限速,被惡意攻擊造成大量的 QPS,不僅拖垮了服務,也會心疼如水的資費。鑒于君子固窮的原則,在我的郵箱服務里加上限速。
關于如何限速,有兩個比較出名的算法,漏桶算法與令牌桶算法,這里對其簡單介紹一下,最后再實踐在我發(fā)郵件的API中
以下是發(fā)送郵件的 API,已限制為一分鐘兩次,你可以通過修改 email 進行試驗。你也可以在我的站點直接試驗
curl "https://graphql.xiange.tech/graphql" -H "Content-Type: application/json" --data-binary "{"query":"mutation SEND($email: String!) { sendEmailVerifyCode (email: $email) }","variables":{"email":"xxxxxx@qq.com"}}"
以下是我關于登錄實踐的系列文章
【登錄那些事】實現(xiàn) Material Design 的登錄樣式
【登錄那些事】使用 jwt 登錄與校驗驗證碼
【登錄那些事】郵件發(fā)送,限流,漏桶與令牌桶
本文地址:https://shanyue.tech/post/rat...
Leaky Bucket (漏桶算法)漏桶算法表示水滴(請求)先進入到漏桶里,漏桶(bucket)以一定的速度出水,當漏桶中水滿時,無法再加水。
維護一個計數(shù)器作為 bucket,計數(shù)器的上限為 bucket 的大小
計數(shù)器滿時拒絕請求
每隔一段時間清空計數(shù)器
用 option 代表在 option.window 的窗口時間內最多可以通過 option.max 次請求
以下是使用 redis 的計數(shù)器實現(xiàn)限流的偽代碼
const option = { max: 10, // window 時間內限速10個請求 window: 1000 // 1s } function access(req) { // 根據請求生成唯一標志 const key = identity(req) // 計數(shù)器自增 const counter = redis.incr(key) if (counter === 1) { // 如果是當前時間窗口的第一個請求,設置過期時間 redis.expire(key, window) } if (counter > option.window) { return false } return true }
這里有 Redis 官方使用 INCR 實現(xiàn)限流的文檔 https://redis.io/commands/INCR
此時有一個不算問題的問題,就是它的時間窗口并不是滑動窗口那樣在桶里出去一個球,就可以再進來一個球。而更像是一個固定時間窗口,從桶里出去一群球,再開始進球。正因為如此,它可能在固定窗口的后一半時間收到 max-1 次請求,又在下一個固定窗口內打來 max 次請求,此時在一個隨機的窗口時間內最多會有 2 * max - 1 次請求。
另外還有一個redis的 INCR 與 EXPIRE 的原子性問題,容易造成 Race Condition,可以通過 SETNX 來解決
redis.set(key, 0, "EX", option.window, "NX")
另外也可以通過一個 LUA 腳本來搞定,顯然還是 SETNX 簡單些
local current current = redis.call("incr",KEYS[1]) if tonumber(current) == 1 then redis.call("expire",KEYS[1],1) end
為了解決 2N 的問題,可以由維護一個計數(shù)器,更改為維護一個隊列。代價是內存占用空間過高,且更難解決 Race Condition
以下是使用 redis 的 set/get string 實現(xiàn)的限流
const option = { max: 10, // window 時間內限速10個請求 window: 1000 // 1s } function access(req) { // 根據請求生成唯一標志 const key = identity(req) const current = Date.now() // cache 視為緩存對象 // 篩選出當前時間窗口的請求個數(shù),每個請求標志為時間戳的格式 // 為了簡單這里不做 json 的序列化和反序列化了... const timestamps = [current].concat(redis.get("timestamps")).filter(ts => ts + option.window > current) if (timestamps.length > option.max) { return false } // 此時讀寫不同步,會有 Race Condition 問題 redis.set("timestamps", timestamps, "EX", option.window) return true }
這里再使用一個 LUA 腳本解決 Race Condition 的問題
TODO
Token Bucket (令牌桶算法)由圖先看一看令牌桶與漏桶的不同
令牌桶初始狀態(tài) bucket 是滿的,漏桶初始狀態(tài) bucket 是空的
令牌桶在 bucket 空的時候拒絕新的請求,漏桶在 bucket 滿的時候拒絕新的請求
當一個請求來臨時,假設一個請求消耗一個token,令牌桶的 bucket 減少一個 token,漏桶增加一個 token
以下使用 redis 實現(xiàn)令牌桶
TODO
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://www.ezyhdfw.cn/yun/104139.html
摘要:接口限流的常用算法計數(shù)器法計數(shù)器法是限流算法里最簡單也是最容易實現(xiàn)的一種算法。由此可見,當滑動窗口的格子劃分的越多,那么滑動窗口的滾動就越平滑,限流的統(tǒng)計就會越精確。漏桶算法漏桶算法,又稱。 接口限流 什么是接口限流 那么什么是限流呢?顧名思義,限流就是限制流量,包括并發(fā)的流量和一定時間內的總流量,就像你寬帶包了1個G的流量,用完了就沒了,所以控制你的使用頻率和單次使用的總消耗。通過限...
摘要:計數(shù)限流算法無論固定窗口還是滑動窗口核心均是對請求進行計數(shù),區(qū)別僅僅在于對于計數(shù)時間區(qū)間的處理。令牌桶限流實現(xiàn)原理令牌桶限流的實現(xiàn)原理在有詳細說明。因此由此為入口進行分析。目前可返回的實現(xiàn)子類包括及兩種,具體不同下文詳細分析。 限流 限流一詞常用于計算機網絡之中,定義如下: In computer networks, rate limiting is used to control t...
摘要:令牌桶算法漏桶算法漏桶漏桶的出水速度是恒定的,那么意味著如果瞬時大流量的話,將有大部分請求被丟棄掉也就是所謂的溢出。 工作中對外提供的API 接口設計都要考慮限流,如果不考慮限流,會成系統(tǒng)的連鎖反應,輕者響應緩慢,重者系統(tǒng)宕機,整個業(yè)務線崩潰,如何應對這種情況呢,我們可以對請求進行引流或者直接拒絕等操作,保持系統(tǒng)的可用性和穩(wěn)定性,防止因流量暴增而導致的系統(tǒng)運行緩慢或宕機。 在開發(fā)高并發(fā)...
閱讀 2379·2021-11-23 09:51
閱讀 5852·2021-09-22 15:39
閱讀 3403·2021-09-02 15:15
閱讀 3556·2019-08-30 15:54
閱讀 2414·2019-08-30 15:53
閱讀 1456·2019-08-30 14:04
閱讀 2510·2019-08-29 18:33
閱讀 2477·2019-08-29 13:08