Redis高并发限流策略之漏斗限流算法
在雙11活動(dòng)當(dāng)天凌晨,打折活動(dòng)開始前多少名客戶下單可以半折甚至是免單優(yōu)惠,客戶當(dāng)然不會(huì)放過這個(gè)一年一次的機(jī)會(huì),瘋狂開始。這時(shí)候我們程序員小哥哥就苦了,稍一個(gè)不注意,服務(wù)器駕崩了,次日頭條見。那么為了防止在當(dāng)天凌晨壓死服務(wù)器的并發(fā),我們想到了一個(gè)很好的策略,一分鐘搞不定的事情,我們可以兩分鐘搞定,至少保證我們的服務(wù)器不會(huì)癱瘓,就是說,假如我們的服務(wù)器并發(fā)量是1w左右,那么我們可以限制在9000的并發(fā)量,超過這個(gè)預(yù)警我們就嚴(yán)格限制請求訪問量不能做越過這個(gè)警戒線,做到削峰或者說是平滑這個(gè)爆發(fā)點(diǎn)。
很快我們程序員小哥哥想到了兩個(gè)絕佳算法令牌桶和漏斗算法,關(guān)于這兩種算法,我們接下來就簡單介紹下。
令牌桶算法:是網(wǎng)絡(luò)流量整形和速率限制中最常使用的一種算法。典型情況下,令牌桶算法用來控制發(fā)送到網(wǎng)絡(luò)上的數(shù)據(jù)的數(shù)目,并允許突發(fā)數(shù)據(jù)的發(fā)送。
? ? ? a. 按特定的速率向令牌桶投放令牌
? ? b. 根據(jù)預(yù)設(shè)的匹配規(guī)則先對報(bào)文進(jìn)行分類,不符合匹配規(guī)則的報(bào)文不需要經(jīng)過令牌桶的處理,直接發(fā)送;
? ? c. 符合匹配規(guī)則的報(bào)文,則需要令牌桶進(jìn)行處理。當(dāng)桶中有足夠的令牌則報(bào)文可以被繼續(xù)發(fā)送下去,同時(shí)令牌桶中的令牌?量按報(bào)文的長度做相應(yīng)的減少;
? ? d. 當(dāng)令牌桶中的令牌不足時(shí),報(bào)文將不能被發(fā)送,只有等到桶中生成了新的令牌,報(bào)文才可以發(fā)送。這就可以限制報(bào)文的流量只能是小于等于令牌生成的速度,達(dá)到限制流量的目的。
漏斗算法:主要目的是控制數(shù)據(jù)注入到網(wǎng)絡(luò)的速率,平滑網(wǎng)絡(luò)上的突發(fā)流量。漏斗算法提供了一種機(jī)制,通過它,突發(fā)流量可以被整形以便為網(wǎng)絡(luò)提供一個(gè)穩(wěn)定的流量
令牌桶和漏斗對比:
兩者主要區(qū)別在于漏斗算法能夠強(qiáng)行限制數(shù)據(jù)的傳輸速率,而令牌桶算法在能夠限制數(shù)據(jù)的平均傳輸速率外,還允許某種程度的突發(fā)傳輸。在令牌桶算法中,只要令牌桶中存在令牌,那么就允許突發(fā)地傳輸數(shù)據(jù)直到達(dá)到用戶配置的門限,所以它適合于具有突發(fā)特性的流量。
Redis Cell的使用
簡單的介紹這兩種限流算法后,我們今天要來使用的是redis4.0提供的漏斗算法Redis Cell,redis只提供了一個(gè)命令cl.throttle
cl.throttle?user?15?30?60?1??????????????▲? ?▲??▲??▲?▲??????????????|???|??|??|?└───?apply?1?token??#每次申請一個(gè)token,默認(rèn)為1??????????????|???|??└──┴─────?30?tokens?/?60?seconds??#速率,60秒生成30個(gè)token??????????????|???└───────────?15?max_burst?????#默認(rèn)最大初始值??????????????└───────────────?key?"user"????#key值 >?cl.throttle?user?15?30?60?11) (integer) 0 # 0 表示允許,1表示拒絕2)?(integer)?15???#?漏斗總?cè)萘?)?(integer)?14???#?漏斗剩余空間4) (integer) -1 # 如果拒絕了,需要多長時(shí)間后再試(漏斗有空間了,單位秒)5)?(integer)?2?? #?表示多久后令牌桶中的令牌會(huì)存滿(單位秒)在執(zhí)行限流指令時(shí),如果被拒絕了,就需要丟棄或重試。cl.throttle指令考慮的非常周到,連重試時(shí)間都幫你算好了,直接取返回結(jié)果數(shù)組的第四個(gè)值進(jìn)行sleep即可,如果不想阻塞線程,也可以異步定時(shí)任務(wù)來重試。
實(shí)現(xiàn)漏斗算法
這里我們簡單的模擬一下漏斗算法
private static class Funnel { private int capacity; private float leakingRate; private int leftQuota; private long leakingTs;? public Funnel(int capacity, int count, int perSecond) { this.capacity = capacity; // 因?yàn)橛?jì)算使用毫秒為單位的 perSecond *= 1000; this.leakingRate = (float) count / perSecond; }? /** * 根據(jù)上次水流動(dòng)的時(shí)間,騰出已流出的空間 */ private void makeSpace() { long now = System.currentTimeMillis(); long time = now - leakingTs; int leaked = (int) (time * leakingRate); if (leaked < 1) { return; } leftQuota += leaked; // 如果剩余大于容量,則剩余等于容量 if (leftQuota > capacity) { leftQuota = capacity; } leakingTs = now; }? /**?????????*?漏斗漏水 * @param quota 流量 * @return 是否有足夠的水可以流出(是否允許訪問) */ public boolean watering(int quota) { makeSpace(); int left = leftQuota - quota; if (left >= 0) { leftQuota = left; return true; } return false; } }漏斗限速方法:
public class FunnelRateLimiter { private Map<String, Funnel> funnelMap = new ConcurrentHashMap<>(); /** * 根據(jù)給定的漏斗參數(shù)檢查是否允許訪問 * @param username 用戶名 * @param action 操作 * @param capacity 漏斗容量 * @param allowQuota 每單個(gè)單位時(shí)間允許的流量 * @param perSecond 單位時(shí)間(秒) * @return 是否允許訪問 */ public boolean isActionAllowed(String username, String action, int capacity, int allowQuota, int perSecond) { String key = "funnel:" + action + ":" + username; if (!funnelMap.containsKey(key)) { funnelMap.put(key, new Funnel(capacity, allowQuota, perSecond)); } Funnel funnel = funnelMap.get(key); return funnel.watering(1); }}測試:
public static void main(String[] args) throws InterruptedException { FunnelRateLimiter limiter = new FunnelRateLimiter(); int testAccessCount = 30; int capacity = 5; int allowQuota = 5; int perSecond = 30; int allowCount = 0; int denyCount = 0; for (int i = 0; i < testAccessCount; i++) {????????????boolean?isAllow?=?limiter.isActionAllowed("user",?"doSomething",?5,?5,?30); if (isAllow) { allowCount++; } else { denyCount++; } System.out.println("訪問權(quán)限:" + isAllow); Thread.sleep(1000); } System.out.println("報(bào)告:"); System.out.println("漏斗容量:" + capacity);????????System.out.println("漏斗流動(dòng)速率:"?+?allowQuota?+?"次/"?+?perSecond?+?"秒"); System.out.println("測試次數(shù)=" + testAccessCount); System.out.println("允許次數(shù)=" + allowCount); System.out.println("拒絕次數(shù)=" + denyCount); }?
一名正在搶救的coder
筆名:mangolove
CSDN地址:https://blog.csdn.net/mango_love
GitHub地址:https://github.com/mangoloveYu
?
總結(jié)
以上是生活随笔為你收集整理的Redis高并发限流策略之漏斗限流算法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阶段01-html和css基础(总结04
- 下一篇: 光寻址与电寻址空间光调制器的介绍