Dubbo服务引用
大致流程
Provider 将服务暴露出来并且注册到注册中心,而Consumer 通过注册中心获取Provider 的信息,之后将自己封装成一个调用类去与Provider 进行交互。
首先需要将所有调用转化为Dubbo中我们熟悉的Invoker ,再通过代理类去远程获取服务。
大致流程如下:
服务引用策略
服务的引用和服务的暴露原理相似,都是Spring 自定义标签机制解析生成对应的Bean,在之前服务暴露使用到的Provider Service 使用的是ServiceBean ,而Comsumer Reference 使用的ReferenceBean 。
服务暴露是在SpringIOC容器完成刷新后开始暴露的,而服务的引入则分为两种,分别是饿汉式 和懒汉式 。
饿汉式是通过实现Spring的InitializingBean 接口中的afterPropertiesSet() 实现的,通过上图中ReferenceBean 的实现也可以获知,而容器通过调用ReferenceBean 中的afterPropertiesSet() 时引入服务。
懒汉式是只有某个服务被注入到其他类时才开始启动引入流程。
默认情况下,Dubbo会使用懒汉式引入策略,如果需要使用饿汉式,需要在<dubbo:reference/> 中配置init 开启。
并且通过上图我们还可以看到ReferenceBean 还实现了FactoryBean ,Dubbo通过这个实现来进行懒汉式引用服务。
服务引用的三种方式
服务引用分为三种方式:
- 本地引入
- 直接使用连接引入远程服务
- 通过注册中心引入远程服务
本地引入的基础是之前介绍过的本地暴露,某个服务端可能同时是Provider 又是Comsumer ,并且可能会自己调用本地的服务,这种情况下不需要进行网络调用,所以引入了本地引用,来避免不必要的网络开销。
服务引入时,第一步做的就是在本地中查找是否有可用的本地服务。
直连引入服务,这种方式不需要注册中心的支持,绕过了注册中心,直接通过Url 来引用远程服务,具体实现是在Comsumer中写死Provider的具体地址,然后在调用时直接连接即可,一般用于测试,如果大量服务之间都使用直连引入,那么服务集群之间的关系将错综复杂,变得那以维护和管理。
注册中心引入服务,Comsumer通过注册中心获取Provider的相关信息,然后进行服务的引入,其中还涉及到多注册中心,服务集群时的负载均衡,以及容错机制。
服务引入流程解析
前提:服务引入选择默认的懒汉式引入策略并且使用注册中心引入服务。
服务引入的入口是ReferenceBean.getObject() :
然后会调用父类ReferenceConfig.get() :
进入init() 方法,前面的一大串代码就是做if else 判断的配置检查并且将配置放入map,构建完毕后的map如下:
再进入createProxy() 方法
如果是本地引入的话,会构建一个本地引入的URL然后进行服务的引入,即图示中的refprotocol.refer()
不是本地引入,那么就是远程引入了,接下来需要判断的是点对点直连provider 还是通过注册中心拿到provider 信息再进行连接。
以下是点对点直连:
以下是通过注册中心连接:
最终拼接出来的URL如下图所示:
方法最后通过代理封装invoker返回代理实现。
上图就是整个服务引用流程。
但是还有很多细节,比如如何从注册中心获得Provider,invoker内部结构是怎么样的?
在上面URL的截图中,我们可以看到此时的协议是registry,因此会调用RegistryPotocol.refer()
获取注册中心实例,之后调用doRefer()
RegistryDirectory 类是实现了NotifyListener 接口的,注册中心的监听功能就此而来。
public class RegistryDirectory<T> extends AbstractDirectory<T> implements NotifyListener {}
public interface NotifyListener {
void notify(List<URL> var1);
}
向注册中心注册自身信息之后,会向注册中心订阅providers ,configurators ,routers 节点信息。订阅之后RegistryDirectory 会收到这几个节点的信息,并触发DubboInvoker 的生成,即用于远程调用的invoker。
再通过cluster封装得到invoker,因此一个服务可能有多个提供者,最终在ProviderConsumerRegTable 记录这些信息,然后返回invoker。
拿到Provider 的信息之后就会通过监听触发DubboProtocol.refer()
重点在getClients() 方法中,这个方法是用于获取客户端实例的,实例类型为ExchangeClient[] ,底层依赖Netty 进行通讯,并且是默认共享连接的。
再进入initClient() 方法中看看初始化客户端的具体细节:
最后返回的(ExchangeClient) client 封装的是NettyClient
最后得到的Invoker如下,其中记录到了许多信息:
最后调用return proxyFactory.getProxy(this.invoker); 将代理对象返回。
以下是整个服务引用的流程图:
总结
首先是通过配置构建URL,再通过协议头自适应拓展得到具体的实现类进行服务引入,之后消费者向注册中心注册自己的信息,然后订阅相关信息,得到远程服务提供者的信息,最后通过NettyClient 进行连接通讯。
还会通过Directory 和Cluster 进行多个服务提供者的合并、屏蔽、容错以及负载均衡,最终将封装好的可执行体Invoker 通过动态代理封装得到代理对象返回。
以上。
整理不易,如果对你有用的话留个三连吧!
|