10分钟带你彻底搞懂微内核架构

十分钟搞懂系列

序号标题链接
110分钟带你彻底搞懂企业服务总线https://blog.csdn.net/belongtocode/article/details/119487731
210分钟带你彻底搞懂微内核架构https://blog.csdn.net/belongtocode/article/details/119107837
310分钟带你彻底搞懂服务限流和服务降级https://blog.csdn.net/belongtocode/article/details/119107785
410分钟带你彻底搞懂负载均衡https://blog.csdn.net/belongtocode/article/details/118977839
510分钟带你彻底搞懂集群容错和服务隔离https://blog.csdn.net/belongtocode/article/details/118968771
610分钟带你彻底搞懂注册中心https://blog.csdn.net/belongtocode/article/details/118639474
710分钟带你彻底搞懂RPC架构https://blog.csdn.net/belongtocode/article/details/118639448

在日常开发过程中,我们经常会遇到这样的需求:针对某个业务场景,我们希望在系统中添加一种新的处理逻辑,但又不想对现有的系统造成太大的影响。从架构设计上讲,这是一种典型的系统扩展性需求。

针对这样的扩展性需求,本质上开发人员想要的是一种类似插件化的架构体系,调用者通过一个插件工厂获取想要的插件,而插件工厂则基于配置动态创建对应的插件,这样整体系统就像搭积木一样可以进行动态的组装。

img

基于插件化系统,我们往现有系统中添加新的业务逻辑时,就只需要实现一个新的插件并替换老的插件即可,系统的扩展性得到了很好的保证。

那么如何实现这样的插件系统呢?微内核(MicroKernel)架构就是业界主流的实现方案。

什么是微内核架构?

微内核架构包含两部分组件,即内核系统和插件。

这里的内核系统用来定义插件的实现规范,并管理着插件的生命周期。而各个插件是相互独立的组件,各自根据实现规范完成某项业务功能,并嵌入到内核系统中。

img

显然,基于微内核架构,当面对系统中的某个组件需要进行修改时,我们要做的只是创建一个新的组件并替换旧组件,而不需要改变原有组件的实现方式,更不需要调整整个系统架构。

img

那么这里的插件具体指的是什么呢?

这就需要我们引入一个概念,即 SPI(Service Provider Interface),也就是服务提供接口。你可以理解为,SPI 就是应对系统扩展性的一个个扩展点,也是我们对系统中所应具备的扩展性的抽象。

插件化实现机制说起来简单,做起来可不容易。我们要考虑两方面内容。

一方面,我们要梳理系统的变化并把它们抽象成 SPI 扩展点。另一方面,当我们实现了这些 SPI 扩展点之后,就需要构建一个能够支持这种可插拔机制的具体实现,从而提供一种 SPI 运行时环境。

img

那么接下来,我们就来一起讨论微内核架构的具体技术体系怎么搭建,进而实现我们需要的插件系统。

如何实现微内核架构?

微内核架构本质上只是为我们提供了一种架构模式,并没有规定具体的实现方式,所以原则上我们也可以提供一套满足自身要求的实现方案。但是,我们不想重复造轮子。

幸好,JDK 已经为我们提供了微内核架构的一种实现方式,这种实现方式针对如何设计和实现 SPI 提出了一些开发和配置上的规范。

对于 SPI 的实现者而言,具体有三个步骤。

img

对于 SPI 而言,我们需要设计一个服务接口,然后根据业务场景提供不同的实现类。然后在 Java 代码工程的 META-INF/services 目录中创建一个以服务接口命名的文件,并配置对应的想要使用的实现类。在代码工程中执行这些步骤,最终我们得到了一个包含 SPI 类和配置的 jar 包。

而对于 SPI 的使用者,就可以通过 jar 包中 META-INF/services/ 目录下的配置文件找到具体的实现类名并进行实例化。

上图中的后面两个步骤实际上都是为了遵循 JDK 中 SPI 的实现机制而进行的配置工作。

接下来,我就通过简单的代码示例来给你演示一下这些步骤。

让我们来模拟这样一个应用场景,一般业务系统中都会涉及日志组件,我们希望对业界主流的日志工具做一层包装,以便支持日志组件的灵活应用。

那么,基于 SPI 的约定,我们将创建一个单独的工程 log-spi 来存放服务接口,并给出接口定义,请注意这个服务接口的完整类路径为 com.spi.LogProvider,接口中只包含一个记录 info 日志的简单示例方法。

