高并發下如何設計秒殺系統?
設計一個秒殺系統,需要考慮這些問題:

- 頁面靜态化
- 按鈕至灰控制
- 服務單一職責
- 秒殺連結加鹽
- 限流
- 分布式鎖
- MQ異步處理
- 限流&降級&熔斷
秒殺活動的頁面,大多數内容都是固定不變的,如商品名稱,商品圖檔等等,可以對活動頁面做靜态化處理,減少通路服務端的請求。秒殺使用者會分布在全國各地,有的在上海,有的在深圳,地域相差很遠,網速也各不相同。為了讓使用者最快通路到活動頁面,可以使用CDN(Content Delivery Network,内容分發網絡)。CDN可以讓使用者就近擷取所需内容。
秒殺活動開始前,按鈕一般需要置灰的。隻有時間到了,才能變得可以點選。這是防止,秒殺使用者在時間快到的前幾秒,瘋狂請求伺服器,然後秒殺時間點還沒到,伺服器就自己挂了。
我們都知道微服務設計思想,也就是把各個功能子產品拆分,功能那個類似的放一起,再用分布式的部署方式。
★如使用者登入相關的,就設計個使用者服務,訂單相關的就搞個訂單服務,再到禮物相關的就搞個禮物服務等等。那麼,秒殺相關的業務邏輯也可以放到一起,搞個秒殺服務,單獨給它搞個秒殺資料庫。”服務單一職責有個好處:如果秒殺沒抗住高并發的壓力,秒殺庫崩了,服務挂了,也不會影響到系統的其他服務。
連結如果明文暴露的話,會有人擷取到請求Url,提前秒殺了。是以,需要給秒殺連結加鹽。可以把URL動态化,如通過MD5加密算法加密随機的字元串去做url。
一般有兩種方式限流:nginx限流和redis限流。
- 為了防止某個使用者請求過于頻繁,我們可以對同一使用者限流;
- 為了防止黃牛模拟幾個使用者請求,我們可以對某個IP進行限流;
- 為了防止有人使用代理,每次請求都更換IP請求,我們可以對接口進行限流。
- 為了防止瞬時過大的流量壓垮系統,還可以使用阿裡的Sentinel、Hystrix元件進行限流。
可以使用redis分布式鎖解決超賣問題。使用Redis的SET EX PX NX + 校驗唯一随機值,再删除釋放鎖。
if(jedis.set(key_resource_id, uni_request_id, "NX", "EX", 100s) == 1){ //加鎖
try {
do something //業務處理
}catch(){
}
finally {
//判斷是不是目前線程加的鎖,是才釋放
if (uni_request_id.equals(jedis.get(key_resource_id))) {
jedis.del(lockKey); //釋放鎖
}
}
}
在這裡,判斷是不是目前線程加的鎖和釋放鎖不是一個原子操作。如果調用jedis.del()釋放鎖的時候,可能這把鎖已經不屬于目前用戶端,會解除他人加的鎖。為了更嚴謹,一般也是用lua腳本代替。lua腳本如下:
if redis.call('get',KEYS[1]) == ARGV[1] then
return redis.call('del',KEYS[1])
else
return 0
end;
如果瞬間流量特别大,可以使用消息隊列削峰,異步處理。使用者請求過來的時候,先放到消息隊列,再拿出來消費。
- 限流,就是限制請求,防止過大的請求壓垮伺服器;
- 降級,就是秒殺服務有問題了,就降級處理,不要影響别的服務;
- 熔斷,服務有問題就熔斷,一般熔斷降級是一起出現。