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

本文深入剖析Dubbo注册中心的缓存机制,包括内存和磁盘缓存的实现,以及重试机制的详情。讨论了FailbackRegistry的抽象模板方法,用于注册、订阅等操作的重试。此外,还探讨了注册中心设计模式,如模板模式和工厂模式的应用,展示了Dubbo扩展性的核心。
摘要由CSDN通过智能技术生成

缓存机制

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

68f237262021ff94b755b1a34db721af.png

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

2d416bb3c460aab6399bcd73486f6261.png

内存中的缓存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所示。

5382266e9a98dc22c50f54da7fe3b0d3.png

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

重试机制

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

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

83b0c8c473c5f413f3cd029cc8cb3d4b.png

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

设计模式

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

模板模式

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

0030e33d0379804211391c9d75e71145.png

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); 

工厂模式

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

42370ae1286cf220448c73dc7761ac62.png

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

0dcac8ff57078746f47e35b802008855.png

虽然每种注册中心都有自己具体的工厂类,但是在什么地方判断,应该调用哪个工厂类实现呢?代码中并没有看到显式的判断。答案就在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加载机制中讲解。

09b7915a5c79978d736e64afc292346b.png

小结

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

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

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

觉得文章不错的话,可以转发关注一下小编!!!

下一篇,我们会详细探讨Dubbo SPI扩展点加载的原理。

9e8cc51dfe1873ac26cab4ce213903e5.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值