從京東618秒殺聊聊秒殺限流的多種實現
【本文轉自微信公眾號爪哇筆記,作者:小柒2012】
前言
俗話說的好,冰凍三尺非一日之寒,滴水穿石非一日之功,羅馬也不是一天就建成的。兩週前秒殺案例初步成型,分享到了中國最大的同性交友網站-碼雲。同時也收到了不少小夥伴的建議和投訴。我從不認為分散式、叢集、秒殺這些就應該是大廠的專利,在網際網路的今天無論什麼時候都要時刻武裝自己,只有這樣,也許你的春天就在明天。
在開發秒殺系統案例的過程中,前面主要分享了佇列、快取、鎖和分散式鎖以及靜態化等等。快取的目的是為了提升系統訪問速度和增強系統的處理能力;分散式鎖解決了叢集下資料的安全一致性問題;靜態化無疑是減輕了快取以及DB層的壓力。
限流
然而再牛逼的機器,再最佳化的設計,對於特殊場景我們也是要特殊處理的。就拿秒殺來說,可能會有百萬級別的使用者進行搶購,而商品數量遠遠小於使用者數量。如果這些請求都進入佇列或者查詢快取,對於最終結果沒有任何意義,徒增後臺華麗的資料。對此,為了減少資源浪費,減輕後端壓力,我們還需要對秒殺進行限流,只需保障部分使用者服務正常即可。
就秒殺介面來說,當訪問頻率或者併發請求超過其承受範圍的時候,這時候我們就要考慮限流來保證介面的可用性,以防止非預期的請求對系統壓力過大而引起的系統癱瘓。通常的策略就是拒絕多餘的訪問,或者讓多餘的訪問排隊等待服務。
限流演算法
任何限流都不是漫無目的的,也不是一個開關就可以解決的問題,常用的限流演算法有:令牌桶,漏桶。
令牌桶
令牌桶演算法是網路流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一種演算法。典型情況下,令牌桶演算法用來控制傳送到網路上的資料的數目,並允許突發資料的傳送(百科)。
在秒殺活動中,使用者的請求速率是不固定的,這裡我們假定為10r/s,令牌按照5個每秒的速率放入令牌桶,桶中最多存放20個令牌。仔細想想,是不是總有那麼一部分請求被丟棄。
漏桶
漏桶演算法的主要目的是控制資料注入到網路的速率,平滑網路上的突發流量。漏桶演算法提供了一種機制,透過它,突發流量可以被整形以便為網路提供一個穩定的流量(百科)。
令牌桶是無論你流入速率多大,我都按照既定的速率去處理,如果桶滿則拒絕服務。
應用限流
Tomcat
在Tomcat容器中,我們可以透過自定義執行緒池,配置最大連線數,請求處理佇列等引數來達到限流的目的。
Tomcat預設使用自帶的連線池,這裡我們也可以自定義實現,開啟/conf/server.xml檔案,在Connector之前配置一個執行緒池:
name:共享執行緒池的名字。這是Connector為了共享執行緒池要引用的名字,該名字必須唯一。預設值:None;
namePrefix:在JVM上,每個執行執行緒都可以有一個name 字串。這一屬性為執行緒池中每個執行緒的name字串設定了一個字首,Tomcat將把執行緒號追加到這一字首的後面。預設值:tomcat-exec-;
maxThreads:該執行緒池可以容納的最大執行緒數。預設值:200;
maxIdleTime:在Tomcat關閉一個空閒執行緒之前,允許空閒執行緒持續的時間(以毫秒為單位)。只有當前活躍的執行緒數大於minSpareThread的值,才會關閉空閒執行緒。預設值:60000(一分鐘)。
minSpareThreads:Tomcat應該始終開啟的最小不活躍執行緒數。預設值:25。
配置Connector
executor:表示使用該引數值對應的執行緒池;
minProcessors:伺服器啟動時建立的處理請求的執行緒數;
maxProcessors:最大可以建立的處理請求的執行緒數;
acceptCount:指定當所有可以使用的處理請求的執行緒數都被使用時,可以放到處理佇列中的請求數,超過這個數的請求將不予處理。
API限流
秒殺活動中,介面的請求量會是平時的數百倍甚至數千倍,從而有可能導致介面不可用,並引發連鎖反應導致整個系統崩潰,甚至有可能會影響到其它服務。
那麼如何應對這種突然事件呢?這裡我們採用開源工具包guava提供的限流工具類RateLimiter進行API限流,該類基於"令牌桶演算法",開箱即用。
自定義定義註解
自定義切面
業務實現:
@Override@ServiceLimit@Transactionalpublic Result startSeckil(long seckillId, long userId) { //省略部分業務程式碼,詳見秒殺原始碼}
分散式限流
Nginx
如何使用Nginx實現基本的限流,比如單個IP限制每秒訪問50次。透過Nginx限流模組,我們可以設定一旦併發連線數超過我們的設定,將返回503錯誤給客戶端。
配置nginx.conf
配置說明
imitconnzone
是針對每個IP定義一個儲存session狀態的容器。這個示例中定義了一個100m的容器,按照32bytes/session,可以處理3200000個session。
limit_rate 300k;
對每個連線限速300k. 注意,這裡是對連線限速,而不是對IP限速。如果一個IP允許兩個併發連線,那麼這個IP就是限速limit_rate×2。
burst=5;
這相當於桶的大小,如果某個請求超過了系統處理速度,會被放入桶中,等待被處理。如果桶滿了,那麼抱歉,請求直接返回503,客戶端得到一個伺服器忙的響應。如果系統處理請求的速度比較慢,桶裡的請求也不能一直待在裡面,如果超過一定時間,也是會被直接退回,返回伺服器忙的響應。
OpenResty
背影有沒有很熟悉,對這就是那個直呼理解萬歲老羅,2015年老羅在錘子科技T2釋出會上將門票收入捐贈給了 OpenResty,也相信老羅是個有情懷的胖子。
這裡我們使用 OpenResty 開源的限流方案,測試案例使用OpenResty1.13.6.1最新版本,自帶lua-resty-limit-traffic模組以及案例 ,實現起來更為方便。
限制介面總併發數/請求數
秒殺活動中,由於突發流量暴增,有可能會影響整個系統的穩定性從而造成崩潰,這時候我們就要限制秒殺介面的總併發數/請求數。
這裡我們採用 lua-resty-limit-traffic中的resty.limit.count模組實現,由於文章篇幅具體程式碼參見原始碼openresty/lua/limit_count.lua。
限制介面時間窗請求數
秒殺場景下,有時候並都是人肉滑鼠,比如12306的搶票軟體,軟體刷票可比人肉滑鼠快多了。此時我們就要對客戶端單位時間內的請求數進行限制,以至於刷票不是那麼猖獗。當然了道高一尺魔高一丈,搶票軟體總是會有辦法繞開你的防線,從另一方面講也促進了技術的進步。
這裡我們採用 lua-resty-limit-traffic中的resty.limit.conn模組實現,具體程式碼參見原始碼openresty/lua/limit_conn.lua。
平滑限制介面請求數
之前的限流方式允許突發流量,也就是說瞬時流量都會被允許。突然流量如果不加以限制會影響整個系統的穩定性,因此在秒殺場景中需要對請求整形為平均速率處理,即20r/s。
這裡我們採用 lua-resty-limit-traffic 中的resty.limit.req 模組實現漏桶限流和令牌桶限流。
其實漏桶和令牌桶根本的區別就是,如何處理超過請求速率的請求。漏桶會把請求放入佇列中去等待均速處理,佇列滿則拒絕服務;令牌桶在桶容量允許的情況下直接處理這些突發請求。
漏桶
桶容量大於零,並且是延遲模式。如果桶沒滿,則進入請求佇列以固定速率等待處理,否則請求被拒絕。
令牌桶
桶容量大於零,並且是非延遲模式。如果桶中存在令牌,則允許突發流量,否則請求被拒絕。
壓測
為了測試以上配置效果,我們採用AB壓測,Linux下執行以下命令即可:
# 安裝yum -y install httpd-tools# 檢視ab版本ab -v# 檢視幫助ab --help
測試命令:
ab -n 1000 -c 100
測試結果:
總結
以上限流方案,只是針對此次秒殺案例做一個簡單的小結,大家也不要刻意區分那種方案的好壞,只要適合業務場景就是最好的。
參考
https://blog.52itstyle.com/archives/1764/
https://blog.52itstyle.com/archives/775/
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31137683/viewspace-2156907/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 同步秒殺實現:Redis在秒殺功能的實踐Redis
- 【Python秒殺指令碼】淘寶或京東等秒殺搶購Python指令碼
- 雲端計算秒殺電商秒殺
- 實現秒殺的幾個想法
- 秒殺多執行緒執行緒
- 【Redis核心知識】實現秒殺的三種方案Redis
- 秒殺最佳化-基於阻塞佇列實現秒殺最佳化佇列
- 短影片直播系統,實現高併發秒殺的多種方式
- RocketMQ實現優惠券秒殺MQ
- 高併發秒殺系統架構詳解,不是所有的秒殺都是秒殺!架構
- 什麼,秒殺系統也有這麼多種!
- 秒殺架構實踐架構
- 從構建分散式秒殺系統聊聊WebSocket推送通知分散式Web
- Android 實現GridView的橫向滾動,實現仿京東秒殺效果AndroidView
- Redis輕鬆實現秒殺系統Redis
- 秒殺流程圖流程圖
- 【高併發】秒殺系統架構解密,不是所有的秒殺都是秒殺(升級版)!!架構解密
- 從構建分散式秒殺系統聊聊執行緒池分散式執行緒
- Redis在秒殺功能的實踐Redis
- 基於redis分散式鎖實現“秒殺”Redis分散式
- 用Redis輕鬆實現秒殺系統Redis
- 用 Redis 輕鬆實現秒殺系統Redis
- PHP開發中多種方案實現高併發下的搶購、秒殺功能PHP
- 秒殺系統分析
- 萬物皆可秒——淘寶秒殺Python指令碼,掃貨618,備戰雙11!Python指令碼
- Redis秒殺實戰-微信搶紅包-秒殺庫存,附案例原始碼(Jmeter壓測)Redis原始碼JMeter
- 秒殺系統的設計
- 從構建分散式秒殺系統聊聊Disruptor高效能佇列分散式佇列
- 秒殺外掛的業務邏輯分析 秒殺外掛可以幫助您什麼?
- 秒殺系統設計
- Redis 實現高併發下的搶購 / 秒殺功能Redis
- js程式碼實現倒數計時秒殺的效果JS
- python版:單機redis實現秒殺,防止超限PythonRedis
- 使用redis watch實現秒殺搶購,避免超賣Redis
- 秒殺系統:如何打造並維護一個超大流量的秒殺系統?
- 秒殺架構模型設計架構模型
- 電商秒殺系統設計
- 微服務電商秒殺系統微服務