实践 -- 微核与插件模式如何在业务中使用

之前在学习Dubbo源代码时,了解到了微核这一应用架构设计,在框架中使用微核设计可以很好理解,能够使得该框架拥有很高的扩展性,那么这种架构如何在业务系统中使用呢?本文主要是解决这一疑问,并提出自己的解法,欢迎探讨。(本文只提供示意图,代码部分请参考 示例项目 模块)

微核架构

什么是微核架构?微核的概念来自于操作系统设计,指的是一种只提供必要服务的操作系统内核,比如进程,线程,内存管理,而其他的功能则像插件一样由外部扩展实现,最终组合起来成为一个操作系统。在应用系统设计中,则一般认为系统只提供必要的能力框架,而平台上的众多功能由对应的插件来实现,比如Eclipse就是典型的微核架构模式,Eclipse本身提供了一个框架,其他功能都由对应的插件来实现。这样来看微核实际上分成了两个部分:核心系统插件模块,核心系统提供系统运转的基本必要能力,保证了对应业务的可运行性,插件则提供了更加丰富的能力扩展,从而演变出各式各样丰富的应用程序。
-w627

根据这些不难想象到微核架构的特性>

  1. 核心能力很明确,因为轻量级内核不可能大而全,很明确自身需要提供什么样子的服务
  2. 高度的扩展性,除核心能力外,众多功能都是由插件来实现,除了插件的注册以及加载,平台对插件本身实现给予了非常高的自由度
  3. 插件隔离,插件与插件之间是隔离存在的,互相不感知,保证了系统架构的简单纯粹,当然也有例外的,比如IDEA,在开发插件过程中是允许依赖其他插件来完成自身功能,不过不太常见。
  4. ….

同时微核架构需要具备三要素

  1. 核心业务流程可拆分为扩展点
  2. 插件注册加载管理器
  3. 插件访问适配器,用于核心系统获取插件

那么剩下来的问题就是该如何在业务开发中使用微核架构,首先看一下Dubbo是如何使用的。

Dubbo的微核架构

Duboo作为流行的RPC框架,其高扩展性得益于微核架构以及插件系统,同时作为业界优秀的框架,非常适合微核架构的学习。那么微核架构的三要素在Dubbo中是如何体现的呢?

核心业务流程

Dubbo的作者梁飞之前分享过一个Dubbo的扩展性设计文档,该文档中详细描述了Dubbo的核心接口设计以及流程,这些本质上就是Dubbo的核心,有兴趣的同学可以参考原PDFDubbo_Framework_Extensions.pdf。为了描述简单,本文简化了详细接口设计,仅仅展示相关核心能力。作为核心系统,Dubbo提供了服务发现接口,并不关心服务到底存储在ZK,还是Local。Dubbo提供了负载均衡能力,并不关心具体实现算法是什么。Dubbo也提供了服务调用能力,当然也不关心底层是Netty还是Mina。这些实际上是微核架构所产生的效果,Dubbo在此基础上可以扩展出来众多的能力支持。
-w843

插件注册加载管理器

插件是微核架构下扩展性的主要来源,Dubbo主要使用了SPI机制,在服务启动时ExtensionLoader会感知对应的插件配置,本质上是ClassLoader下配置的k-v properties,感知后则会主动加载插件存放到内存中,需要用到时再实例化插件。更加详细设计可以参考Dubbo – 扩展机制实现原理
-w803

插件访问适配器

插件与核心系统之间一般会存在一层适配器,适配器为核心系统提供定位以及获取到对应插件的能力,适配器的入参一般是插件坐标,针对Dubbo,则为配置中的key值,出参则为插件实例对象,主要实现逻辑也相当简单,根据坐标从所有该类型插件中筛选出来核心系统想要的那一个。
-w567

业务中如何使用微核架构?

核心业务流程

三要素之一-核心业务流程需要具有可扩展性,具有了扩展性才有使用插件的必要。那么什么样子的业务具有可扩展性?这个不好回答,一般需要程序设计者站在当前以及未来考虑。比如要设计一个在线签名工具,支持MD5,SHA1,SHA256等算法,那么很明显MD5以及SHA1每一个签名算法就是一个插件,系统接收到请求后,只需要拿到对应的插件进行签名返回即可。再比如复杂点的用户下单支付流程,首先入参都是 商品Ids + 优惠券,进入到内部后,首先决定订单类型,是实物订单还是虚拟订单,或者某一类服务订单,那么每一种订单类型的校验可能都是不同的逻辑,并且后续还可能增加更多的订单类型,那么这自然是一个扩展点。接着判断优惠券是否支持商品,然而一般优惠券有很多类型,比如限定商品券,满减券,满3赠1券,那么优惠券类型校验也是一个扩展点,最后发起支付,支付对接有支付宝,微信,银联,那么支付渠道又是一个扩展点。
上述两个例子也能体现微核架构的普适性,无论是在整个系统级使用还是在某一个单一维度使用都能很好的嵌入进当前的结构。从设计模式角度来看,更像是被全局管理起来的策略模式。
-w648

插件注册加载管理器

Dubbo的插件使用SPI进行管理,对于框架来说这个可能很合适,不需要依赖外部框架,但是对于业务系统来说却很不友好,首先就没解决依赖注入的问题,其次不好管理,尤其是禁用某个插件还需要走发布流程。目前业务系统中都会有对应的IOC容器,IOC容器本身又管理着应用中的实例,那么依托与IOC容器,设计一个插件管理中心是当下最佳做法。

假设业务是上述的订单业务,IOC容器是Spring,插件的注册即被IOC实例化,那么只需要解决插件加载问题。定义接口PluginRegisterFactory,该接口能感知对应的IOC容器,并提供从IOC获取插件的能力,另外为了统一管理,额外增加了PluginType字段。接着整个Spring IOC容器初始化完毕后,会触发Refesh事件,监听该事件,从ApplicationContext中根据接口获取到对应的所有插件,放到自定义Map,即可完成插件的加载。

-w656

插件访问适配器

按照上述流程,系统启动后,每一类插件有自己的注册管理器,同时每一个注册管理器都有PluginType来唯一标识,访问层适配的思路就可以根据PluginType来进行路由,找到对应的插件管理器,取出对应的插件。由于逻辑比较简单,这里就不再赘述,具体可以参考示例项目。
-w532

示例项目

参考

  • 《软件架构设计》
实践 -- Velocity渲染SQL如何避免注入?
V2ray -- 使用Cloudflare CDN加速访问