類saas的多租戶系統(tǒng)限流方案

一、問題起因

前幾天在面試的時候,因為我以前有個B2B訂貨平臺(saas系統(tǒng)架構(gòu)环鲤,平臺給每個租戶,提供完全獨立的在線商城服務(wù)憎兽,而所有的商城實際上還是在同一個系統(tǒng)中)的項目經(jīng)驗冷离,所以面試官問到了這個問題结闸,當(dāng)某個租戶的流量特別大,怎么保證其他租戶的訪問不受限制酒朵?

二桦锄、一般系統(tǒng)常見的限流方案

1.限制并發(fā)數(shù)

假設(shè)系統(tǒng)瞬時可接受的并發(fā)數(shù)為1000,那么每來一個請求將該數(shù)值減1蔫耽,請求執(zhí)行完成后將該數(shù)值加1结耀,當(dāng)該數(shù)值小于等于0時拒絕訪問。該方法實現(xiàn)非常簡單粗暴匙铡,java應(yīng)用中可以通過java.util.corrurnent包下的Semaphore信號量的tryAcquire和release操作來實現(xiàn)图甜。
該方法其實常見于一些長連接的限制上,比如db連接鳖眼。對于執(zhí)行時間較短黑毅,波動較大的請求,并不能很公平的限制流量钦讳,因為每個請求執(zhí)行時間不一樣矿瘦,甚至不同系統(tǒng)負(fù)載下的同一個請求執(zhí)行時間也不一樣,如果都只獲取一個并發(fā)數(shù)并不是一個很優(yōu)的方案愿卒。(這個也是我當(dāng)時面試的時候給面試官的方案缚去,給每個租戶一個固定并發(fā)數(shù)來限制,現(xiàn)在想來其實還有些欠妥)

2.漏桶(Leaky Bucket)算法

漏桶算法

初始一個定長的漏桶琼开,將請求任務(wù)放到漏桶中易结,以固定的流出速率去執(zhí)行請求,如果桶滿了就丟棄柜候。實現(xiàn)方式搞动,事先準(zhǔn)備一個定長隊列,存放請求任務(wù)渣刷,準(zhǔn)備一個任務(wù)執(zhí)行線程池固定時間間隔取任務(wù)來執(zhí)行即可鹦肿,隊列滿了就丟棄。
該方法類似于一些消息隊列如rocketmq和kafka的消息消費方式(consumer定速率的從broker上pull消息)飞主,這種方法嚴(yán)格限制了系統(tǒng)的執(zhí)行速率狮惜,起到限流的作用高诺,但對于一些可接受范圍內(nèi)突發(fā)的大流量請求也會被限制碌识,導(dǎo)致部分請求延遲過大。

3.令牌桶(Token Bucket)算法

令牌桶算法

初始一個定長的桶虱而,以固定的速率往桶內(nèi)放令牌筏餐,溢出的令牌丟棄,每個請求可以獲得任意個數(shù)令牌牡拇,如果取到了足夠令牌就可以繼續(xù)執(zhí)行魁瞪,如果取不到就拒絕該請求穆律。Google的Guava包中的RateLimiter即是以該算法實現(xiàn),實現(xiàn)比較簡單导俘,有興趣的小伙伴可以自己去研究源碼峦耘。
該方法算是第一種限制并發(fā)數(shù)的改進(jìn)版,取得令牌數(shù)是可以動態(tài)改變的旅薄,釋放令牌的數(shù)量也不會受限于每個請求的執(zhí)行時間辅髓,而且可以應(yīng)對可接受范圍內(nèi)的突發(fā)流量,應(yīng)該也是目前最常用的方式少梁。

三洛口、針對多租戶系統(tǒng)的限流方案(僅個人想法)

1.事先分配

事先為每個租戶分配獨立的限制量,當(dāng)然可以簡單的平均分配凯沪,也可以根據(jù)租戶事先定制的值分配第焰。這種方法應(yīng)該是最容易想到,根據(jù)總的租戶個數(shù)平均分配限制量后妨马,再通過對多租戶的身份識別分別做流量控制挺举,前面提到的三種算法應(yīng)該都比較容易通過改進(jìn)來實現(xiàn)。
缺點:不夠靈活烘跺,不活躍的租戶的量其實是可以暫時分配或者是部分分配給其他租戶的豹悬。

2.私有+公有令牌桶

①基于令牌桶算法的改進(jìn),每個租戶都有一個私有的令牌桶液荸,所有租戶有個公有的令牌桶瞻佛,租戶先從公有令牌桶取令牌,取不到再去私有令牌桶取娇钱,每個租戶有各自獨立的放令牌速率伤柄,先放到私有桶中,溢出的部分再放到公有桶中文搂,公有桶溢出后就丟棄适刀。這種方式可以保證每個租戶都有一個可以得到保障的最低流量,而且還可以將系統(tǒng)資源得到充分的利用煤蹭。但是這種方案使用時卻并不那么美好笔喉,因為放令牌操作要遍歷每個私有桶,他的時間復(fù)雜度是O(n)硝皂,相比較原來O(1)的時間復(fù)雜度常挚,尤其在海量租戶的情況下,嚴(yán)重影響系統(tǒng)效率稽物。
偽代碼:

