「面經」慌了,面試居然被問到怎麼做高並發系統的限流?

2020-12-21 酷扯兒

本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫

作者:nick haocnblogs.com/haoxinyue/p/6792309.html

開濤大神在博客中說過:在開發高並發系統時有三把利器用來保護系統:緩存、降級和限流。本文結合作者的一些經驗介紹限流的相關概念、算法和常規的實現方式。

緩存

緩存比較好理解,在大型高並發系統中,如果沒有緩存資料庫將分分鐘被爆,系統也會瞬間癱瘓。使用緩存不單單能夠提升系統訪問速度、提高並發訪問量,也是保護資料庫、保護系統的有效方式。大型網站一般主要是「讀」,緩存的使用很容易被想到。

在大型「寫」系統中,緩存也常常扮演者非常重要的角色。比如累積一些數據批量寫入,內存裡面的緩存隊列(生產消費),以及HBase寫數據的機制等等也都是通過緩存提升系統的吞吐量或者實現系統的保護措施。甚至消息中間件,你也可以認為是一種分布式的數據緩存。

降級

服務降級是當伺服器壓力劇增的情況下,根據當前業務情況及流量對一些服務和頁面有策略的降級,以此釋放伺服器資源以保證核心任務的正常運行。降級往往會指定不同的級別,面臨不同的異常等級執行不同的處理。根據服務方式:可以拒接服務,可以延遲服務,也有時候可以隨機服務。

根據服務範圍:可以砍掉某個功能,也可以砍掉某些模塊。總之服務降級需要根據不同的業務需求採用不同的降級策略。主要的目的就是服務雖然有損但是總比沒有好。

限流

限流可以認為服務降級的一種,限流就是限制系統的輸入和輸出流量已達到保護系統的目的。一般來說系統的吞吐量是可以被測算的,為了保證系統的穩定運行,一旦達到的需要限制的閾值,就需要限制流量並採取一些措施以完成限制流量的目的。

比如:延遲處理,拒絕處理,或者部分拒絕處理等等。

限流的算法

常見的限流算法有:計數器、漏桶和令牌桶算法。

計數器

計數器是最簡單粗暴的算法。比如某個服務最多只能每秒鐘處理100個請求。我們可以設置一個1秒鐘的滑動窗口,窗口中有10個格子,每個格子100毫秒,每100毫秒移動一次,每次移動都需要記錄當前服務請求的次數。

內存中需要保存10次的次數。可以用數據結構LinkedList來實現。格子每次移動的時候判斷一次,當前訪問次數和LinkedList中最後一個相差是否超過100,如果超過就需要限流了。

很明顯,當滑動窗口的格子劃分的越多,那麼滑動窗口的滾動就越平滑,限流的統計就會越精確。

示例代碼如下:

//服務訪問次數,可以放在Redis中,實現分布式系統的訪問計數Long counter = 0L;//使用LinkedList來記錄滑動窗口的10個格子。LinkedList<Long> ll = new LinkedList<Long>();public static void main(String[] args){Counter counter = new Counter(); counter.doCheck();}private void doCheck(){ while (true) { ll.addLast(counter); if (ll.size() > 10) { ll.removeFirst(); } //比較最後一個和第一個,兩者相差一秒 if ((ll.peekLast() - ll.peekFirst()) > 100) { //To limit rate } Thread.sleep(100); }}

漏桶算法

漏桶算法即leaky bucket是一種非常常用的限流算法,可以用來實現流量整形(Traffic Shaping)和流量控制(Traffic Policing)。貼了一張維基百科上示意圖幫助大家理解:

漏桶算法的主要概念如下:

一個固定容量的漏桶,按照常量固定速率流出水滴;如果桶是空的,則不需流出水滴;可以以任意速率流入水滴到漏桶;如果流入水滴超出了桶的容量,則流入的水滴溢出了(被丟棄),而漏桶容量是不變的。

漏桶算法比較好實現,在單機系統中可以使用隊列來實現(.Net中TPL DataFlow可以較好的處理類似的問題,你可以在這裡找到相關的介紹),在分布式環境中消息中間件或者Redis都是可選的方案。

令牌桶算法

令牌桶算法是一個存放固定容量令牌(token)的桶,按照固定速率往桶裡添加令牌。令牌桶算法基本可以用下面的幾個概念來描述:

令牌將按照固定的速率被放入令牌桶中。比如每秒放10個。桶中最多存放b個令牌,當桶滿時,新添加的令牌被丟棄或拒絕。當一個n個字節大小的數據包到達,將從桶中刪除n個令牌,接著數據包被發送到網絡上。如果桶中的令牌不足n個,則不會刪除令牌,且該數據包將被限流(要麼丟棄,要麼緩衝區等待)。

如下圖:

令牌算法是根據放令牌的速率去控制輸出的速率,也就是上圖的to network的速率。to network我們可以理解為消息的處理程序,執行某段業務或者調用某個RPC。

漏桶和令牌桶的比較

令牌桶可以在運行時控制和調整數據處理的速率,處理某時的突發流量。放令牌的頻率增加可以提升整體數據處理的速度,而通過每次獲取令牌的個數增加或者放慢令牌的發放速度和降低整體數據處理速度。而漏桶不行,因為它的流出速率是固定的,程序處理速度也是固定的。更多算法相關:算法聚合

整體而言,令牌桶算法更優,但是實現更為複雜一些。

限流算法實現

Guava

Guava是一個Google開源項目,包含了若干被Google的Java項目廣泛依賴的核心庫,其中的RateLimiter提供了令牌桶算法實現:平滑突發限流(SmoothBursty)和平滑預熱限流(SmoothWarmingUp)實現。

1. 常規速率:

創建一個限流器,設置每秒放置的令牌數:2個。返回的RateLimiter對象可以保證1秒內不會給超過2個令牌,並且是固定速率的放置。達到平滑輸出的效果

public void test(){/** * 創建一個限流器,設置每秒放置的令牌數:2個。速率是每秒可以2個的消息。 * 返回的RateLimiter對象可以保證1秒內不會給超過2個令牌,並且是固定速率的放置。達到平滑輸出的效果 */ RateLimiter r = RateLimiter.create(2); while (true) { /** * acquire()獲取一個令牌,並且返回這個獲取這個令牌所需要的時間。如果桶裡沒有令牌則等待,直到有令牌。 * acquire(N)可以獲取多個令牌。 */ System.out.println(r.acquire()); }}

上面代碼執行的結果如下圖,基本是0.5秒一個數據。拿到令牌後才能處理數據,達到輸出數據或者調用接口的平滑效果。acquire()的返回值是等待令牌的時間,如果需要對某些突發的流量進行處理的話,可以對這個返回值設置一個閾值,根據不同的情況進行處理,比如過期丟棄。

2. 突發流量:

突發流量可以是突發的多,也可以是突發的少。首先來看個突發多的例子。還是上面例子的流量,每秒2個數據令牌。如下代碼使用acquire方法,指定參數。

System.out.println(r.acquire(2));System.out.println(r.acquire(1));System.out.println(r.acquire(1));System.out.println(r.acquire(1));

得到如下類似的輸出。

如果要一次新處理更多的數據,則需要更多的令牌。代碼首先獲取2個令牌,那麼下一個令牌就不是0.5秒之後獲得了,還是1秒以後,之後又恢復常規速度。這是一個突發多的例子,如果是突發沒有流量,如下代碼:

System.out.println(r.acquire(1));Thread.sleep(2000);System.out.println(r.acquire(1));System.out.println(r.acquire(1));System.out.println(r.acquire(1));

得到如下類似的結果:

等了兩秒鐘之後,令牌桶裡面就積累了3個令牌,可以連續不花時間的獲取出來。處理突發其實也就是在單位時間內輸出恆定。這兩種方式都是使用的RateLimiter的子類SmoothBursty。另一個子類是SmoothWarmingUp,它提供的有一定緩衝的流量輸出方案。

/*** 創建一個限流器,設置每秒放置的令牌數:2個。速率是每秒可以210的消息。* 返回的RateLimiter對象可以保證1秒內不會給超過2個令牌,並且是固定速率的放置。達到平滑輸出的效果* 設置緩衝時間為3秒*/RateLimiter r = RateLimiter.create(2,3,TimeUnit.SECONDS);while (true) {/** * acquire()獲取一個令牌,並且返回這個獲取這個令牌所需要的時間。如果桶裡沒有令牌則等待,直到有令牌。 * acquire(N)可以獲取多個令牌。 */ System.out.println(r.acquire(1)); System.out.println(r.acquire(1)); System.out.println(r.acquire(1)); System.out.println(r.acquire(1));}

