前言
首先,要明確一點,高並發場景下系統的瓶頸出現在哪里,其實主要就是數據庫,那么就要想辦法為數據庫做層層防護,減輕數據庫的壓力。
一、簡單圖示
我用一個比較簡單直觀的圖來表達大概的處理思路
二、生產環境中秒殺搶購的解決方案
####1、前端 #####1)、動靜分離,將靜態資源放到第三方雲服務中進行CDN加速,減輕秒殺時的帶寬壓力,比如阿里雲、七牛雲等等。 實踐證明,CDN加速的效果十分明顯,對於一些響應不是很快的網站而言,靜態資源做了CDN加速后會變得很快,前后響應速度截然不同,是生產中必不可少的一種方式。
#####2)、點擊秒殺按鈕后,記得將按鈕禁用。 主要是為了防止重復點擊提交
#####3)、使用驗證碼惡意防刷 類似於斗魚等直播平台搶禮物的場景,你幾乎每次在最后一秒點擊的時候都會彈出比較復雜的圖形驗證碼,感官上好像是耽誤了你一兩秒的時間,實際上這種簡單的方式不僅分散了流量,而且防止有惡意刷秒殺接口的行為,十分好用。
#####4)、秒殺詳情頁的頁面端,使用定時器查詢秒殺結果。 這是秒殺場景下必不可少的一件事,判斷是否秒殺到就是在前端通過一個定時器不斷輪詢服務端接口,查詢秒殺結果最終返回是成功或失敗。
#####5)、商品的詳情頁可以使用頁面靜態化技術提高響應速度 有兩種方式,一種是使用nginx對頁面進行緩存配置,一種是直接利用瀏覽器端緩存,兩種差不多,相比之下后一種其實更科學。
2、網關
網關一般在微服務中用來做認證鑒權以及限流操作,這里在秒殺場景中就是使用限流算法,對用戶秒殺請求實現限流和服務保護。
限流算法有很多,比如redis限流、nginx、hystrix等等,實際工作中使用最多的還是令牌桶算法,可以基於這個算法自己寫一個注解,也可以使用Google工具類已經實現的RateLimter,兩三行就能實現效果。
3、服務端
服務端主要就是對秒殺接口的優化
1)、服務端模板技術進行頁面靜態化,一般針對詳情頁,使用freemarker、thymeleaf、velocity等模板技術,適用於訪問量較大的頁面,頁面又不會頻繁改變的場景。一般要設置緩存過期時間,給它一個較短的緩存期,比如60秒;
2)、服務端對象緩存,一般針對商品列表,使用redis,有分頁的緩存個1-3頁就OK了,一般用戶也就點個幾頁就不點了;
3)、秒殺接口的熔斷降級,主要是對接口進行保護;
4)、token令牌方式處理秒殺請求,結合redis,將和庫存數量一致的token令牌放入redis(這個放令牌的操作是在后台完成的),每一個令牌都承接一個秒殺請求,請求獲取到令牌就返回秒殺成功,沒獲取到就返回秒殺失敗,這樣就防止大量請求來訪問接口並且對數據庫進行操作,很大程度上提高了性能和接口響應速度;
5)、對於秒殺成功的請求,需要修改庫存,那么就要對數據庫進行操作,可以結合MQ異步修改庫存,降低高並發場景下對數據庫帶來的壓力;
6)、編寫一個返回秒殺結果的接口,對應前面寫的前端定時器輪詢查詢秒殺結果這部分。
4、服務器優化
進行服務器集群即可,比如nginx+lvs,分擔服務器承載請求的壓力,同時也是分散流量最傳統的一種方式。
5、超賣問題
主要兩種方式:
1)、一種是使用數據庫自帶的行鎖機制,這種方式我在工作中用過,完全可以解決超賣問題,對於流量不大的秒殺場景實際上完全夠用,性能消耗也不明顯;
2)、另一種是現在比較流行的version版本號實現的樂觀鎖機制,就是在數據庫中加一個version字段來表示版本號,修改庫存時先獲取當前版本號,然后修改時就傳入該版本號並且對當前版本號+1,這種相較於第一種就更科學,在性能上更優越,而且對於流量較大的秒殺場景而言容錯率更高,這個后面會講。
6、壓測工具的使用
可以使用Apache的jmeter壓測工具,操作簡單,在測試秒殺接口時可根據測試結果不斷優化,還能生成測試結果的報表。
總結
以上是對java秒殺整體思路的概括,之后會分別對每一個階段的實現進行講解。