消息隊列堆積太多,下游處理不過來怎么辦呢?
作為后端程序員日常工作中難免會遇到要跟消息隊列打交道的時候,而且在當(dāng)下微服務(wù)的場景下,很多服務(wù)的性能不是我們自己能控制的。
這不阿粉最近就遇到了一個場景,由于上游服務(wù)流量增加,發(fā)送到消息隊列的消息增多,阿粉在處理消息的時候需要依賴下游的一個服務(wù),可是誰想到下游的服務(wù)效率太差,消息太多處理不過來,CPU 居高不下。
看過我們昨天文章的小伙伴應(yīng)該都知道,這個時候我們就需要進行限流了,為了避免將下游的服務(wù)打垮,我們來進行單機限流操作。這里我們來模擬一下操作過程,首先我們通過一段偽代碼來模擬大流量,然后通過配置 sentinel 的控制臺來配置規(guī)則從而實現(xiàn)單機 QPS 20 的限制。
創(chuàng)建 SpringBoot 服務(wù)
首先我們創(chuàng)建一個 SpringBoot 服務(wù),在 pom.xml 文件中增加下面的配置。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.4</version>
</dependency>
然后我們提供一個對外的 http 接口,通過訪問接口來觸發(fā)我們的限流代碼,接口代碼如下:
代碼如下:
package com.example.demo.controller;
import com.alibaba.csp.sentinel.SphO;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <br>
* <b>Function:</b><br>
* <b>Author:</b>@author ziyou<br>
* <b>Date:</b>2022-05-08 12:56<br>
* <b>Desc:</b>無<br>
*/
@RestController
public class LoginController {
@GetMapping(value = "/login")
public void login(String username, String password) {
System.out.println("login");
//模擬一百萬條消息
for (int i = 0; i < 1000000; i++) {
boolean entry = false;
try {
entry = SphO.entry("HelloWorld");
while (!entry) {
try {
Thread.sleep(50);
System.out.println("entry false");
entry = SphO.entry("HelloWorld");
} catch (InterruptedException e) {
}
}
System.out.println("entry true");
} catch (Exception e) {
} finally {
if (entry) {
SphO.exit();
}
}
}
}
}
調(diào)用接口過后,通過循環(huán)一百萬次來模擬大流量,這里我們要解釋以下幾個內(nèi)容
- SphO.entry("HelloWorld"):是 Sentinel 的資源控制器,"HelloWord" 是資源的名稱,資源 是 sentinel 的一個很重要的概念,所有的限流都是針對資源的操作;SphO.entry() 返回值是布爾值,為 true 表示資源可用,沒有被限流,為 false 表示資源被限流;
- 這里模擬在被限流了過后,程序等待一段時間,再去判斷是否限流,只有在資源未被限流的時候,才能繼續(xù)處理;
- 在 finally 里面需要進行 SphO.exit(); 操作,當(dāng)被限流了以后,也就是SphO.entry() == true 后一定要執(zhí)行 SphO.exit(); 否則代碼會創(chuàng)建多個Entry 對象,程序運行時間長了過后會導(dǎo)致內(nèi)存泄露,引發(fā) FullGC。
這個時候我們啟動一個服務(wù),調(diào)用一下接口,可以看到效果如下,很快就會運行完,并沒有達(dá)到限流的效果,那是因為我們此刻還沒有配置限流規(guī)則,所以沒有觸發(fā)到限流的邏輯。
配置 sentinel 控制臺
接下來我們安裝一下 sentinel 的控制臺,通過控制臺來配置限流規(guī)則,從而達(dá)到限流的目的,控制臺的搭建很簡單,我們通過官方地址下載指定版本的 jar 然后本地運行即可。通過地址 https://github.com/alibaba/Sentinel/releases/download/1.8.4/sentinel-dashboard-1.8.4.jar 進行下載。
然后通過命令java -Dserver.port=8081 -Dcsp.sentinel.dashboard.server=localhost:8081 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.4.jar 運行即可,如下所示:
這里我們通過指定 8081 端口,用于訪問 sentinel,啟動成功過后,通過瀏覽器我們可以進行登錄,默認(rèn)的初始賬號和密碼都是 sentinel。
因為上面的命令我們指定了 sentinel-dashboard 項目,所以默認(rèn)只會看到 sentinel-dashboard 這個項目,這個時候我們需要,修改代碼,在 pom.xml 中增加下面的配置。
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.4</version>
</dependency>
然后在 JVM 的啟動參數(shù)中增加-Dcsp.sentinel.dashboard.server=localhost:8081 指明 sentinel 的地址和端口號,再啟動我們的應(yīng)用。啟動完過后,我們要手動調(diào)用一下接口,然后就可以看到我們的程序項目連接到 sentinel 了。不過此時只是我們程序和 sentinel 連接成功,還沒有限流規(guī)則,接下來我們要配置一下限流規(guī)則。
按照上圖配置好了過后,我們再調(diào)用一次接口,可以看到此時我們的處理速度明顯慢了下來,每秒只有 20 個 QPS 能獲取資源了,至此我們基于 sentinel 的單機限流QPS 20 的目標(biāo)完成。