輸出結果如下圖,由於設置了緩衝的時間是3秒,令牌桶一開始並不會0.5秒給一個消息,而是形成一個平滑線性下降的坡度,頻率越來越高,在3秒鐘之內達到原本設置的頻率,以後就以固定的頻率輸出。

圖中紅線圈出來的3次累加起來正好是3秒左右。這種功能適合系統剛啟動需要一點時間來「熱身」的場景。

Nginx

對於Nginx接入層限流可以使用Nginx自帶了兩個模塊:

連接數限流模塊ngx_http_limit_conn_module漏桶算法實現的請求限流模塊ngx_http_limit_req_module

1. ngx_http_limit_conn_module

我們經常會遇到這種情況,伺服器流量異常,負載過大等等。對於大流量惡意的攻擊訪問,會帶來帶寬的浪費,伺服器壓力,影響業務,往往考慮對同一個ip的連接數,並發數進行限制。

ngx_http_limit_conn_module 模塊來實現該需求。該模塊可以根據定義的鍵來限制每個鍵值的連接數,如同一個IP來源的連接數。並不是所有的連接都會被該模塊計數,只有那些正在被處理的請求(這些請求的頭信息已被完全讀入)所在的連接才會被計數。

我們可以在nginx_conf的http{}中加上如下配置實現限制:

#限制每個用戶的並發連接數,取名onelimit_conn_zone $binary_remote_addr zone=one:10m;#配置記錄被限流後的日誌級別,默認error級別limit_conn_log_level error;#配置被限流後返回的狀態碼,默認返回503limit_conn_status 503;

然後在server{}裡加上如下代碼:

#限制用戶並發連接數為1limit_conn one 1;

然後我們是使用ab測試來模擬並發請求:

ab -n 5 -c 5 http://10.23.22.239/index.html

得到下面的結果,很明顯並發被限制住了,超過閾值的都顯示503:

另外剛才是配置針對單個IP的並發限制,還是可以針對域名進行並發限制,配置和客戶端IP類似。

#http{}段配置limit_conn_zone $ server_name zone=perserver:10m;#server{}段配置limit_conn perserver 1;

2. ngx_http_limit_req_module

上面我們使用到了ngx_http_limit_conn_module 模塊,來限制連接數。那麼請求數的限制該怎麼做呢?這就需要通過ngx_http_limit_req_module 模塊來實現,該模塊可以通過定義的鍵值來限制請求處理的頻率。

特別的,可以限制來自單個IP位址的請求處理頻率。限制的方法是使用了漏鬥算法,每秒固定處理請求數,推遲過多請求。如果請求的頻率超過了限制域配置的值,請求處理會被延遲或被丟棄,所以所有的請求都是以定義的頻率被處理的。

在http{}中配置

#區域名稱為one,大小為10m,平均處理的請求頻率不能超過每秒一次。limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

在server{}中配置

#設置每個IP桶的數量為5limit_req zone=one burst=5;

上面設置定義了每個IP的請求處理只能限制在每秒1個。並且服務端可以為每個IP緩存5個請求,如果操作了5個請求,請求就會被丟棄。

使用ab測試模擬客戶端連續訪問10次:

ab -n 10 -c 10 http://10.23.22.239/index.html

如下圖,設置了通的個數為5個。一共10個請求,第一個請求馬上被處理。第2-6個被存放在桶中。由於桶滿了,沒有設置nodelay因此,餘下的4個請求被丟棄。

