开源c语言微内核源码及详解,ShardingSphere源码解析之微内核架构(下)

通过上一篇的介绍,我们对微内核架构模式以及JDK所提供的SPI机制有了一定的了解。在此基础上,今天我们就来深入分析ShardingSphere中所用到的微内核架构的实现原理。

1. ShardingSphere中的微内核架构基础实现机制

我们发现,在ShardingSphere源码的根目录下,存在一个独立的工程shardingsphere-spi。显然,从命名上看,这个工程中应该包含了ShardingSphere实现SPI的相关代码。我们快速浏览该工程,发现里面只有一个接口定义和两个工具类。

我们先来看这个接口定义TypeBasedSPI,如下所示:

public interface TypeBasedSPI {

String getType();

Properties getProperties();

void setProperties(Properties properties);

}

从定位上讲,这个接口在ShardingSphere中应该是一个顶层接口,它的类层结构如下所示,显然,我们在上一篇提到的用于生成分布式主键的ShardingKeyGenerator、用于数据脱敏的ShardingEncryptor、用于分布式事务的ShardingTransactionManager以及用于数据库治理的注册中心接口RegistryCenter都继承了该接口:

d19a5a85e4a54cbf5cbd2869b439be0b.png

然后,我们再来看NewInstanceServiceLoader类,从命名上看,我们也不难想象该类的作用类似于一种ServiceLoader,用于加载新的目标对象实例。NewInstanceServiceLoader代码如下所示:

public final class NewInstanceServiceLoader {

private static final Map>> SERVICE_MAP = new HashMap<>();

//通过ServiceLoader获取新的SPI服务实例并注册到SERVICE_MAP中

public static void register(final Class service) {

for (T each : ServiceLoader.load(service)) {

registerServiceClass(service, each);

}

}

@SuppressWarnings("unchecked")

private static void registerServiceClass(final Class service, final T instance) {

Collection> serviceClasses = SERVICE_MAP.get(service);

if (null == serviceClasses) {

serviceClasses = new LinkedHashSet<>();

}

serviceClasses.add(instance.getClass());

SERVICE_MAP.put(service, serviceClasses);

}

@SneakyThrows

@SuppressWarnings("unchecked")

public static Collection newServiceInstances(final Class service) {

Collection result = new LinkedList<>();

if (null == SERVICE_MAP.get(service)) {

return result;

}

for (Class> each : SERVICE_MAP.get(service)) {

result.add((T) each.newInstance());

}

return result;

}

}

这里首先看到了熟悉的ServiceLoader.load(service)方法,这是上一篇中介绍的JDK ServiceLoader工具类的具体应用。同时,我们注意到ShardingSphere使用了一个HashMap来保存类的定义以及类的实例之前的一对多关系,可以认为这是一种用于提高访问效率的缓存机制。

最后,我们来看一下TypeBasedSPIServiceLoader的实现,该类依赖于前面介绍的NewInstanceServiceLoader类,如下所示的方法使用NewInstanceServiceLoader获取实例类列表,并根据所传入的类型做过滤:

//使用NewInstanceServiceLoader获取实例类列表,并根据类型做过滤

private Collection loadTypeBasedServices(final String type) {

return Collections2.filter(NewInstanceServiceLoader.newServiceInstances(classType), new Predicate() {

@Override

public boolean apply(final T input) {

return type.equalsIgnoreCase(input.getType());

}

});

}

TypeBasedSPIServiceLoader对外暴露服务的接口如下所示,对通过loadTypeBasedServices方法获取的服务实例设置对应的属性然后返回:

//基于类型通过SPI创建实例

public final T newService(final String type, final Properties props) {

Collection typeBasedServices = loadTypeBasedServices(type);

if (typeBasedServices.isEmpty()) {

throw new RuntimeException(String.format("Invalid `%s` SPI type `%s`.", classType.getName(), type));

}

T result = typeBasedServices.iterator().next();

result.setProperties(props);

return result;

}

同时,TypeBasedSPIServiceLoader也对外暴露了不需要传入类型的newService方法,该方法使用了loadFirstTypeBasedService工具方法来获取第一个服务实例,相关代码如下所示:

//基于默认类型通过SPI创建实例

public final T newService() {

T result = loadFirstTypeBasedService();

result.setProperties(new Properties());

return result;

}

private T loadFirstTypeBasedService() {

Collection instances = NewInstanceServiceLoader.newServiceInstances(classType);

if (instances.isEmpty()) {

throw new RuntimeException(String.format("Invalid `%s` SPI, no implementation class load from SPI.", classType.getName()));

}

return instances.iterator().next();

}

这样,ShardingSphere的shardingsphere-spi工程中的内容就介绍完毕。这部分内容相当于是微内核架构的基础实现机制,下面我们来找一个典型的应用场景来看一下具体的使用方法。

2. 微内核架构在ShardingSphere中的应用

