大数据架构师带你深入Dubbo注册中心:缓存+重试机制+设计模式

本文详细阐述了Dubbo中的缓存机制,包括内存和磁盘缓存的使用,以及注册中心如何通过缓存优化性能。讨论了重试机制和失败备份策略,以及注册中心采用的模板模式和工厂模式实现扩展性。重点关注了ZooKeeper和Redis注册中心的实现和它们在设计中的角色。
摘要由CSDN通过智能技术生成

缓存机制

缓存的存在就是用空间换取时间,如果每次远程调用都要先从注册中心获取一次可调用的服务列表,则会让注册中心承受巨大的流量压力。另外,每次额外的网络请求也会让整个系统的性能下降。因此Dubbo的注册中心实现了通用的缓存机制,在抽象类AbstractRegistry中实现。AbstractRegistry类结构关系如图3-5所示。

消费者或服务治理中心获取注册信息后会做本地缓存。内存中会有一份,保存在Properties对象里,磁盘上也会持久化一份文件,通过file对象引用。在AbstractRegistry抽象类中有如下定义,如代码清单3-10所示。

内存中的缓存notified是ConcurrentHashMap里面又嵌套了一个Map,外层Map的key是消费者的 URL,内层 Map 的 key 是分类,包含 providers> consumers> routes> configurators四种。value则是对应的服务列表,对于没有服务提供者提供服务的URL,它会以特殊的empty://前缀开头。

缓存的加载

在服务初始化的时候,AbstractRegistry构造函数里会从本地磁盘文件中把持久化的注册数据读到Properties对象里,并加载到内存缓存中,如代码清单3-11所示。

代码清单3-11 Properties缓存初始化

private void loadProperties() {
if (file != null && file.exists。)(
InputStream in = null;
try {
in = new FilelnputStream(file);
properties.load(in);
}
} catch (Throwable e) (
) finally (
}
}
}

Properties保存了所有服务提供者的URL,使用URL#serviceKey()作为key,提供者列表、路由规则列表、配置规则列表等作为value。由于value是列表,当存在多个的时候使用空格隔开。还有一个特殊的key.registies,保存所有的注册中心的地址。如果应用在启动过程中,注册中心无法连接或宕机,则Dubbo框架会自动通过本地缓存加载Invokerso

缓存的保存与更新

缓存的保存有同步和异步两种方式。异步会使用线程池异步保存,如果线程在执行过程中出现异常,则会再次调用线程池不断重试,如代码清单3.12所示。

AbstractRegistry#notify方法中封装了更新内存缓存和更新文件缓存的逻辑。当客户端第一次订阅获取全量数据,或者后续由于订阅得到新数据时,都会调用该方法进行保存。

重试机制

由图 3-5 我们可以得知
com.alibaba.dubbo.registry.support.FailbackRegistry 继承了AbstractRegistry,并在此基础上增加了失败重试机制作为抽象能力。ZookeeperRegistry和RedisRegistry继承该抽象方法后,直接使用即可。

FailbackRegistry抽象类中定义了一个ScheduledExecutorService,每经过固定间隔(默认为5秒)调用FailbackRegistry#retry()方法。另外,该抽象类中还有五个比较重要的集合,如表3-3所示。

在定时器中调用retry方法的时候,会把这五个集合分别遍历和重试,重试成功则从集合中移除。FailbackRegistry实现了 subscribe> unsubscribe等通用方法,里面调用了未实现的模板方法,会由子类实现。通用方法会调用这些模板方法,如果捕获到异常,则会把URL添加到对应的重试集合中,以供定时器去重试。

设计模式

Dubbo注册中心拥有良好的扩展性,用户可以在其基础上,快速开发出符合自己业务需求的注册中心。这种扩展性和Dubbo中使用的设计模式密不可分,本节将介绍注册中心模块使用的设计模式。学习完本节后,能降低读者对注册中心源码阅读的门槛。

模板模式

整个注册中心的逻辑部分使用了模板模式,其类的关系如图3-6所示。

AbstractRegistry实现了 Registry接口中的注册、订阅、查询、通知等方法,还实现了磁盘文件持久化注册信息这一通用方法。但是注册、订阅、查询、通知等方法只是简单地把URL加入对应的集合,没有具体的注册或订阅逻辑。

FailbackRegistry又继承了 AbstractRegistry,重写了父类的注册、订阅、查询和通知等方法,并且添加了重试机制。此外,还添加了四个未实现的抽象模板方法,如代码清单3-13所示。

代码清单3-13未实现的抽象模板方法

protected abstract void doRegister(URL url);
protected abstract void dollnregister(URL url);
protected abstract void doSubscribe(URL url. NotifyListener listener);
protected abstract void doUnsubscribe(URL url. NotifyListener listener);

以订阅为例,FailbackRegistry重写了 subscribe方法,但只实现了订阅的大体逻辑及异常处理等通用性的东西。具体如何订阅,交给继承的子类实现。这就是模板模式的具体实现,如代码清单3.14所示。

代码清单3-14模板模式调用

public void subscribe(URL url, NotifyListener listener) (
super.subscribe(urllistener);
removeFailedSubscribed(url> listener);
try (
doSubscribe(url, listener); <一此处调用了模板方法,由子类自行实现
} catch (Exception e) (

工厂模式

所有的注册中心实现,都是通过对应的工厂创建的。工厂类之间的关系如图3.7所示。

AbstractRegistryFactory 实现了 RegistryFactory 接口的 getRegistry(URL url)方法,是一个通用实现,主要完成了加锁,以及调用抽象模板方法createRegistry(URL url)创建具体实现等操作,并缓存在内存中。抽象模板方法会由具体子类继承并实现,如代码清单3-15所示。

虽然每种注册中心都有自己具体的工厂类,但是在什么地方判断,应该调用哪个工厂类实现呢?代码中并没有看到显式的判断。答案就在RegistryFactory接口中,该接口里有一个Registry getRegistry(URL url)方法,该方法上有@Adaptive({"protocol"))注解,如代码清单3-16所示。

代码清单 3-16 RegistryFactory 源码
@SPI("dubbo")
public interface RegistryFactory (
@Adaptive({"protocol"})
Registry getRegistry(URL url);
)

了解AOP的读者就会很容易理解,这个注解会自动生成代码实现一些逻辑,它的value参数会从URL中获取protocol键的值,并根据获取的值来调用不同的工厂类。例如,当url.protocol = redis时,获得RedisRegistryFactory实现类。具体Adaptive注解的实现原理会在第4章Dubbo加载机制中讲解。

小结

本章介绍了 Dubbo中已经支持的注册中心。重点介绍了 ZooKeeper和Redis两种注册中心。

讲解了两种注册中心的数据结构,以及订阅发布机制的具体实现。

然后介绍了注册中心中一些通用的关键特性,如数据缓存、重试等机制。最后,在对各种机制己经了解的前提下,讲解了整个注册中心源码的设计模式。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值