大致流程
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
通过动态代理封装得到代理对象返回。
以上就是java开发分布式服务框架Dubbo服务引用详解的详细内容,更多关于Dubbo服务引用的资料请关注编程网其它相关文章!