前言 首先,要明確一點,高併發場景下系統的瓶頸出現在哪裡,其實主要就是資料庫,那麼就要想辦法為資料庫做層層防護,減輕資料庫的壓力。 一、簡單圖示 我用一個比較簡單直觀的圖來表達大概的處理思路 二、生產環境中秒殺搶購的解決方案 1、前端 1)、動靜分離,將靜態資源放到第三方雲服務中進行CDN加速,減輕 ...
前言
首先,要明確一點,高併發場景下系統的瓶頸出現在哪裡,其實主要就是資料庫,那麼就要想辦法為資料庫做層層防護,減輕資料庫的壓力。
一、簡單圖示
我用一個比較簡單直觀的圖來表達大概的處理思路
二、生產環境中秒殺搶購的解決方案
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秒殺整體思路的概括,之後會分別對每一個階段的實現進行講解。