业务程序员在各种需求与业务压力的驱动下,非常容易沦为crud if else选手。
解脱之法就是多看别人的优秀代码,取其精华,知行合一,把自己的代码写优秀。
本文所有的代码均源自于dubbo 2.7.5 版本
工厂方法
Dubbo支持Service粒度的多注册中心,早期大家最常用的是zookeeper,后续又有redis,nacos,etcd等等,还在持续扩展中,不同注册中心,关于网络初始化,服务注册,服务摘除,订阅等,背后的处理逻辑都不同。
那么dubbo是如何实现的呢?见下图
dubbo使用了工厂方法模式。对于不同的注册中心实例,由其对应的Factory工厂类来负责创建。dubbo启动时,根据配置文件中指定的注册中心种类,基于[dubbo spi机制] (https://www.jianshu.com/p/d4d7ebc8f7bb),进行RegistryFactory的初始化,以及对应Registry的创建。
以zookeeper注册中心为例,配置见下
dubbo.registry.address=zookeeper://${zookeeper.addres}:2181
启动时,将配置加载为Url对象后,protocal即为zookeeper,dubbo spi会根据META-INF文件夹下
org.apache.dubbo.registry文件中配置的实现类,去找到对应的工厂实现,进行初始化。
zookeeper=org.apache.dubbo.registry.zookeeper.ZookeeperRegistryFactory
redis=org.apache.dubbo.registry.redis.RedisRegistryFactory
nacos=org.apache.dubbo.registry.nacos.NacosRegistryFactory
...
当需要拓展新的注册中心时,只需要按照规范去新增Registry实现,对应的工厂RegistryFactory实现,配置文件增加一条记录即可,原有代码可以做到一行都不改,完美满足开闭原则。
模板方法
不同的注册中心工厂,他们初始化Registry的逻辑不同,但是创建完毕后,存储Registry的数据结构,获取Registry,清除Registry等的流程和逻辑是相同的。dubbo在此处使用了模板方法模式,所有的工厂去继承AbstractRegistryFactory,复用父类的数据结构和其他的公有逻辑,自己仅实现createRegistry()的个性化部分。见下图
模板方法在技术上满足开闭原则,在业务上,可以把流程规范化,把风险控制在最小范围,很多时候,我们接手一个历史项目的时候,如果面对堆砌出来的代码逻辑,我们一般是不太敢动的,风险实在不可控,如果是基于模板方法设计的逻辑,那么我们其实是有底的,风险和测试边界都是相对可控的。
代理模式
看起来dubbo注册中心的初始化,就是通过RegistryFactory生产Registry的过程,但是dubbo在这里预埋了一批钩子,以支持Registry在register,unregister,subscribe,unsubscribe等事件发生的时候,触发一些操作(RegistryServiceListener),实现方式是代理模式(动态代理),见下图
标红部分,引入了RegistryFactoryWrapper和ListenerRegistryWrapper两个代理类。
其中RegistryFactoryWrapper这个代理类,代理逻辑即通过SPI初始化RegistryServiceListener,然后把这些RegistryServiceListener塞到ListenerRegistryWrapper中。
public Registry getRegistry(URL url) {
return new ListenerRegistryWrapper(registryFactory.getRegistry(url),
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(RegistryServiceListener.class)
.getActivateExtension(url, "registry.listeners")));
}
ListenerRegistryWrapper代理类,主要的代理逻辑也很简单,遍历RegistryServiceListener,执行对应的事件监听方法。
public void register(URL url) {
try {
registry.register(url);
} finally {
if (CollectionUtils.isNotEmpty(listeners)) {
RuntimeException exception = null;
for (RegistryServiceListener listener : listeners) {
if (listener != null) {
try {
listener.onRegister(url);
} catch (RuntimeException t) {
logger.error(t.getMessage(), t);
exception = t;
}
}
}
if (exception != null) {
throw exception;
}
}
}
那么饶了这么一大圈,在Factory和Register这两层都加上代理之后,这个RegistryServiceListener都起了什么作用呢?答案是什么实际作用都没有,以2.7.5版本代码为例,代码并未初始化任何RegistryServiceListener的实现,这两层代理只是为了把执行Listener的路铺好,目前并未有任何车在上面跑,这里可以看到dubbo团队为了支撑扩展性,在这些细节处,也算是费劲心机了。