我们回顾《ShardingSphere源码解析之SQL解析引擎(二)》中的内容,讲到具体的SQLParser的生成由SQLParserFactory负责,SQLParserFactory定义如下:

public final class SQLParserFactory {

public static SQLParser newInstance(final String databaseTypeName, final String sql) {

//通过SPI机制加载所有扩展

for (SQLParserEntry each : NewInstanceServiceLoader.newServiceInstances(SQLParserEntry.class)) {

}

可以看到,这里并没有使用前面介绍的TypeBasedSPIServiceLoader来加载实例,而是直接使用了更为底层的NewInstanceServiceLoader。

这里引入的SQLParserEntry接口就位于shardingsphere-sql-parser-spi 工程的org.apache.shardingsphere.sql.parser.spi包中。显然,从包的命名上看,该接口是一个SPI接口,定义如下:

public interface SQLParserEntry {

String getDatabaseTypeName();

Class extends Lexer> getLexerClass();

Class extends SQLParser> getParserClass();

}

SQLParserEntry接口有一批实现,其类层结构如下所示:

18c3e8a089cb0c6c938105488c15de36.png

我们先来看针对Mysql的代码工程shardingsphere-sql-parser-mysql,我们在META-INF/services目录下找到了一个org.apache.shardingsphere.sql.parser.spi.SQLParserEntry文件,如下所示:

7dde24cef127ab614d7f21c709bfe7f3.png

可以看到这里指向了org.apache.shardingsphere.sql.parser.MySQLParserEntry类。然后我们再来到Oracle的代码工程shardingsphere-sql-parser-oracle,在META-INF/services目录下同样找到了一个org.apache.shardingsphere.sql.parser.spi.SQLParserEntry文件,如下所示:

e18aa255c8df1ce1a6c938a218c04585.png

显然,这里应该指向org.apache.shardingsphere.sql.parser.OracleParserEntry类。通过这种方式,系统在运行时就会根据类路径动态加载SPI。

我们注意到SQLParserEntry接口的类层结构中实际上并没有使用到TypeBasedSPI,而是完全基于JDK原生的SPI机制。接下来,我们来找一个使用TypeBasedSPI的示例,比方说ConfigCenter(位于org.apache.shardingsphere.orchestration.config.api包下),该接口的声明如下所示:

public interface ConfigCenter extends TypeBasedSPI

可以看到ConfigCenter接口继承了TypeBasedSPI接口,而在ShardingSphere中也存在一批ConfigCenter接口的实现,其类层结构如下所示:

4fb058d44083ac5a41891910ffc828c9.png

以ApolloConfigCenter为例,我们来看来它的使用方法,我们在sharding-orchestration-core工程的org.apache.shardingsphere.orchestration.internal.configcenter中找到ConfigCenterServiceLoader类,该类扩展了前面提到的TypeBasedSPIServiceLoader类:

public final class ConfigCenterServiceLoader extends TypeBasedSPIServiceLoader {

static {

NewInstanceServiceLoader.register(ConfigCenter.class);

}

public ConfigCenterServiceLoader() {

super(ConfigCenter.class);

}

//基于SPI加载ConfigCenter

public ConfigCenter load(final ConfigCenterConfiguration configCenterConfig) {

Preconditions.checkNotNull(configCenterConfig, "Config center configuration cannot be null.");

ConfigCenter result = newService(configCenterConfig.getType(), configCenterConfig.getProperties());

result.init(configCenterConfig);

return result;

}

}

首先,在ConfigCenterServiceLoader类中通过NewInstanceServiceLoader.register(ConfigCenter.class)语句将所有ConfigCenter注册到系统中,这一步会通过JDK的ServiceLoader工具类加载类路径中的所有ConfigCenter实例。

然后,我们可以看到上面的load方法中,通过父类TypeBasedSPIServiceLoader的newService方法基于类型创建了SPI实例。

不难想象,在sharding-orchestration-config-apollo工程的META-INF/services目录下应该存在一个名为org.apache.shardingsphere.orchestration.config.api.ConfigCenter的配置文件,指向ApolloConfigCenter类,如下所示:

e6afa6b16c0963d6a61a95de636d8040.png

其他的ConfigCenter实现也是一样,大家可以自行查阅sharding-orchestration-config-zookeeper-curator等工程中的SPI配置文件。

至此,我们对ShardingSphere中的微内核架构有了一个全面的了解。实际上,相较Dubbo等框架,ShardingSphere中微内核架构的实现并不复杂,基本就是对JDK中SPI机制的封装。

更多内容可以关注我的公众号:程序员向架构师转型。

2d33dd1edbc0c2d5535059ea4818d832.png

3bc44d77a31db6bd90f99fb0d76bd55d.png

天涯兰的博客

博客专家

发布了94 篇原创文章 · 获赞 9 · 访问量 11万+

私信

关注

标签:ShardingSphere,public,final,SPI,源码,result,微内核,shardingsphere,ConfigCenter

来源: https://blog.csdn.net/lantian08251/article/details/104511053

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值