缓存机制
缓存的存在就是用空间换取时间,如果每次远程调用都要从注册中心获取一次可调用的服务列表,则会让注册中心承受巨大的流量压力。另外,每次额外的网络请求也会让整个系统的性能下降。因此,Dubbo的注册中心实现了通用的缓存机制,在抽象类AbstractRegistry中实现。
消费者或服务治理中心获取注册信息后会做本地缓存。内存中会有一份,保存在Properties对象里,磁盘上也会持久化一份文件,通过file对象引用。
在AbstractRegistry抽象类中有如下定义:
内存中的缓存notified是ConcurrentHashMap里面又嵌套了一个Map,外层Map的key是消费者的URL
,内层Map的key是分类
,包含providers、consumers、coutes、configurators四种。value则是对应的服务列表,对于没有服务提供者提供服务的URL,它会以特殊的empty://
前缀开头。
缓存的加载
在服务初始化的时候,AbstractRegistry构造函数里会从本地磁盘文件中把持久化的注册数据读到Properties对象里,,并加载到内存缓存中:
Properties保存了所有服务提供者的URL,使用URL#serviceKey()
作为key,提供者列表、路由规则列表、配置规则列表等作为value。由于value是列表,当存在多个的时候使用空格隔开。还有一个特殊的key.registies
,保存所有的注册中心的地址。如果应用在启动过程中,注册中心无法连接或宕机,则Dubbo框架会自动通过本地缓存加载Invokers。
缓存的保存与更新
缓存的保存有同步和异步两种方式。异步会使用线程池异步保存,如果线程在执行过程中出现异常,则会再次调用线程池不断重试:
AbstractRegistry#notify
方法中封装了更新内存缓存和更新文件缓存的逻辑。当客户端第一次订阅获取全量数据,或者后续由于订阅得到新数据时,都会调用该方法进行保存。
重试机制
com.alibaba.dubbo.registry.support.FailbackRegistry
继承了AbstractRegistry
,并在此基础上增加了失败重试机制作为抽象能力。ZookeeperRegistry
和RedisRegistry
继承该抽象方法之后,直接使用即可。
FailbackRegistry抽象类中定义了一个ScheduledExecutorService,每经过固定间隔(默认5秒
)调用FailbackRegistry#retry()
方法。另外,该抽象类中还有五个比较重要的集合:
结合名称 | 集合介绍 |
Set failedRegistered | 发起注册失败的URL集合 |
Set failedUnregistered | 取消注册失败的URL集合 |
ConcurrentMap>failedSubscribed | 发起订阅失败的监听器集合 |
ConcurrentMap>failedUnsubscribed | 取消订阅失败的监听器集合 |
ConcurrentMap>>failedNotified | 通知失败的URL集合 |
在定时器中调用retyr方法的时候,会把这五个集合分别遍历和重试,重试成功则从集合中移除。FailbackRegistry实现sbscribe、unsubscribe等通用方法,里面调用额未实现的模板方法,会由子类实现。通用方法会调用这些模板方法,如果捕获到异常,则会把URL添加到对应的重试结合中,以供定时器去重试。
注册中心应用的设计模式
模板方法:
整个注册中心的逻辑部分使用了模板模式:
AbstractRegistry实现了Registry接口中的注册、订阅、查询、通知等方法,还实现了磁盘文件持久化注册信息这一通用方法。但是注册、订阅、查询、通知等方法只是简单地把URL加入对应的集合,没有具体的注册或订阅逻辑。
FailbackRegistry又继承了AbstractRegistry,重写了父类的注册、订阅、查询和通知等方法,并且添加了重试机制。此外,还添加了四个未实现的抽象模板方法:
protected abstract void doRegister(URL url);
protected abstract void doUnregister(URL url);
protected abstract void doSubscribe(URL url, NtifyListener listener);
protected abstract void doUnsubscribe(URL url, NotifyListener listener);
工厂模式:
所有的注册中心实现,都是通过对应的工厂创建的。
AbstractRegistryFactory实现了RegistryFactory接口的getRegistry(URL url)方法,是一个通用的实现,主要完成了加锁,以及调用抽象模板方法,createRegistry(URL url)
创建具体实现等操作,并缓存在内存中。抽象模板方法会由具体子类继承并实现:
虽然每种注册中心都有自己的具体工厂类,但是在什么地方判断,应该调用哪个工厂类实现呢?
答案就在RegistryFactory接口中,该接口里有个Registry getRegistry(URL url)
方法,该方法上有@Adaptive({"protocol"})
注解:
这个注解会自动生成代码实现一些逻辑,它的value参数会从URL中获取protocol键的值,并根据获取的值来调用不同的工厂类。
例如:当url.protocol=redis
时,获得RedisRegistryFactory实现类。