相關焦點

  • 「面經分享」兩份Hors-Dap最新面試經驗請查收!
    2019年Hors-DAP直錄程序Etudes en France系統中表格確認提交的截止日期原定為2019年3月20日。法國高等教育署在官網上更新了2019年的時間表,把表格確認提交的截止日期延後到2019年5月5日。
  • 2021 阿里字節快手面經 & 個人成長經驗分享
    整體感受總體面下來沒有讓人緊張的感覺,中規中矩,從剛開始較高的心理預期到後面慢慢回落……由於我的水平有限,最終沒有爭取到多高的薪水。快手二面掛一面(60min)快速實現 [1, 2, ...100],所有你能想到的解es6 及 es6+ 的能力集,你最常用的,這其中最有用的,都解決了什麼問題。
  • 字節跳動三面offer到手,面試官都問了些啥?
    原標題:字節跳動三面offer到手,面試官都問了些啥?     前段時間,我一哥們去面試字節跳動,我聽他說過程艱難,但還是費了九牛二虎之力拿下了。     字節跳動的面試挺有挑戰性的感覺,不過還是挺有趣的,感覺啥技術都問。今天就跟大家說說字節跳動的面經。
  • 四面阿里定級P7,復盤一下面試過程,居然如此簡單!
    分布式系統的全局id如何實現?用zookeeper如何實現的呢,機器號+時間戳即可?分布式鎖的方案,redis和zookeeper那個好,如果是集群部署,高並發情況下哪個性能更好?kafka了解麼,了解哪些消息隊列?
  • 阿里曝光內部高並發實戰手冊,Github星標41K
    隨著科技的不斷發展,行業要求我們程式設計師這一群體在高性能、高並發的開發知識和技術這塊也要有所建樹,並且現在關於高並發的面試以前基本是BAT等大公司的專利,現在幾乎蔓延至與Java項目相關的整個行業,簡直抬升了Java工程師的學習臺階和面試門檻。那不學習了?送外賣了?必不可能!
  • 這份AI算法崗面經很乾貨:亞馬遜分享實戰經驗,履歷到面試全都有
    那麼問題來了:如果你是算法工程師,加入亞馬遜這樣的公司,會經歷一個怎樣的面試過程?最近這則詳實面經,獲得高贊,或許可能給你一些參考和借鑑。滿滿乾貨,建議先收藏後看~崗位要求首先,我們先來看看最刺激的——崗位要求。
  • 四面阿里接到Offere定級P7,復盤一下面試過程,居然如此簡單!
    分布式系統的全局id如何實現?用zookeeper如何實現的呢,機器號+時間戳即可?分布式鎖的方案,redis和zookeeper那個好,如果是集群部署,高並發情況下哪個性能更好?kafka了解麼,了解哪些消息隊列?樂觀鎖,悲觀鎖?
  • 分布式限流
    對於現在普遍的分布式應用,簡單開發了一個分布式限流的方案。為了模擬並發,在 User 應用中開啟了 10 個線程調用 Order(限流次數為5) 接口(也可使用專業的並發測試工具 JMeter 等)。既然要達到分布式全局限流的效果,那自然需要一個第三方組件來記錄請求的次數。其中 Redis 就非常適合這樣的場景。每次請求時將當前時間(精確到秒)作為 Key 寫入到 Redis 中,超時時間設置為 2 秒,Redis 將該 Key 的值進行自增。當達到閾值時返回錯誤。
  • 104 阿里業務型產品經理實習面經
    (ps:想看乾貨、不喜囉嗦的可以直接跳過看下面的面試問題環節)一直以來都很想寫一篇像模像樣的面經,總是因為自己沒有足夠的資歷和能力而作罷。前段時間,身邊好多朋友詢問我面試的經驗,本來不打算寫面經的我答應了他們要寫一篇面經分享給大家,希望能給那些像我一樣文科僧、沒技術、沒項目的童鞋一些啟發和幫助。
  • 「應聘面試通關指南」面試中常見問題匯總及應答
    本質上來說,你的目的不過就是「談到好的 Offer」,面試官想知道的不過就是你的性格,三觀,人生態度什麼的,所以,避開「太個性的」總結,「帶著技巧」的實事求是地去回答「1-2 自己的缺點」就行了。Example:比如針對「周杰倫新專輯」,假設我是半撇私塾的新媒體運營,因為公司的產品是新媒體運營培訓,所以我不能從娛樂角度切入,我想到的切入點是「周杰倫新專輯發布,新媒體運營可以從角度追熱點」,這樣一篇標題的文章。【四】如果是你,你會怎麼做?
  • 你知道現在的面試有多難嗎?不服來看這三道大廠面試題……
    01來自於阿里:「請尋求最優解,不要只是粗暴 wait()」有一個總任務 A,分解為子任務 A1 A2 A3 ...,任何一個子任務失敗後要快速取消所有任務,請寫程序模擬。02「請尋求最優解,不要簡單的synchronized」請用兩個線程交替輸出 A1B2C3D4...,A 線程輸出字母,B 線程輸出數字,要求 A 線程首先執行,B 線程其次執行!
  • 谷歌八年高級工程師親授面試經驗
    這份「谷歌面經」不獨適用於谷歌的軟體工程師位置,對提請其他商社的軟體工程師及另一個位置(如研究科學家)也有搭手。在白文中,谷歌大腦高級軟體工程研究員、變本加厲就學框架「多巴胺」(Dopamine)作者 Pablo Samuel Castro 大飽眼福了他謀取谷歌 offer 的經驗。這份「谷歌面經」不惟適用於谷歌的軟體工程師崗位,對提請其餘商社的軟體工程師及其他哨位(如切磋科學家)也有相助。艱難就業季,如何在谷歌負有一張一頭兒沉?
  • 架構師成長之路之限流漫談
    我們為什麼需要限流在上一篇架構師成長之路之服務治理漫談裡面,我們已經談到了高可用治理的部分。為了「反脆弱」,在微服務複雜拓撲的情況下,限流是保障服務彈性和拓撲健壯的重中之重。當然,也不是所有的系統都需要限流,這取決於架構師對於當前業務發展的預判。2. 我們常見的限流手段我們來列舉業內比較常見的一些限流手段。2.1 信號量計數信號量競爭是用來控制並發的一個常見手段。
  • 秒針超詳細面經(附答案)
    自我介紹答:自我介紹是面試中唯一的自己主動介紹自己的環節,一定要好好把握好,你數據結構學的號可以手撕一個紅黑樹你就說我數據結構掌握地很好,反正就是要把自己的優勢凸顯出來,比如我是保研的以及對於java的知識較熟悉,我介紹完自己的本科經歷以後,我就說我是保送到本校繼續讀研究生,然後最末尾會加上自己熟悉java,然後面試官就會問java的一些東西;
  • 分布式限流,你想知道的都在這裡
    前言在一個高並發系統中對流量的把控是非常重要的,當巨大的流量直接請求到我們的伺服器上沒多久就可能造成接口不可用,不處理的話甚至會造成整個應用不可用。對此就必須要做限流處理,每秒鐘生產一定限額的數據到kafka,這樣就能極大程度的保證web的正常運轉。其實不管處理何種場景,本質都是降低流量保證應用的高可用。
  • 安永、畢馬威、全國股份轉讓系統等名企筆面試經驗分享
    經過兩次面試拿到上海諮詢所offer:M面:線上面試2對1模式,簡單的自我介紹過後對實習經歷,項目經歷細緻提問,所以一定要回答的有理有據同時也要突出實習/項目的特點。 因為項目中有涉及到的計算機相關的技術,機器學習,輿情分析。。。所以M顯得很有興趣,後被問到SQL 各種join的區別還有如何理解風險諮詢業務的。
  • 2020年02月24日(周一)| 雅詩蘭黛、花旗銀行、波士頓諮詢等名企筆面試經驗
    > 【保險/金融租賃/金融資產管理公司通用資料及討論】【面經】2020上海保險交易所校招面經 【滙豐(HSBC)】渣打 CB BJ HR面經分享 【歐萊雅管理培訓生計劃(L』Oréal MT)】管培補招掛經(supply chain) 【中國人民銀行】央行面試95分群面tip分享 【瑞銀集團(UBS)】2020 summer Global markets SH AC面經
  • 面試網絡公司大廠都會被問到哪些問題
    提到找工作一定離不開面試,而大廠的面試對於有些人來說很容易,對於有些人來說卻很難,那麼如何才能得心應手地應對大廠面試呢?知己知彼,百戰不殆兵法有雲,知己知彼,才能從容獲勝。我們要想應對大廠的面試,首先應該知道的就是大廠面試會問哪些問題,我們對應準備才能得心應手。
  • 《面試攻略》別被對方問倒了!一篇文破解最常見的3大面試問題
    面試問題百百種!其中又以「為何離職」、「你的缺點是什麼」、「為何選擇跨產業、非原本職務」幾乎是面試官必問的三大挑戰,要如何回答才不會演變成誤踩禁忌的災難呢?一起跟小編來看看三大問題要如何化解!一、面試官:「你為什麼離開上一份工作?」