通过Soul看微内核架构(二)

前一篇文章简单介绍了什么是微内核架构,以及将微内核架构作为中台化系统核心实现的逻辑,参见:什么是微内核架构

前言

简单来说,微内核架构是一种面向插件的架构,通过对功能进行拆分到各个插件组件下,进而实现架构的扩展性。

经过插件化组件拆分,一个单体系统可以分割成核心系统和插件模块,通过插件组件实现扩展、隔离、自定义逻辑实现。

微内核架构的本质在于,将个性化逻辑与变化封装在插件中,实现快速扩展的目的,而不会影响核心系统的稳定性。

插件化系统设计来说需要实现以下几点:

  1.  插件管理:有哪些插件可用、如何加载插件、合适加载。常见的解决方案是类似于插件注册表的机制。

  2. 插件集成:如何将不同的插件组件集成到核心系统上。常见的做法是由核心系统定义集成规范,然后插件按照规范实现,核心系统按照规范加载。具体实现可以参照:OSGI、消息模式、依赖注入等。

  3. 插件通信:插件和插件之间、核心系统和插件之间通信。前文说过什么是微内核架构,通信必须经过核心系统,因此一般是由核心系统提供插件通信机制。

插件化设计

微内核架构可以分为两部分:核心系统(core-system),插件模块(plugin-system)。

插件实现

所有插件都继承于ExtPlugin接口。AbstractExtPlugin类继承ExtPlugin接口,做一些模板方法的实现。

具体业务逻辑插件继承AbstractExtPlugin类,并实现ExtPlugin接口的方法,比如:

  • void execute(PluginParam param,PluginChain);核心处理方法。

  • int getOrder();获取插件排序。

  • String named();获取插件名称。

  • Boolean skip(PluginParam param);判断是否执行此插件,跳过则本次不作处理。

每次插件逻辑处理之前(调用execute之前),先进行skip判断。

AbstractExtPlugin类中重点实现execute方法,可以抽象包装一个doExecute方法,包装execute。

参考网上例子:

if (pluginData.getEnable()){
// 获取插件数据
final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName);
// 获取选择器数据
final Collection<SelectorData> selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName);
final SelectorData selectorData = matchSelector(exchange, selectors);
// 获取规则
final List<RuleData> rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId());
    RuleData rule;
if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) {
//get last
    rule = rules.get(rules.size() - 1);
  } else {
    rule = matchRule(exchange, rules);
  }
// 执行具体处理
return doExecute(exchange, chain, selectorData, rule);
}
// 继续执行后续插件处理
return chain.execute(exchange);

获取选择器数据和规则,然后传入 doExecute 方法进行具体处理,doExecute 方法为抽象方法,交由子类具体实现。

比如有个具体插件实现叫:RoterPlugin。其实现的doExecute方法逻辑如下:

// 获取网关上下文和规则处理器
final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class);
// 获取上游列表
final List<DivideUpstream> upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId());
// 选择待分发的目标上游
final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress();
DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip);
// 设置 http url 
String domain = buildDomain(divideUpstream);
String realURL = buildRealURL(domain, soulContext, exchange);
exchange.getAttributes().put(Constants.HTTP_URL, realURL);
// 设置 http timeout
exchange.getAttributes().put(Constants.HTTP_TIME_OUT, ruleHandle.getTimeout());
exchange.getAttributes().put(Constants.HTTP_RETRY, ruleHandle.getRetry());
return chain.execute(exchange);

RouterPlugin实现了请求的分发,根据选择器和规则找到对应的服务,再通过负载均衡策略分配到其他服务实例。

插件管理实现

引入plugin-admin系统,实现可视化的插件录入、查询、管理。通过事件驱动方式,将发布的的插件注入到目标服务。

目标服务启动(reload),在容器启动过程中,借助于springboot start机制,自动扫描并注册bean到spring 容器中。

插件集成实现

借助springboot的多实例自动注入的能力(ObjectProvider plugins),将插件Bean列表集成注入到服务的插件链中,实现系统与插件的集成。

插件通信实现

在插件链初始化阶段完成插件排序,依靠责任链模式,将参数贯穿所有插件依次处理,实现插件通信。

插件链实现

将多个插件组合放在Chain中进行顺序调用。比如DefaultExtPluginChain实现ExtPluginChain。

在DefaultExtPluginChain中有execute方法,实现插件的链式调用:

public Mono<Void> execute(final ServerWebExchange exchange) {
// 反应式编程语法:Mono.defer
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();
    });
}

SoulWebHandler 是 web 请求处理的起点,在此创建并开始插件链的处理。

同 DefaultSoulPluginChain 一样,SoulWebHandler 也是持有通过构造方法传入的插件链。

看看 handle 方法:

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());
return new DefaultSoulPluginChain(plugins).execute(exchange).subscribeOn(scheduler)
            .doOnSuccess(t -> startTimer.ifPresent(time -> MetricsTrackerFacade.getInstance().histogramObserveDuration(time)));
}

handle 方法负责插件链执行指标度量的采集,通过在 DefaultSoulPluginChain 执行时加订阅实现,DefaultSoulPluginChain 在此处完成初始化。

全局查找 SoulWebHandler 构造方法,定位到 SoulConfiguration 的 soulWebHandler 方法。

SoulConfiguration 是核心配置类,负责自动装配网关所需的核心 bean 对象。

如装配 SoulWebHandler:

@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);
}

初始化 SoulWebHandler

app-bootstrap 启动的过程中,所有插件是怎么形成 ObjectProvider<list> plugins,然后初始化 SoulWebHandler 的呢?

SoulWebHandler 所在的配置类通过配置 @ComponentScan(“org.dromara.soul”),通知 spring 扫描 org.dromara.soul 包。

借助 springboot 的 starter 机制,将 spring.factories 里指定的配置类自动加载到容器。

参考SPI实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值