Sentinel原始碼解析之一次請求走進Sentinel

$碼出未來發表於2020-11-25

原創不易,轉載請註明出處


前言

本篇開始,我們就正式進入Sentinel原始碼解析了,本篇主要是介紹下Sentinel與SpringMVC整合,傳送批量請求去Sentinel控制檯看看效果,最後我們就要揭祕一下Sentinel是怎樣做到能夠統計請求的。

1.整合Sentinel與Spring MVC

這裡是用的springboot 然後匯入spring-boot-starter-web 快速整合的Spring mvc
這裡貼一下我的pom 引用

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-web-servlet</artifactId>
    <version>1.6.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
    <version>1.6.0</version>
</dependency>

這裡需要 sentinel與servlet的適配專案sentinel-web-servlet,這個專案主要是適配的, 然後還需要通訊專案sentinel-transport-simple-http,這個主要是用來與控制檯sentinel-dashboard 網路通訊的。
接下來我們就得配置了

@Configuration
public class SentinelServletConfig implements WebMvcConfigurer {

    @Bean
    public FilterRegistrationBean sentinelFilterRegistration() {
        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new CommonFilter());
        registration.addUrlPatterns("/*");
        registration.setName("sentinelFilter");
        registration.setOrder(1);

        //logger.info("Sentinel servlet CommonFilter registered");

        return registration;
    }
}

這裡就是個配置類,然後註冊一下CommonFilter 這個元件,想都不用想,sentinel就是通過這個filter 來進行流控的,這裡配置所有的請求都走這個filter
然後就是再隨便寫個Controller。

@RestController()
@RequestMapping("/test")
public class TestController {
    @RequestMapping("/getName")
    public String getName(){
        System.out.println("getName");
        return null;
    }
}

2.批量請求看看效果

啟動sentinel-dashboard 這個專案,它就是個springboot 專案,你可以隨心所欲改動,我這裡是8000 埠啟動的。

在這裡插入圖片描述
賬號密碼都是sentinel,這可以在配置檔案中配置。
在這裡插入圖片描述
登陸進去就是這個樣子,現在只有它一個。
在這裡插入圖片描述
然後在啟動上面整合的那個專案, 啟動引數設定

-Dcsp.sentinel.dashboard.server=localhost:8000
-Dproject.name=consumer_app 

csp.sentinel.dashboard.server 這個引數就是 sentinel-dashboard 的地址,因為你那個專案啟動的時候就要向這個dashboard 專案傳送心跳啥的,然後dashboard 定時管你要Metric資訊,不然它怎麼實時展示。
project.name 專案名稱,這個向dashboard 傳送心跳的時候,會帶上,用來區分專案的。
我們啟動整合的那個專案。
啟動完成後,再來看看sentinel 的dashboard, 發現還是沒有變化,不是應該向sentinel傳送心跳嗎?其實,我們們專案啟動並沒有初始化這個sentinel,當我們發起請求的時候,經過這個CommonFilter 元件,就會進行sentinel的初始化,到時候我們再來看下 sentinel dashboard的效果。
先來傳送一次請求,用什麼工具隨意,我這裡用postman
ok,我這裡傳送完成了,看下效果,可以看到,這個dashboard 已經有我那個project了。
在這裡插入圖片描述
來組批量請求,看下sentinel dashboard 實時效果。我這裡使用postman 傳送1000次請求
在這裡插入圖片描述
在這裡可以看到我某個請求的實時通過qps與拒絕qps ,響應時間。
在sentinel的世界中, 我們/test/getName 就是一個資源,你可以把一個介面看作一個資源。

3.走進Sentinel的Servlet適配專案

我們來看下 sentinel 與servlet適配專案的這個CommonFilter都幹了些啥,簡單看一下這個CommonFilter 的原始碼,在sentinel-web-servlet這個子專案中
在這裡插入圖片描述
首先是解析了一下 requset ,得到一個target,這裡其實就是解析出來你的請求路徑,接著就是使用UrlCleaner 把這個路徑清理了一下,可以看下這個註釋,就是這個意思,接著就是parseOrigin 方法來解析origin
在這裡插入圖片描述
這個originParser 是個null ,所以就不走了,這塊其實就是解析源的,也就是這個請求的上層,你可以通過某種方式帶過來,然後自己寫個parser 再把這個源解析出來,這裡我們就不看了。
接著就是

ContextUtil.enter(target, origin);
entry = SphU.entry(target, EntryType.IN);

這兩行了我們這裡重點 看下ContextUtil.enter(target, origin);,後面這個更重要,我們後面天天打交道。
在這裡插入圖片描述
判斷name是不是sentinel_default_context ,很顯然不是,執行trueEnter 方法
在這裡插入圖片描述
先是從contextHolder 中獲取一個context ,這個contextHolder 就是個threadLocal,也就是一個執行緒一個context,如果沒有的話,接著就是contextNameNodeMap 中獲取 你這個name的 DefaultNode ,關於 DefaultNode 先不要管,如果node是null的話,這個map中快取的是不是大於2000了,超過2000的話就往這個執行緒對應的threadLocal 設定一個空的context,下面就是加鎖再來檢查一遍,建立一個EntranceNode,你可以認為這個EntranceNode 是這個資源(就是你傳過來的那個name)的起始 node, 然後根結點新增這個node為位元組點,將name 對應node放入 map中快取起來。建立context,然後將這個context 設定到treadLocal中。
好了,先解析到這裡,其實這個name 你就可以認為一個資源,在servlet中就是/test/getName這種形式的,一個介面就是一個資源。一個資源對應一個EntranceNode,到後面還對應一個ClusterNode。

總結

本文主要是介紹了一下 sentinel 與我們springboot 的web 專案整合,其實什麼web專案都是一樣,配置一下CommonFilter,請求經過這個Filter ,sentinel就能發揮作用,然後就是批量請求了一下,感受了一下sentinel的實時監控功能,通過qps,拒絕qps,rt,最後就是看看這個CommonFilter 到底幹了啥,這裡並沒有深入解析,而是簡單看看,後面會有若干的篇章進行解析原始碼。

相關文章