設定一個場景,假如一個商品接口在某段時間突然上升,會怎么辦?
生活中的例子來說,假設冰墩墩在當天晚上上熱搜之后,迅速有十幾萬人去淘寶下單購買,此時并沒有做好對該商品的緩存預熱以及準備,如何操作?
對于這個問題,在電商高并發系統中,對接口的保護一般采用:緩存、限流、降級 來操作。
假設該接口已經接受過風控的處理,過濾掉一半的機器人腳本請求,剩下都是人為的下單請求。
服務限流
限流 主要的目的是通過對并發訪問/請求進行限速,或者對一個時間窗口內的請求進行限速,一旦達到限制速率則可以拒絕服務、排隊或等待、降級等處理。
限流算法
1. 漏斗算法
漏桶算法 是當請求到達時直接放入漏桶,如果當前容量已達到上限(限流值),則進行丟棄或其他策略(觸發限流策略)。漏桶以固定的速率(根據服務吞吐量)進行釋放訪問請求(即請求通過),直到漏桶為空。
漏斗算法的思想就是,不管你來多少請求,我的接口消費速度一定是小于等于流出速率的閾值的。
可以基于消息隊列來實現。
2. 令牌桶算法
令牌桶算法 是程序以v(v = 時間周期 / 限流值)的速度向令牌桶中增加令牌,直到令牌桶滿,請求到達時向令牌桶請求令牌,如果獲取成功則通過請求,如果獲取失敗觸發限流策略。
令牌桶算法和漏斗算法的思想差別在于,前者可以允許突發請求的發生。
3. 滑窗算法
滑窗算法 是將一個時間周期分為N個小周期,分別記錄每個小周期內訪問次數,并且根據時間滑動刪除過期的小周期。
如下圖所示,假設時間周期為1分鐘,將1分鐘再分為2個小周期,統計每個小周期的訪問數量,則可以看到,第一個時間周期內,訪問數量為75,第二個時間周期內,訪問數量為100,如果一個時間周期內所有的小周期總和超過100的話,則會觸發限流策略。
Sentinel的實現 和 TCP滑窗。
接入層限流
Nginx限流
Nginx 限流采用的是漏桶算法。
它可以根據客戶端特征,限制其訪問頻率,客戶端特征主要指 IP、UserAgent等。使用 IP 比 UserAgent 更可靠,因為 IP 無法造假,UserAgent 可隨意偽造。
本地接口限流
Semaphore
Java 并發庫 的 Semaphore 可以很輕松完成信號量控制,Semaphore 可以控制某個資源可被同時訪問的個數,通過 acquire() 獲取一個許可,如果沒有就等待,而 release() 釋放一個許可。
假如我們對外提供一個服務接口,允許最大并發數為40,我們可以這樣:
privatefinalSemaphorepermit=newSemaphore(40,true); publicvoidprocess(){ try{ permit.acquire(); //TODO處理業務邏輯 }catch(InterruptedExceptione){ e.printStackTrace(); }finally{ permit.release(); } }
具體的 Semaphore 實現參考源碼。
分布式接口限流
使用消息隊列
不管是用MQ中間件,或是Redis的List實現的消息隊列,都可以作為一個 緩沖隊列 來使用。思想就是基于漏斗算法。
當對于一個接口請求達到一定閾值時,就可以啟用消息隊列來進行接口數據的緩沖,并根據服務的吞吐量來消費數據。
基于 Spring Boot + MyBatis Plus + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能
服務降級
在接口做好風控的前提下,發現了接口請求的并發量迅速上升,我們可以啟用兜底方案,進行服務降級。
一般服務降級應該用來對一些 不重要 或 不緊急 的服務或任務進行服務的 延遲使用 或 暫停使用。
降級方案
停止邊緣業務
比如淘寶雙11前,就不可以查詢三個月前的訂單,對邊緣業務進行降級,保證核心業務的高可用。
拒絕請求
在接口請求并發量大于閾值,或是接口出現大量失敗請求等等突發情況,可以拒絕一些訪問請求。
拒絕策略
隨機拒絕:隨機拒絕超過閾值的請求 。
拒絕舊請求:按照請求的時間,優先拒絕更早收到的請求。
拒絕非核心請求:根據系統業務設置核心請求清單,將非核心清單內的請求拒絕掉。
恢復方案
在實現服務降級之后,對于突增流量我們可以繼續注冊多個消費者服務來應對并發量,之后我們再對一些服務器進行慢加載。
降級具體實現參考其他文章。
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能
數據緩存
在接口做好風控的前提下,發現了接口請求的并發量迅速上升,我們可以分以下幾個操作執行:
對訪問請求使用分布式鎖進行阻塞。
在這個短時間中,我們可以將對應操作行的熱點數據,緩存在緩存中間件中。
放行請求后,讓所有請求優先操作緩存數據。
再將操作的結果通過消息隊列發送給消費接口慢慢消費。
緩存問題
假設我們操作的是一個庫存接口,此時數據庫中只有100個庫存。
那假如此時我們將一條數據放入緩存中,如果所有的請求都來訪問這個緩存,那它還是被打掛,我們該怎么操作?
讀寫分離
第一種想法,讀寫分離。
使用Redis的哨兵集群模式來進行主從復制的讀寫分離操作。讀的操作肯定大于寫操作,等庫存被消費到0時,讀操作直接快速失敗。
負載均衡
第二種想法,負載均衡。
在緩存數據后,如果所有請求都來緩存中操作這個庫存,不管是加悲觀鎖還是樂觀鎖,并發率都很低,此時我們可以對這個庫存進行拆分。
我們可以參照 ConcurrentHashMap 中的 counterCells 變量的設計思想,將100個庫存拆分到10個緩存服務中,每個緩存服務有10個緩存,然后我們再對請求進行負載均衡到各個緩存服務上。
但是這種方式會有問題,如果大部分用戶被hash到同一個緩存上,導致其他緩存沒有被消費,卻返回沒有庫存,這是不合理的。
page cache
第三種想法,page cache。
大部分軟件架構其實都用到了這種方法,比如linux內核的硬盤寫入、mysql的刷盤等等,即將短時間內的寫操作聚合結果寫入,所有的寫操作在緩存內完成。
審核編輯:湯梓紅
-
接口
+關注
關注
33文章
8728瀏覽量
152092 -
算法
+關注
關注
23文章
4633瀏覽量
93468
原文標題:海量請求下的接口并發解決方案
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
![](https://file1.elecfans.com/web2/M00/84/62/wKgaomRmBViARIRxAACMFNuLnfA833.png)
存儲器接口生成器(MIG)解決方案
基于U盤的單片機海量存儲方案
HDMI接口靜電保護解決方案
怎樣使用Redis + LUA腳本進行系統控制并發以防止無效請求呢
P2P流媒體系統中并發請求的數據分發算法
海思參展ICTC 2008并發布雙向多功能機頂盒芯片解決方案
海量小文件存儲的問題以及解決方案
一文匯總并發http請求最快的幾種實現方式用
![一文匯總<b class='flag-5'>并發</b>http<b class='flag-5'>請求</b>最快的幾種實現方式用](https://file.elecfans.com/web2/M00/18/A7/pYYBAGFvumyAPM3KAAA8fNdd6UE230.png)
解密高并發業務場景下典型的秒殺系統的架構
![解密高<b class='flag-5'>并發</b>業務場景<b class='flag-5'>下</b>典型的秒殺系統的架構](https://file.elecfans.com/web2/M00/1D/E1/pYYBAGGUbGuACheAAAAGR-ltEWo575.jpg)
效率加倍,高并發場景下的接口請求合并方案
所有接口都用post請求的原因
![所有<b class='flag-5'>接口</b>都用post<b class='flag-5'>請求</b>的原因](https://file1.elecfans.com/web2/M00/95/36/wKgaomTmu5-AJCNLAABGHV1p4ok179.png)
評論