逅弈 轉載請註明原創出處,謝謝!
系列文章
通過 Sentinel 的控制檯,我們可以對規則進行查詢和修改,也可以檢視到實時監控,機器列表等資訊,所以我們需要對 sentinel 的控制檯做個完整的瞭解。
部署控制檯
首先需要啟動控制檯, sentinel 的控制檯是用 spring boot 寫的一個web 應用,我們有幾種方式來獲取控制檯:
下載可執行 jar 包
從 release 頁面 下載截止目前為止最新版本的控制檯 jar 包,如下圖所示:
下載原始碼構建
除了可以下載預先構建好的可執行 jar 包之外,我們還可以把控制檯的工程下載下來自行用原始碼構建,sentinel 是一個多 maven 模組的專案,控制檯是其中的一個專案,如下圖所示:
如上圖所示,我們可以下載完整的 sentinel 的專案,然後構建其中的 sentinel-dashboard 模組,也可以只下載 sentinel-dashboard 模組然後構建。
這裡我選擇將完整的 sentinel 工程下載下來,然後構建 sentinel-dashboard 模組,首先在專案根目錄下執行:
cd sentinel-dashboard
複製程式碼
將會進入 dashboard 模組,然後在 dashboard 目錄下執行:
mvn clean package
複製程式碼
maven將會把 sentinel-dashboard 模組打包成一個可執行的 fat jar包,如下圖所示:
啟動控制檯
構建成功後,就可以啟動控制檯了,執行以下命令:
java -Dserver.port=8080 \
-Dcsp.sentinel.dashboard.server=localhost:8080 \
-jar target/sentinel-dashboard.jar
複製程式碼
其中 -Dserver.port=8080
用於指定 Sentinel 控制檯埠為 8080
。
執行完之後,你將看到如下資訊:
當看到 Started DashboardApplication in xx seconds
時,說明你的控制檯已經啟動成功了,訪問 http://localhost:8080/ 就可以看到控制檯的樣子了,如下圖所示:
可以看到當前控制檯中沒有任何的應用,因為還沒有應用接入。
接入控制檯
要想在控制檯中操作我們的應用,除了需要部署一個控制檯的服務外,還需要將我們的應用接入到控制檯中去。
引入 transport 依賴
首先需要在我們使用 sentinel 的服務中引入 sentinel-transport 的依賴,因為我們的應用是作為客戶端,通過transport模組與控制檯進行通訊的,依賴如下所示:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>x.y.z</version>
</dependency>
複製程式碼
版本依然選擇最新的 1.4.0
配置應用啟動引數
引入了依賴之後,接著就是在我們的應用中配置 JVM 啟動引數,如下所示:
-Dproject.name=xxx -Dcsp.sentinel.dashboard.server=consoleIp:port
複製程式碼
其中的consoleIp和port對應的就是我們部署的 sentinel dashboard 的ip和port,我這裡對應的是 127.0.0.1 和 8080,按照實際情況來配置 dashboard 的ip和port就好了,如下圖所示:
從圖中可以看到我設定的客戶端的應用名為:lememo,當客戶端連線上控制檯後,會顯示該應用名。
PS:需要注意的是,除了可通過 JVM -D 引數指定之外,也可通過 properties 檔案指定,配置檔案的路徑為 ${user_home}/logs/csp/${project.name}.properties
。
配置檔案中引數的key和型別如下所示:
優先順序順序:JVM -D 引數的優先順序最高,若 properties 檔案和 JVM 引數中有相同項的配置,以 JVM -D 引數配置的為準。
觸發客戶端連線控制檯
客戶端配置好了與控制檯的連線引數之後,並不會主動連線上控制檯,需要觸發一次客戶端的規則才會開始進行初始化,並向控制檯傳送心跳和客戶端規則等資訊。
客戶端與控制檯的連線初始化是在 Env 的類中觸發的,即下面程式碼中的 InitExecutor.doInit();
:
public class Env {
public static final NodeBuilder nodeBuilder = new DefaultNodeBuilder();
public static final Sph sph = new CtSph();
static {
// If init fails, the process will exit.
InitExecutor.doInit();
}
}
複製程式碼
埋點
上篇文章中我們建立了一個 UserService 來做驗證,正常時會返回一個使用者物件,被限流時返回一個null,但是這樣不太直觀,本篇文章我換一個更簡單和直觀的驗證方式,程式碼如下所示:
@GetMapping("/testSentinel")
public @ResponseBody
String testSentinel() {
String resourceName = "testSentinel";
Entry entry = null;
String retVal;
try{
entry = SphU.entry(resourceName,EntryType.IN);
retVal = "passed";
}catch(BlockException e){
retVal = "blocked";
}finally {
if(entry!=null){
entry.exit();
}
}
return retVal;
}
複製程式碼
PS:這裡有個需要注意的知識點,就是 SphU.entry 方法的第二個引數 EntryType 說的是這次請求的流量型別,共有兩種型別:IN 和 OUT 。
IN:是指進入我們系統的入口流量,比如 http 請求或者是其他的 rpc 之類的請求。
OUT:是指我們系統呼叫其他第三方服務的出口流量。
入口、出口流量只有在配置了系統規則時才有效。
設定 Type 為 IN 是為了統計整個系統的流量水平,防止系統被打垮,用以自我保護的一種方式。
設定 Type 為 OUT 一方面是為了保護第三方系統,比如我們系統依賴了一個生成訂單號的介面,而這個介面是核心服務,如果我們的服務是非核心應用的話需要對他進行限流保護;另一方面也可以保護自己的系統,假設我們的服務是核心應用,而依賴的第三方應用老是超時,那這時可以通過設定依賴的服務的 rt 來進行降級,這樣就不至於讓第三方服務把我們的系統拖垮。
下圖描述了流量的型別和系統之間的關係:
連線控制檯
應用接入 transport 模組之後,我們主動來訪問一次 /testSentinel
介面,順利的話,客戶端會主動連線上控制檯,並將自己的ip等資訊傳送給控制檯,並且會與控制檯維持一個心跳。
現在我們在來訪問下控制檯,看到客戶端已經連線上來了,如下圖所示:
客戶端連線上dashboard之後,我們就可以為我們定義的資源配置規則了,有兩種方式可以配置規則:
- 在【流控規則】頁面中新增
- 在【簇點鏈路】中新增
我們可以在【流控規則】頁面中新增,點選【流控規則】進入頁面,如下圖所示:
在彈出框中,填寫資源名和單機閾值,其他的屬性保持預設設定即可,如下圖所示:
點選【新增】後,規則即生效了。
第二種方式就是在【簇點鏈路】的頁面中找到我們埋點的資源名,然後直接對該資源進行增加流控規則的操作,如下圖所示:
上圖中右側的【+流控】的按鈕點選後,彈出框與直接新增規則是一樣的,只是會自動將資源名填充進去,省去了我們設定的這一步。
驗證效果
規則建立完成之後,我們就可以在【流控規則】頁面查詢到了,如下圖所示:
接著我們就可以來驗證效果了,讓我們在瀏覽器中快速的重新整理來請求 /testSentinel
這個介面,不出意外,應該會看到如下圖所示的情況:
說明我們設定的流控規則生效了,請求被 block 了。
現在我們再到控制檯的【實時監控】頁面查詢下,剛剛我們的一頓瘋狂請求應該有很多都被 block 了,通過的 qps 應該維持在2以下,如下圖所示:
原理
我們知道 sentinel 的核心就是圍繞著幾件事:資源的定義,規則的配置,程式碼中埋點。
而且這些事在 sentinel-core 中都有能力實現,也對外暴露了相應的 http 介面方便我們檢視 sentinel 中的相關資料。
CommandCenter
sentinel-core 在第一次規則被觸發的時候,啟動了一個 CommandCenter,也就是我們引入的 sentinel-transport-simple-http 依賴中被引入的實現類:SimpleHttpCommandCenter。
這個 SimpleHttpCommandCenter 類中啟動了兩個執行緒池:主執行緒池和業務執行緒池。
主執行緒池啟動了一個 ServerSocket 來監聽預設的 8719 埠,如果埠被佔用,會自動嘗試獲取下一個埠,嘗試3次。
業務執行緒池主要是用來處理 ServerSocket 接收到的資料。
將不重要的程式碼省略掉之後,具體的程式碼如下所示:
public class SimpleHttpCommandCenter implements CommandCenter {
// 省略初始化
private ExecutorService executor;
private ExecutorService bizExecutor;
@Override
public void start() throws Exception {
Runnable serverInitTask = new Runnable() {
int port;
{
try {
port = Integer.parseInt(TransportConfig.getPort());
} catch (Exception e) {
port = DEFAULT_PORT;
}
}
@Override
public void run() {
// 獲取可用的埠用以建立一個ServerSocket
ServerSocket serverSocket = getServerSocketFromBasePort(port);
if (serverSocket != null) {
// 在主執行緒中啟動ServerThread用以接收socket請求
executor.submit(new ServerThread(serverSocket));
// 省略部分程式碼
} else {
CommandCenterLog.info("[CommandCenter] chooses port fail, http command center will not work");
}
executor.shutdown();
}
};
new Thread(serverInitTask).start();
}
class ServerThread extends Thread {
private ServerSocket serverSocket;
ServerThread(ServerSocket s) {
this.serverSocket = s;
}
@Override
public void run() {
while (true) {
Socket socket = null;
try {
socket = this.serverSocket.accept();
setSocketSoTimeout(socket);
// 將接收到的socket封裝到HttpEventTask中由業務執行緒去處理
HttpEventTask eventTask = new HttpEventTask(socket);
bizExecutor.submit(eventTask);
} catch (Exception e) {
// 省略部分程式碼
}
}
}
}
}
複製程式碼
具體的情況如下圖所示:
HTTP介面
SimpleHttpCommandCenter 啟動了一個 ServerSocket 來監聽8719埠,也對外提供了一些 http 介面用以操作 sentinel-core 中的資料,包括查詢|更改規則,查詢節點狀態等。
PS:控制檯也是通過這些介面與 sentinel-core 進行資料互動的!
提供這些服務的是一些 CommandHandler 的實現類,每個類提供了一種能力,這些類是在 sentinel-transport-common 依賴中提供的,如下圖所示:
查詢規則
執行下面命令,則會返回現有生效的規則:
curl http://localhost:8719/getRules?type=<XXXX>
複製程式碼
其中,type有以下取值:
flow
以 JSON 格式返回現有的限流規則;degrade
則返回現有生效的降級規則列表;system
則返回系統保護規則。
更改規則
同時也可以通過下面命令來修改已有規則:
curl http://localhost:8719/setRules?type=<XXXX>&data=<DATA>
複製程式碼
其中,type 可以輸入 flow
、degrade
等方式來制定更改的規則種類,data
則是對應的 JSON 格式的規則。
其他的介面不再一一詳細舉例了,有需要的大家可以自行檢視原始碼瞭解。
更多原創好文,請關注公眾號「逅弈逐碼」