Soul閘道器原始碼閱讀(九)外掛配置載入初探
Soul閘道器原始碼閱讀(九)外掛配置載入初探
簡介
今日來探索一下外掛的初始化,及相關的配置的載入
原始碼Debug
外掛初始化
首先來到我們非常熟悉的外掛鏈呼叫的類: SoulWebHandler ,在其中的 DefaultSoulPluginChain ,我們看到plugins是通過建構函式傳入的
我們來跟蹤一下是誰傳入的,在SoulWebHandler的public那一行打上斷點,重啟後跟蹤呼叫棧
public final class SoulWebHandler implements WebHandler { private final List<SoulPlugin> plugins; // 在這裡也看到plugins是通過建構函式傳入的,在public這一行打上斷點,然後重啟 public SoulWebHandler(final List<SoulPlugin> plugins) { this.plugins = plugins; String schedulerType = System.getProperty("soul.scheduler.type", "fixed"); if (Objects.equals(schedulerType, "fixed")) { int threads = Integer.parseInt(System.getProperty( "soul.work.threads", "" + Math.max((Runtime.getRuntime().availableProcessors() << 1) + 1, 16))); scheduler = Schedulers.newParallel("soul-work-threads", threads); } else { scheduler = Schedulers.elastic(); } } @Override public Mono<Void> handle(@NonNull final ServerWebExchange exchange) { MetricsTrackerFacade.getInstance().counterInc(MetricsLabelEnum.REQUEST_TOTAL.getName()); Optional<HistogramMetricsTrackerDelegate> startTimer = MetricsTrackerFacade.getInstance().histogramStartTimer(MetricsLabelEnum.REQUEST_LATENCY.getName()); // 傳入 plugins 到 DefaultSoulPluginChain return new DefaultSoulPluginChain(plugins).execute(exchange).subscribeOn(scheduler) .doOnSuccess(t -> startTimer.ifPresent(time -> MetricsTrackerFacade.getInstance().histogramObserveDuration(time))); } private static class DefaultSoulPluginChain implements SoulPluginChain { private int index; private final List<SoulPlugin> plugins; // 通過建構函式得到 plugins DefaultSoulPluginChain(final List<SoulPlugin> plugins) { this.plugins = plugins; } @Override public Mono<Void> execute(final ServerWebExchange exchange) { return Mono.defer(() -> { if (this.index < plugins.size()) { SoulPlugin plugin = plugins.get(this.index++); Boolean skip = plugin.skip(exchange); if (skip) { return this.execute(exchange); } return plugin.execute(exchange, this); } return Mono.empty(); }); } } }
我這有點慢,重啟等以後後,我們檢視上一個呼叫,來到了 SoulConfiguration
通過類的大致資訊可以看到是Spring之類的載入機制。通過前面文章的分析中,大致應該就是配置了 auto configuration ,然後啟動的時候Spring進行載入的
public class SoulConfiguration { @Bean("webHandler") public SoulWebHandler soulWebHandler(final ObjectProvider<List<SoulPlugin>> plugins) { List<SoulPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList); final List<SoulPlugin> soulPlugins = pluginList.stream() .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList()); soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName())); return new SoulWebHandler(soulPlugins); } }
大致就知道是Spring自動載入的,通過AutoConfiguration機制,下面就是Spring相關了,這裡就不繼續跟蹤了
配置初始化
在上一篇的文章分析中,我們分析了路由匹配的大致細節,需要選擇器和規則的配置,下面我們來跟蹤一下這些配置的初始化
首先進入路由匹配核心類: AbstractSoulPlugin ,其中進行 pluginData 、selectorData、rules的獲取,如下面程式碼註釋:
public abstract class AbstractSoulPlugin implements SoulPlugin { @Override public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) { String pluginName = named(); // 獲取外掛資料 final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); if (pluginData != null && pluginData.getEnabled()) { // 根據外掛名稱,獲取選擇器資料 final Collection<SelectorData> selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName); if (CollectionUtils.isEmpty(selectors)) { return handleSelectorIsNull(pluginName, exchange, chain); } final SelectorData selectorData = matchSelector(exchange, selectors); if (Objects.isNull(selectorData)) { return handleSelectorIsNull(pluginName, exchange, chain); } selectorLog(selectorData, pluginName); // 根據選擇器的id,獲取其規則資料 final List<RuleData> rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId()); if (CollectionUtils.isEmpty(rules)) { return handleRuleIsNull(pluginName, exchange, chain); } RuleData rule; if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) { rule = rules.get(rules.size() - 1); } else { rule = matchRule(exchange, rules); } if (Objects.isNull(rule)) { return handleRuleIsNull(pluginName, exchange, chain); } ruleLog(rule, pluginName); return doExecute(exchange, chain, selectorData, rule); } return chain.execute(exchange); } }
我們到這個類: BaseDataCache 看一下,看到它有兩個資料的Map儲存:外掛資料、選擇器資料、規則資料
public final class BaseDataCache {
/**
* pluginName -> PluginData.
*/
private static final ConcurrentMap<String, PluginData> PLUGIN_MAP = Maps.newConcurrentMap();
/**
* pluginName -> SelectorData.
*/
private static final ConcurrentMap<String, List<SelectorData>> SELECTOR_MAP = Maps.newConcurrentMap();
/**
* selectorId -> RuleData.
*/
private static final ConcurrentMap<String, List<RuleData>> RULE_MAP = Maps.newConcurrentMap();
}
我們發現它是一個靜態單例,而外掛資料的配置是使用下面的函式,我們在函式上打上斷點,看看是誰呼叫了它,它的呼叫棧是怎麼樣的
public final class BaseDataCache {
public void cachePluginData(final PluginData pluginData) {
Optional.ofNullable(pluginData).ifPresent(data -> PLUGIN_MAP.put(data.getName(), data));
}
}
在public這一行打上斷點,重啟,順利進入上面的函式,我們檢視下 pluginData ,還挺簡單,大致如下
PluginData(id=1, name=sign, config=null, role=1, enabled=false)
我們跟蹤其呼叫棧,其呼叫鏈的類和函式如下,類在呼叫棧的順序也是對應的
public class CommonPluginDataSubscriber implements PluginDataSubscriber {
// 在這個裡面觸發 subscribeDataHandler , subscribeDataHandler 觸發 DaseDataCache的呼叫
@Override
public void onSubscribe(final PluginData pluginData) {
subscribeDataHandler(pluginData, DataEventTypeEnum.UPDATE);
}
private <T> void subscribeDataHandler(final T classData, final DataEventTypeEnum dataType) {
Optional.ofNullable(classData).ifPresent(data -> {
// 外掛資料的操作
if (data instanceof PluginData) {
PluginData pluginData = (PluginData) data;
if (dataType == DataEventTypeEnum.UPDATE) {
// 在這進行觸發呼叫
BaseDataCache.getInstance().cachePluginData(pluginData);
Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.handlerPlugin(pluginData));
} else if (dataType == DataEventTypeEnum.DELETE) {
BaseDataCache.getInstance().removePluginData(pluginData);
Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.removePlugin(pluginData));
}
// 選擇器的操作
} else if (data instanceof SelectorData) {
SelectorData selectorData = (SelectorData) data;
if (dataType == DataEventTypeEnum.UPDATE) {
BaseDataCache.getInstance().cacheSelectData(selectorData);
Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.handlerSelector(selectorData));
} else if (dataType == DataEventTypeEnum.DELETE) {
BaseDataCache.getInstance().removeSelectData(selectorData);
Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.removeSelector(selectorData));
}
// 規則的操作
} else if (data instanceof RuleData) {
RuleData ruleData = (RuleData) data;
if (dataType == DataEventTypeEnum.UPDATE) {
BaseDataCache.getInstance().cacheRuleData(ruleData);
Optional.ofNullable(handlerMap.get(ruleData.getPluginName())).ifPresent(handler -> handler.handlerRule(ruleData));
} else if (dataType == DataEventTypeEnum.DELETE) {
BaseDataCache.getInstance().removeRuleData(ruleData);
Optional.ofNullable(handlerMap.get(ruleData.getPluginName())).ifPresent(handler -> handler.removeRule(ruleData));
}
}
});
}
}
// 這個類中觸發了上面單個外掛的配置,並且這個類拿到了所有的外掛資料
public class PluginDataHandler extends AbstractDataHandler<PluginData> {
@Override
protected void doRefresh(final List<PluginData> dataList) {
// 觸發上面的呼叫
pluginDataSubscriber.refreshPluginDataSelf(dataList);
dataList.forEach(pluginDataSubscriber::onSubscribe);
}
}
// 繼續跟下去,看看datalist的來源
public abstract class AbstractDataHandler<T> implements DataHandler {
@Override
public void handle(final String json, final String eventType) {
List<T> dataList = convert(json);
if (CollectionUtils.isNotEmpty(dataList)) {
DataEventTypeEnum eventTypeEnum = DataEventTypeEnum.acquireByName(eventType);
switch (eventTypeEnum) {
case REFRESH:
case MYSELF:
// 觸發上面函式的呼叫
doRefresh(dataList);
break;
case UPDATE:
case CREATE:
doUpdate(dataList);
break;
case DELETE:
doDelete(dataList);
break;
default:
break;
}
}
}
}
// 來到了websocket,進行跟
public class WebsocketDataHandler {
public void executor(final ConfigGroupEnum type, final String json, final String eventType) {
ENUM_MAP.get(type).handle(json, eventType);
}
}
// 在這個類中,看到外掛資料列表是從websocket來的,那就是從Soul-Admin那邊獲取來到的資料
public final class SoulWebsocketClient extends WebSocketClient {
public void onMessage(final String result) {
handleResult(result);
}
@SuppressWarnings("ALL")
private void handleResult(final String result) {
WebsocketData websocketData = GsonUtils.getInstance().fromJson(result, WebsocketData.class);
ConfigGroupEnum groupEnum = ConfigGroupEnum.acquireByName(websocketData.getGroupType());
String eventType = websocketData.getEventType();
String json = GsonUtils.getInstance().toJson(websocketData.getData());
websocketDataHandler.executor(groupEnum, json, eventType);
}
}
看下result的大致內容:也就是一些簡單的配置
{
"groupType": "PLUGIN",
"eventType": "MYSELF",
"data": [{
"id": "1",
"name": "sign",
"role": 1,
"enabled": false
}, {
"id": "9",
"name": "hystrix",
"role": 0,
"enabled": false
}]
}
結合路由匹配的關鍵函式,好像它的作用就是用來判斷是否開啟和根據name獲得選擇器
public abstract class AbstractSoulPlugin implements SoulPlugin {
@Override
public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
String pluginName = named();
final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName);
// 用於判斷是否開啟
if (pluginData != null && pluginData.getEnabled()) {
.......
}
}
}
外掛資料這一塊的流程定位下來比較輕鬆,畢竟前面流程也梳理差不多了
選擇器和規則資料的初始化流程,這裡就不進行定位了,邏輯和外掛資料基本一樣,這裡就不贅述了
總結
今天分析了外掛的初始化和相關配置的初始化
外掛的初始化是使用Spring的自動配置載入機制進行實現了,結合使用外掛都需要引入哪些start的依賴
外掛資料、選擇器資料、規則資料的初始化,都是從Websocket(或者同步通訊模組)那邊過來的,接收到資料以後,進行配置載入到本地
我們還看到一些刪除和更新相關的操作,這些屬於後面的資料同步分析了,這裡先放一放