long timeStamp=getNowTime();
int publicCapacity;              // 公有桶的容量
int privateCapacity;            //私有桶的容量
Map rateMap ;              //每個租戶令牌放入速度
int publicTokens;            //公有桶的當(dāng)前水量
Map privateTokensMap;       //私有桶的當(dāng)前水量

bool grant(int tenantId, int grantTokens){ //取令牌方法
    //先執(zhí)行添加令牌的操作
    putTokens();
    //先從公有桶取奄毡,再從私有桶取
    int privateTokens = privateTokensMap.get(tenantId);
    if(privateTokens+publicTokens >= grantTokens){
        int tokensDel = publicTokens - grantTokens;
        if(tokensDel >= 0){
            //完全從公有桶取
            publicTokens = tokensDel
        }else{
            //共有桶不夠,從私有桶補(bǔ)足
            publicTokens = 0;
            privateTokensMap.put(tenantId,privateTokens+tokensDel);
        }
        return true;
    }else{
        //令牌不夠贝或,拒絕請求
        retun false;
    }
}
void putTokens(){
    long now = getNowTime();
    long timeDelta = now - timeStamp;
    timeStamp = now;
    for(Map.Entry rateEntry : rateMap){//遍歷每個桶吼过,將令牌放入锐秦,私有桶溢出的令牌放到公有桶,公有桶溢出的丟棄
        int tenantId = rateEntry.getKey();
        int rate = rateEntry.getValue();
        int privateTokens = privateTokensMap.get(tenantId);
        int tokensDelta = timeDelta*rate;
        if(privateTokens + tokenDelta > privateCapacity){
            rateEntry.setValue(privateCapacity);
            publicTokens = min(publicCapacity, publicTokens+(privateTokens + tokenDelta - privateCapacity));
        }else{
            rateEntry.setValue(privateTokens + tokenDelta);
        }
    }
}

②在上一種方案的基礎(chǔ)下盗忱,公有桶也作為一個獨立的令牌桶使用酱床,公有桶的令牌流入速率與私有桶不同,每個租戶先從公有桶嘗試獲取趟佃,獲取不到的情況下斤葱,再從私有桶獲取,但是每個桶(包括公有桶)的速率總和還是要小于等于系統(tǒng)可接受的最大流量揖闸。這樣的時間復(fù)雜度依然是常數(shù)級別的揍堕,也可以提高部分閑置資源的使用率。借助現(xiàn)有的限流工具也很容易實現(xiàn)汤纸。
偽代碼:

Map<RateLimiter> privateRateLimiterMap;    //租戶的私有令牌桶衩茸,RateLimiter是普通的令牌桶實現(xiàn),例如:Guava包里的令牌桶RateLimiter
RateLimiter publicRateLimiter;   //公有的令牌桶

void init(Map privateRateMap, int publicRate){ //初始化令牌桶
    publicRateLimiter = RateLimiter.create(publicRate);
    privateRateLimiterMap = new HashMap<>();
    for(Map.Entry rateEntry : privateRateMap){
        RateLimiter privateRateLimiter = RateLimiter.create(rateEntry.getValue());
        privateRateLimiterMap.put(rateEntry.getKey(), privateRateLimiter);
    }
}

bool grant(int tenantId, int grantTokens){ //取令牌方法
    if(publicRateLimiter.tryAcquire(grantTokens))
        return true;
    else
        return privateRateLimiterMap.get(tenantId).tryAcquire(grantTokens);
}

歡迎來我的個人博客逛逛: https://blog.52xtg.com

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贮泞,一起剝皮案震驚了整個濱河市楞慈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌啃擦,老刑警劉巖囊蓝,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異令蛉,居然都是意外死亡聚霜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門珠叔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蝎宇,“玉大人,你說我怎么就攤上這事祷安±呀妫” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵汇鞭,是天一觀的道長凉唐。 經(jīng)常有香客問我,道長霍骄,這世上最難降的妖魔是什么台囱? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮腕巡,結(jié)果婚禮上玄坦,老公的妹妹穿的比我還像新娘。我一直安慰自己绘沉,他們只是感情好煎楣,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著车伞,像睡著了一般择懂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上另玖,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天困曙,我揣著相機(jī)與錄音,去河邊找鬼谦去。 笑死慷丽,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鳄哭。 我是一名探鬼主播要糊,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼妆丘!你這毒婦竟也來了锄俄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤勺拣,失蹤者是張志新(化名)和其女友劉穎奶赠,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體药有,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡毅戈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了愤惰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片竹祷。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖羊苟,靈堂內(nèi)的尸體忽然破棺而出塑陵,到底是詐尸還是另有隱情,我是刑警寧澤蜡励,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布令花,位于F島的核電站,受9級特大地震影響凉倚,放射性物質(zhì)發(fā)生泄漏兼都。R本人自食惡果不足惜躯畴,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一后控、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧堤瘤,春花似錦、人聲如沸慎王。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赖淤。三九已至蜀漆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咱旱,已是汗流浹背确丢。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留吐限,地道東北人鲜侥。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像诸典,于是被迫代替她去往敵國和親描函。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容