写点什么

Soul 网关源码阅读(九)插件配置加载初探

用户头像
关注
发布于: 2021 年 01 月 21 日

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(或者同步通信模块)那边过来的,接收到数据以后,进行配置加载到本地


    我们还看到一些删除和更新相关的操作,这些属于后面的数据同步分析了,这里先放一放


Soul 网关源码分析文章列表


Github



掘金



发布于: 2021 年 01 月 21 日阅读数: 19
用户头像

关注

还未添加个人签名 2018.09.09 加入

代码是门手艺活,也是门艺术活

评论

发布
暂无评论
Soul网关源码阅读(九)插件配置加载初探