實戰案例:如何防止超預期的高併發流量壓垮系統?實戰接口限流防護!程式碼已上傳!

2025.05.12
在網路應用中,高並發系統會面臨一個重大的挑戰,那就是大量流高並發訪問,例如:天貓的雙十一、京東618、秒殺、搶購促銷等,這些都是典型的大流量高並發場景。

HTTP介面極限實戰
這裡,我們實作Web介面限流,具體方式為:使用自訂註解封裝基於令牌桶限流演算法實作介面限流。

不使用註解實現介面限流
搭建專案

這裡,我們使用SpringBoot項目來建置Http介面限流項目,SpringBoot專案本質上還是一個Maven專案。所以,小夥伴們可以直接建立一個Maven項目,我這裡的專案名稱為mykit-ratelimiter-test。接下來,在pom.xml檔案中加入如下依賴讓專案建構成一個SpringBoot專案。

可以看到,我在專案中除了引用了SpringBoot相關的Jar套件外,還引用了guava框架,版本為28.2-jre。

創建核心類
這裡,我主要是模擬一個支付介面的限流場景。首先,我們定義一個PayService介面和MessageService介面。 PayService介面主要用於模擬後續的支付業務,MessageService介面模擬發送訊息。介面的定義分別如下所示。

PayService

  • MessageService

接下來,創建二者的實作類,分別如下。

MessageServiceImpl

  • PayServiceImpl

由於是模擬支付和發送訊息,所以,我在具體實現的方法中列印出了相關的日誌,並沒有實現具體的業務邏輯。

接下來,就是創建我們的Controller類別PayController,在PayController類別的介面pay()方法中使用了限流,每秒鐘向桶中放入2個令牌,並且客戶端從桶中獲取令牌,如果在500毫秒內沒有獲取到令牌的話,我們可以則直接走服務降級處理。

PayController的程式碼如下所示。

至此,我們不使用註解方式實現限流的Web應用就基本完成了。

運行專案
專案創建完成後,我們來運行項目,運行SpringBoot項目比較簡單,直接運行MykitLimiterApplication類別的main()方法即可。

專案運行成功後,我們在瀏覽器網址列輸入連結:http://localhost:8080/boot/pay。頁面會輸出「支付成功」的字樣,表示專案搭建成功了。如下圖所示。

此時,我只訪問了一次,並沒有觸發限流。接下來,我們不停的刷瀏覽器,此時,瀏覽器會輸出「支付失敗,再試一次吧...」的字樣,如下所示。

在PayController類別中還有一個sendMessage()方法,模擬的是發送訊息的接口,同樣使用了限流操作,具體程式碼如下所示。

sendMessage()方法的程式碼邏輯和運作效果與pay()方法相同,我就不再瀏覽器造訪 http://localhost:8080/boot/send/message 位址的存取效果了,小夥伴們可以自行驗證。

不使用註解實現限流缺點
透過專案的編寫,我們可以發現,當在專案中對介面進行限流時,不使用註解進行開發,會導致程式碼出現大量冗餘,每個方法中幾乎都要寫一段相同的限流邏輯,程式碼十分冗餘。

如何解決程式碼冗餘的問題呢?我們可以使用自訂註解進行實作。

使用註解實現介面限流
使用自訂註解,我們可以將一些通用的業務邏輯封裝到註解的切面中,在需要加入註解業務邏輯的方法上加上對應的註解即可。針對我們這個限流的實例來說,可以基於自訂註解實作。

實現自訂註解
實現,我們來創建一個自訂註解,如下所示。

自訂註解切面實現
接下來,我們也要實作一個切面類別MyRateLimiterAspect,如下所示。

接下來,我們改造下PayController類別中的sendMessage()方法,修改後的方法片段程式碼如下所示。

運行部署項目
部署專案比較簡單,只需要執行MykitLimiterApplication類別下的main()方法即可。這裡,為了簡單,我們還是從瀏覽器直接輸入連結位址來進行存取。

效果如下圖所示。

接下來,我們不斷的刷新瀏覽器。會出現「訊息傳送失敗,再試一次吧..」的字樣,說明已經觸發限流操作。

基於限流演算法實現限流的缺點
上面介紹的限流方式都只能用於單機部署的環境中,如果將應用部署到多台伺服器進行分佈式、集群,則上面限流的方式就不適用了,此時,我們需要使用分佈式限流。至於在分散式場景下,如何實現限流操作,我們就在下一篇介紹。