package com.spi;

public interface LogProvider {
// 记录 Info 日志
public void info(String info);
}

假设系统在设计之初使用的是 log4j 日志库。然后我们需要实现这个服务接口,这里创建另一个代码工程 log-log4j 用来提供基于 log4j 日志库的实现,请注意,这个实现类的名称是 Log4jProvider。

public class Log4jProvider implements LogProvider{

@Override
public void info(String info){
System.out.println("Log4j:” + info);
}
}

接下来的这个步骤很关键,在这个代码工程的 META-INF/services/ 目录下需要创建一个以服务接口完整类路径命名的文件,文件的内容就是指向该接口所对应的实现类。显然,当前的这个实现类就是前面创建的 Log4jProvider。

img

最后,我们需要创建一个外部工程 log-consumer 来调用服务接口,首先需要将 log-log4j 所生成的 jar 包添加到这个外部工程中的类路径中。然后,我们使用 JDK 中 ServiceLoader 工具类来完成对 LogProvider 实例的加载。

在这里,我们通过 ServiceLoader.load 方法获取所有的 LogProvider 实例,然后遍历这些实例并调用服务接口方法。

import java.util.ServiceLoader;
import com.spi.LogProvider;

public class Main {
public static void main(String[] args) {

ServiceLoader<LogProvider> loader = ServiceLoader.load(LogProvider.class);

for (LogProvider provider : loader) {
System.out.println(provider.getClass());
provider.info(“testInfo”);
}
}
}

运行这段代码,我们会得到系统的输出。可以看到这里获取的是针对 log4j 的 Log4jProvider 类中的具体方法实现,表示整个 SPI 实例的加载过程是正常的。

class com.spi.Log4jProvider
Log4j:testInfo

请注意,在上面这个 Main 函数中,我们并没有引入任何与 Log4jProvider 相关的包结构,但在运行过程中,却实现了对 Log4jProvider 类的动态调用,这是怎么回事呢?

这就是微内核架构的核心特征,对组件之间的依赖关系进行了解耦,从而确保了系统的扩展性。

现在,我们再来考虑这样一种场景。随着工具的更新或者架构的调整,我们需要提供一套基于 logback 日志库的日志实现来替换现有的基于 log4j 的方案。

当然,我们可以重构原有代码来达成这个目标。但基于扩展性考虑,更好的办法是提供另一个 SPI 实例。这样,我们就可以创建新的代码工程 log-logback,并完成对 LogProvider SPI 接口的实现。请注意,这次的实现类的名称是 LogbackProvider。

public class LogbackProvider implements LogProvider {

@Override
public void info(String info){
System.out.println("Logback:” + info);
}
}

同样,我们需要在这个代码工程的 META-INF/services/ 目录下创建一个配置文件,并指向新创建的 LogbackProvider 类。

img

接下来我们把 log-logback 所生成的 jar 包也添加到外部工程的类路径中,完成这些操作之后,我们再来执行前面的 main 函数,得到的就是来自 Log4jProvider 和 LogbackProvider 类中的输出结果。

class com.spi.Log4jProvider
Log4j:testInfo
class com.spi.LogbackProvider
Logback:testInfo

当然,你可以根据需要提供任何 LogProvider 接口的实现类并动态集成到系统的执行流程中。

不过要注意的是,无论是添加、替换还是移除具体的 SPI 实现,对于原有的 log-consumer 工程而言,我们并没有做任何的代码调整。

这就满足了我们在开篇时提到的扩展性设计理念,就是在现有系统中添加新的组件时,不会对现有系统造成太大的影响。

总结

以上就是这节课的全部内容了。

我们明确了微内核架构模式是实现系统扩展性的有效手段,在 Dubbo、ShardingSphere、Skywalking 等主流的开源框架中都有广泛应用。

而 JDK 提供的 SPI 机制以及 ServiceLoader 工具类为我们实现微内核架构提供了技术方案,帮助我们更好地把这一架构模式应用到了日常开发工作中。

最后,我想给你留一道思考题:想要使用 JDK 中提供的 SPI 机制来实现微内核架构,开发人员需要完成哪些步骤呢?

参考文章:
整理于极客时间每日一课对应文章

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Apple_Web

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值