前言
刷新服务列表的原理
由于在服务消费方启动的时候监听了zk注册中心某个服务的providers节点,而服务提供方在上线或者下线的时候,都会往往该节点对应的写入或者剔除 服务信息
那么服务的某个提供方实例在上线,或者下线的时候都会以事件监听的方式通知到消费方,去根据最新的providers列表刷新自己本地的服务列表,以方便后续的服务调用。
- 服务上线的话,消费方则会把其加到可调用的服务列表中,纳入负载均衡的范围之内
- 服务下线的话,消费方则会把他从服务列表中剔除,下次则不会调用下线的服务实例
1. 服务列表存在哪
RegistryDirectory
服务消费方的服务列表都是存储在RegistryDirectory对象中,并且是一对一的关系。
服务列表的成员变量 :
// serviceUrl和invokerDelegate的映射关系
Map<String, Invoker<T>> urlInvokerMap
// 方法名称和invoker的关系
Map<String, List<Invoker<T>>> methodInvokerMap
并且实现NotifyListener接口
同时这个类的对象也是一个监听器,监听了某服务下providers,configurators,routers的变化。
那么当远程服务列表providers节点发生改变的时候,则会调用到RegistryDirectory的notify方法去更新本地内存的服务列表。
1. 服务列表的初始化
当消费方启动的时候就会先获取到远程的服务列表,后续服务列表发生变更将会以事件通知的形式通知消费端更新服务列表。
1.1 注册监听后,会获取被监听节点的children子节点,并手动调用一次监听器的notify方法
获取到providers,configurators,routers的所有url
先根据category进行分组,依次category分组后的Url调用监听器的notify,这里的监听器类是RegistryDirectory类
我们先只关注providers节点的url,调进来会先判断category,放到invokerUls中
刷新Invokers,就是刷新服务列表
先把旧的服务列表存起来,再把传进来的url缓存起来
如果你把远程的providers都清空了,进到方法里传进来的invokerUrls就会为空,这个时候就会为了容错性不刷新服务列表,直接return
1.2 建立url和invoke的映射关系
循环providers url
1.2.1 合并url
接下来紧接的就是合并url,把provider的url与本机的配置信息进行合并,如果有相同的配置以consumer的配置优先
合并后的url还是要经过configurator配置的url的配置覆盖
把一些不需要更改的配置的key移除掉
后面吧provider的url配置全部移除掉,加入本地consumer的配置
1.2.2 创建invoker对象
合并完url之后还要用protocol对象的provider Url对应的Invoker对象
由于这里的协议是个providers节点下的dubbo协议,所以这里经过spi,获取到的protocol对象是一个包装着dubboProtocal的包装类对象,经过层层修饰最终还是会调到dubboProtocol的refer方法
最终返回DubboInvoker对象,第三个参数getClients(url)会涉及到netty服务端的创建,用于与服务提供方进行网络通讯,后面会详细讲
1.2.3 建立url与invoker对象的映射
存到newUrlInvokerMap中,返回
如果有invokerUrls有多个相同的url,会有缓存,先从缓存中获取invoker,如果有就会直接根据key put,如果没有就会新创建invoker,所以最终每个不同的providers url就只会对应一个invoker对象
建立好newUrlInvokerMap之后,赋值给RegistryDirectory的urlInvokerMap成员变量
RegistryDirectory的urlInvokerMap属性
// Map<url, Invoker> cache service url toinvoker mapping.
private volatile Map<String, Invoker<T>> urlInvokerMap; // The initial value is null and the midway may be assigned to null, please use the local variable reference
1.2 建立服务中方法和invoke的映射关系
往往服务消费端对服务提供方的调用是由一个方法,然后走代理进行远程调用的逻辑的,所以同时还会有服务中方法和invoker的映射关系,以便快速的根据所调的方法找到对应的invoker,进行远程服务实例对应方法的调用。
循环urlInvokerMap的value集合,就是Invoker集合
获取invoker的url的methods的值,就是所有的接口方法,以 逗号隔开
-
建立映射关系,key是方法名称,value是invokeList,put到newMethodInvokerMap中
image
其中还要根据routers节点的配置,过滤掉一些Invokes,然后再put进去,具体逻过滤的逻辑后面细讲。
最后对每一个方法的invoker集合进行url的排序,最后重新put进去,并且放到不可改的list中,其他地方不能更改这个服务列表。最后返回
返回后赋值到RegistryDirectory的成员变量methodInvokerMap中
RegistryDirectory的methodInvokerMap属性
//缓存本地服务列表,建立method和invokers的映射关系
// Map<methodName, Invoker> cache service method to invokers mapping.
private volatile Map<String, List<Invoker<T>>> methodInvokerMap; // The initial value is null and the midway may be assigned to null, please use the local variable
1.3 销毁不可用的invoker方法
就是循环之前的服务列表,如果新的服务列表里不包含这个invoker对象,就掉invoker对象的destory进行销毁工作
2. 服务提供方上下线通知服务消费方更新服务列表
服务列表初始化时,是用注册providers节点的监听器时,获取到children(url)后手动调用监听器的notify方法
如果某个服务上线或者下线,更改了zk的providers节点数据,就会zk通知客户端,调用监听器的notify方法,并传入更新后的children(url),之后的逻辑和手动调用监听器的notify方法是一样的,根据urls重新构建
RegistryDirectory类的methodInvokerMap,urlInvokerMap。