关于如何做好开放、扩展的技术架构设计和相关想法。

背景

做过相关基础工作的同学应该都比较清楚,除了工作本身之外,很大一部分在于如何支持到上层业务和扩展上。如果没有好的技术架构来支撑扩展,那么将会导致巨大的基础咨询、基础开发,也会因为没有好的隔离、抽象,导致整体的可维护性急剧下降,甚至导致线上稳定性的问题,并使业务不可用。

开放&扩展

从主体角度出发,如何做好开放,才有后续客体的扩展。所以,开放扩展是需要一体考虑的。

首先从现实的开发世界来看,已有哪些开放&扩展的方案设计。从核心、常用的软件说起,比如:

  • 后端开发的大致核心流程:Eclipse IDE 开发 -> Maven / Gradle 构建 -> Docker 打包部署 -> JVM Runtime Container 运行时 -> Tomcat / Jetty Web 容器

  • 前端开发的大致核心流程:VSCode 开发 -> Webpack 构建 -> NodeJS Runtime 运行时 -> Browser Renderer 渲染器

后端核心流程的开放&扩展机制

Eclipse Plugin

Eclipse Plugin 的开放与扩展,参考 A Quick-Start Tutorial to Eclipse Plug-in Development

image

图中举例了一个选中扩展,Eclipse 提供一个 Selection Service 的总线方式,包括代码编辑器、包浏览器、大纲浏览器等,都会触发「选中事件」,然后属性展示器或声明展示器就会监听「选中事件」,然后展示对应的属性或声明。更多事件可参考 org.eclipse.jface.viewers

简单来说,Eclipse Plugin 的开放与扩展,就是通过总线的形式,并用发布/订阅模式来实现扩展。

Maven Plugin

参考 Maven 的执行生命周期 maven-lifecycle-vs-phase-vs-plugin-vs-goal

相关的插件开发,通过指定生命周期,并配置 pom.xml 配置来实现。参考:guide-java-report-plugin-development

image

小结:Maven Plugin 的开放与扩展,就是通过生命周期来做扩展,生命周期即为扩展点,配置文件就是扩展点的执行逻辑和顺序,依次寻找插件扩展点并执行即可。

JVM 的 JNI 扩展

参考 JNI

image

$ java -Djava.library.path=/path/to/jnilib Test

JVM 的底层扩展,通过启动时加载对应的库,实现扩展。对应的库实现相关 JNI 接口,业务侧在启动的时候,注册相关的扩展 (RegisterNatives 在 static 区块声明,相当于做了一次回调并注册)并做 puclic native xx 相关声明即可被其他 Java 代码正常调用。

JNI 的扩展,是简单的接口约定 + 实现的逻辑。一个接口约定对应一个扩展点,且仅对应一个扩展实现。

Servlet Filter 扩展机制

Servlet 的机制大致如下:

image

Servlet Filter 机制,本身是出于解决 HTTP 请求的场景,本身具有较高的复杂度,比如:登录态、CORS 等等。所以,相关的开放与扩展机制,做得也相对成熟一些。

可以指定任意路径的 Filter,不同路径的 filter 的数量可以自由配置, 相关顺序由 web.xml 配置决定。

前端核心流程的开放&扩展机制

VSCode Extension

参考 VSCode API

image

VSCode 的插件机制,为了追求较高的扩展、轻量、快速反应的编辑器,整体都做了相当不错的隔离。包括主进程和渲染进程的隔离,使用 IPC/RPC 通信。

插件也通过 VSCode 的各种 Protocol 协议来实现扩展,和 Eclipse 的插件机制类似,但总体会更轻量些,也能看成是发布订阅的机制。

Webpack Plugin

Webpack 的插件实现,是基于 tapable 实现的。

总的思路是,所有包括 webpack 本身都是插件,暴露 run/emit/done 等 hooks 来扩展。

image

参考 图解Webpack——实现Plugin

NodeJS Native

参考 NodeJS Native

image

和 JNI 类似,NodeJS Native 的扩展,通过一层 interface 抽象,也是简单的接口约定 + 实现的逻辑。

相关方案小结

  • IDE 和编辑器的扩展,总的是围绕「编辑」展开的,相关的扩展通过事件的发布/订阅机制实现。
  • Maven、Webpack 有明确的编译生命周期,扩展的时机也较为明确,基于编译生命周期,叠加相关的代码逻辑即可。
  • JVM、Node 的扩展,类似 Interface 定义的约定,运行时按某种约定的 protocol 来做调用,实现方按 protocol 来做相关实现。

参考