天天看點

Sentinel源碼分析九、擴充點分析

先回顧一下Sentinel的子產品結構以及互相關系:

Sentinel源碼分析九、擴充點分析

簡單說明上圖的幾個子產品

  • sentinel-dashboard:一個通過 spring boot 實作的 web 應用,相當于是 Sentinel 的 OPS 工具,通過 dashboard 我們可以更友善的對規則進行調整、查詢實時統計資訊等,但是這并不是必須的,沒有 dashboard 我們也能使用 Sentinel,甚至我們可以通過 Sentinel 提供的 api 來實作自己的 dashboard。
  • sentinel-transport:一個 sentinel-core 和 sentinel-dashboard 通訊的橋梁,如果我們的應用接入了 Sentinel,并且也想通過 dashboard 來管理的話,那就需要引入 sentinel-transport 子產品。
  • sentinel-extension:一個 Sentinel 的擴充子產品,主要是實作了規則的動态更新和持久化。另外熱點參數限流也在這裡實作的,除此之外注解的相關實作也是在這個子產品中。
  • sentinel-adapter:一個擴充卡的擴充,通過擴充卡可以很友善的為其他架構進行 Sentinel 的內建。
  • sentinel-cluster:叢集限流的擴充,通過引入這個子產品可以在叢集環境中使用 Sentinel。

之前已經分析完了Sentinel的名額收集以及後續Slot對于名額統計資料的規則處理。這部分是Sentinel的核心邏輯。

下面我們看幾個擴充點:

系統初始化 InitFunc

Sentinel提供InitFunc接口供系統初始化,如果我們需要在系統初始化時做一些事情可以實作這個接口。這是一個SPI接口。

代碼:

public class Env {

    public static final Sph sph = new CtSph();

    static {
        // If init fails, the process will exit.
        InitExecutor.doInit();
    }

}
           

這裡執行初始化操作。Env的靜态代碼塊。

public static void doInit() {
    if (!initialized.compareAndSet(false, true)) {
        return;
    }
    try {
        ServiceLoader<InitFunc> loader = ServiceLoaderUtil.getServiceLoader(InitFunc.class);
        List<OrderWrapper> initList = new ArrayList<OrderWrapper>();
        for (InitFunc initFunc : loader) {
            RecordLog.info("[InitExecutor] Found init func: " + initFunc.getClass().getCanonicalName());
            insertSorted(initList, initFunc);
        }
        for (OrderWrapper w : initList) {
            w.func.init();
            RecordLog.info(String.format("[InitExecutor] Executing %s with order %d",
                w.func.getClass().getCanonicalName(), w.order));
        }
    } catch (Exception ex) {
        RecordLog.warn("[InitExecutor] WARN: Initialization failed", ex);
        ex.printStackTrace();
    } catch (Error error) {
        RecordLog.warn("[InitExecutor] ERROR: Initialization failed with fatal error", error);
        error.printStackTrace();
    }
}
           

SPI讀取InitFunc接口的實作,基于@InitOrder注解排序,排序後執行init方法。

Sentinel已實作的有如下幾個:

Sentinel源碼分析九、擴充點分析

其中

  • CommandCenter 的初始化
  • HeartBeat 的初始化與心跳發送
  • 叢集服務端和用戶端的初始化
  • 熱點限流中 StatisticSlot 回調的初始化

規則持久化

兩個接口

  • ReadableDataSource:讀資料源負責監聽持久化的資料源的變更,在接收到變更事件時将最新的資料更新
  • WritableDataSource:寫資料源負責将變更後的規則寫入到持久化的資料源中

目前系統中隻有一種檔案資料源實作了 WritableDataSource 接口,其他的資料源隻實作了 ReadableDataSource 接口。這部分Sentinel已經實作了,基本不需要有什麼修改。

Sentinel源碼分析九、擴充點分析

我們看一下 FlowRuleManager 的 loadRules方法:

public static void loadRules(List<FlowRule> rules) {
    currentProperty.updateValue(rules);
}
           

實際上是通過 DynamicSentinelProperty 的 updateValue 方法來動态更新規則的。那麼我們隻需要在持久化的規則發生變更時,通過觸發 SentinelProperty 的 updateValue 方法把更新後的規則注入進去就可以了。我們隻需要實作監聽每種持久化的資料源在發生資料變更時的事件,當接收到最新的資料時将它 update 進 FlowRuleManager 中即可。

Sentinel源碼分析九、擴充點分析

網絡通信

sentinel-transport 子產品中的功能基本上都是網絡通訊相關的,而我們有很多的網絡協定:http、tcp等,是以網絡通訊這塊肯定也要有可擴充的能力。目前 sentinel-transport-common 子產品中抽象了3個接口作為擴充點:

  • CommandCenter:該接口主要用來在 sentinel-core 中啟動一個可以對外提供 api 接口的服務端,Sentinel 中預設有兩個實作,分别是 http 和 netty。但是官方預設推薦的是使用 http 的實作。
  • CommandHandler:該接口主要是用來處理接收到的請求的,不同的請求有不同的 handler 類來進行處理,我們可以實作我們自己的 CommandHandler 并注冊到 SPI 配置檔案中來為 CommandCenter 添加自定義的指令。
  • HeartbeatSender:該接口主要是為 sentinel-core 用來向 sentinel-dashboard 發送心跳的,預設也有兩個實作,分别是 http 和 netty。
Sentinel源碼分析九、擴充點分析

Slot鍊生成器

SPI接口SlotChainBuilder,預設使用DefaultSlotChainBuilder構造器,可以自定義SlotChainBuilder的實作來構造SlotChain

自定義Slot

SPI接口ProcessorSlot,預設8個實作,這個之前已經分析過了。 可以自定義ProcessorSlot的實作,并設定@SpiOrder合适的值已達到插入自定義Slot的效果

StatisticSlot回調

分析 StatisticSlot代碼的時候已經看到了這部分。這裡包含兩個接口

  • ProcessorSlotEntryCallback

    包含 onPass 和 onBlocked 兩個回調函數,分别對應着請求在 pass 和 blocked 的時候執行。

  • ProcessorSlotExitCallback

    包含 onExit 回調函數,對應着請求在 exit 的時候執行。

可以自定義接口實作,并通過StatisticSlotCallbackRegistry的addEntryCallback或addExitCallback方法注冊自定義回調。