缓存机制

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

dubbo 在本地缓存连接 dubbo本地缓存更新_注册中心

消费者或服务治理中心获取注册信息后会做本地缓存。内存中会有一份,保存在Properties对象里,磁盘上也会持久化一份文件,通过file对象引用。

在AbstractRegistry抽象类中有如下定义:

dubbo 在本地缓存连接 dubbo本地缓存更新_注册中心_02


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

缓存的加载

在服务初始化的时候,AbstractRegistry构造函数里会从本地磁盘文件中把持久化的注册数据读到Properties对象里,,并加载到内存缓存中:

dubbo 在本地缓存连接 dubbo本地缓存更新_dubbo 在本地缓存连接_03

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

缓存的保存与更新

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

dubbo 在本地缓存连接 dubbo本地缓存更新_dubbo 在本地缓存连接_04

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

dubbo 在本地缓存连接 dubbo本地缓存更新_dubbo 在本地缓存连接_05

重试机制

com.alibaba.dubbo.registry.support.FailbackRegistry继承了AbstractRegistry,并在此基础上增加了失败重试机制作为抽象能力。ZookeeperRegistryRedisRegistry继承该抽象方法之后,直接使用即可。

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

结合名称

集合介绍

Set failedRegistered

发起注册失败的URL集合

Set failedUnregistered

取消注册失败的URL集合

ConcurrentMap>failedSubscribed

发起订阅失败的监听器集合

ConcurrentMap>failedUnsubscribed

取消订阅失败的监听器集合

ConcurrentMap>>failedNotified

通知失败的URL集合

dubbo 在本地缓存连接 dubbo本地缓存更新_dubbo 在本地缓存连接_06

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

注册中心应用的设计模式

模板方法:

整个注册中心的逻辑部分使用了模板模式:

dubbo 在本地缓存连接 dubbo本地缓存更新_注册中心_07

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

工厂模式:

所有的注册中心实现,都是通过对应的工厂创建的。

dubbo 在本地缓存连接 dubbo本地缓存更新_模板方法_08


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

dubbo 在本地缓存连接 dubbo本地缓存更新_Dubbo_09

虽然每种注册中心都有自己的具体工厂类,但是在什么地方判断,应该调用哪个工厂类实现呢?

答案就在RegistryFactory接口中,该接口里有个Registry getRegistry(URL url)方法,该方法上有@Adaptive({"protocol"})注解:

dubbo 在本地缓存连接 dubbo本地缓存更新_dubbo 在本地缓存连接_10

这个注解会自动生成代码实现一些逻辑,它的value参数会从URL中获取protocol键的值,并根据获取的值来调用不同的工厂类。

例如:当url.protocol=redis时,获得RedisRegistryFactory实现类。