Dubbo服务引入的过程
饿汉式就是加载完毕就会引入,懒汉式是只有当这个服务被注入到其他类中时启动引入流程(也就是说用到了才会开始服务引入),默认是懒汉式。
根据配置参数组装成 URL ,一般而言我们都会配置的注册中心
构建 RegistryDirectory向注册中心注册消费者的信息,并且订阅提供者、配置、路由等节点
得知提供者的信息之后会进入 Dubbo 协议的引入,会创建 Invoker
Invoker会包含 NettyClient,来进行远程通信
最后通过 Cluster 来包装 Invoker,默认是 FailoverCluster,最终返回代理类
源码解读
加载模式
启动加载@Reference注解信息生成ReferenceBean对象,添加后置处理器
ReferenceAnnotationBeanPostProcessor#doGetInjectedBean
�ReferenceBean.getObject() 懒汉式加载
�ReferenceBean.afterPropertiesSet()饿汉式加载
调用类封装细节
- 获取提供端地址。封装调用类,首先要知道提供者的地址,并且因为有多种类型的服务引入,得区分多种服务引入方式的服务地址。
- 获取传输协议。不同的提供者可能支持的传输协议不一致,因此需要获取传输协议类型。
- 根据获取到的配置信息创建调用类。SPI机制
- 封装代理类。为了避免造成代码侵入,不能让业务代码直接依赖框架封装的调用类,所以需要支持让业务代码可以直接依赖提供端。那么RPC框架就需要根据提供端和调用类封装一个代理类。
封装调用类
封装调用对象需要考虑做几个事情
- 先前基于注册中心引入服务的地址,并非最终发起调用的协议和地址,而是以注册中心地址为路径,提供端地址为参数组合。因此需要区分开来,封装真正的调用地址。并且registry协议并非真正传输协议,只是标识是注册中心引入,封装调用类还得替换成真正的传输协议,比如dubbo协议。
- 由于提供者有可能有多个提供者,因此需要考虑如何将多个提供者封装成一个调用者,发起调用时如何处理。
- 为了便于知道消费端消费情况,消费端也需要将消费的服务注册到注册中心。并且为了在提供者发生变动时收到通知,还需要订阅提供者的节点数据。
ReferenceConfig#createProxy
只有一个URL时直接通过协议类封装一个invoker对象,如果有多个URL(即多个服务提供者)则通过Cluster封装多个,后续基于集群容错策略做调用
Protocol#refer
- 将原本url为registry协议,修改成真正发起调用使用的协议,默认为dubbo。
- 连接注册中心,创建注册中心实例。
- 注册消费端地址。
- 订阅注册中心提供端和配置数据。
- 基于RegistryDirectory和cluster创建invoke对象返回。
- 创建invoker对象,并对其加工(在后续的服务调用中是以invoker对象为载体完成的)
- 建立与zookeeper的连接并订阅注册中心消息
- 初始化Netty Client并建立与服务提供方Netty Server的连接
- 返回代理对象以便服务调用时使用
当多注册中心和多提供者时,怎么选择注册中心和怎么选提供者流程是怎么设计?
dubbo基于多个注册中心url生成invoke集合,再通过StaticDirectory包装,cluster固定使用**AvailableCluster进行选择任意可用**的节点。获取到clusterInvoke之后,再根据cluster策略(服务引入配置)选择一个提供者。注册中心最大的作用是可以提高服务调用的可用性,某个提供者服务挂了之后,自动下线该提供者,避免调用到该提供者,或者流量增大服务无法应对,动态扩容提供者。那么如何在提供者变动时去更新invoke.
dubbo处理某个注册中心url时,返回的是通过RegistryDirectory与cluster创建的invoke,RegistryDirectory会监听注册中心的通知,动态更新提供者集合。dubbo基于RegistryDirectory订阅注册中心,订阅的时候会将当前RegistryDirectory作为监听器,当订阅的节点发生变动的时候就会通知RegistryDirectory更新invoke集合。
RegistryDirectory#notify创建代理类
默认代理方式支持Javassist和Jdk代理,可以通过引用标签配置proxy指定