本文转载自微信公众号「大鱼仙人」,作者大鱼仙人。转载本文请联系大鱼仙人公众号。
一.前言
前面两篇分别介绍了Dubbo的入门和Dubbo的服务暴露
- Dubbo起飞
- 面试官问我:解释一下Dubbo服务暴露
这篇我们要说的服务引用,服务引用是有两种情况的,也可以看做是两种时机,第一个是在Spring容器调用ReferenceBean的afterPropertiesSet方法时引用服务,第二个就是在ReferenceBean对应的服务被注入到其他类中时引用。这两个引用服务的时机区别在于,第一个是饿汉式的,第二个是懒汉式的
是不是一说饿汉和懒汉,大家顺便回忆了一波单例模式
默认情况下,Dubbo使用懒汉式引用服务。如果需要使用饿汉式,可通过配置
服务引入的三种方式:
第一种是引用本地 (JVM) 服务,上篇在服务暴露里面也说过了每个服务都会通过走injvm协议然后走本地的暴露,因为存在一个服务端和消费端是同一台机器上的情况,这样就直接走本地调用了,不需要走远程调用了,节省网络开销
第二是通过直连方式引用远程服务,这种在线上基本不会采用这种形式的,一般都是平时我们自己测试用,直接写死服务端的地址来调用
第三是通过注册中心引用远程服务,Consumer 通过注册中心得知 Provider 的相关信息,然后进行服务的引入
不管是哪种引用方式,最后都会得到一个 Invoker 实例。如果有多个注册中心,多个服务提供者,这个时候会得到一组 Invoker 实例,此时需要通过集群管理类 Cluster 将多个 Invoker 合并成一个实例。合并后的 Invoker 实例已经具备调用本地或远程服务的能力了
但是呢,开发者秉承不对用户业务代码侵入的原则,所以此时框架还需要通过代理工厂类ProxyFactory为服务接口生成代理类,并让代理类去调用Invoker逻辑,避免了Dubbo框架代码对业务代码的侵入
二.服务发现
dubbo的服务发现,就是通过从注册中心订阅服务提供者,并且组装成URL,然后通过URL创建出invoker来实现的
服务的引入和服务的暴露一样,也是通过 spring 自定义标签机制解析生成对应的 Bean,Provider Service 对应解析的是 ServiceBean 而 Consumer Reference 对应的是 ReferenceBean
dubbo 的服务发现,是通过从注册中心订阅服务提供者组装成 URL,然后通过 URL 创建出 Invoker 来实现的。
这里是入口,进去看ReferenceBean
createLazyProxy中我们会看到DubboReferenceLazyInitTargetSource这一目标资源,点进来new的地方,里面的protected的方法createObject调用了getCallProxy,而这个方法最终调用的是referenceConfig.get()
点进来,我们进入的是ReferenceConfig类的方法,而非ReferenceBean的,这是因为ReferenceConfig是作为ReferenceBean的内部的属性出现的
Init()方法内部主要就是通过Map设置各种参数,我们看init其中的一个方法叫做createProxy,我们根据名字也可以知道大概意思就是创建代理对象,点进去看看
如果是走本地的话,那么直接构建个本地协议的 URL 然后进行服务的引入,即 refprotocol.refer,这个方法之后会做分析,本地的引入就不深入了,就是去之前服务暴露的 exporterMap 拿到服务
如果不是本地,那肯定是远程了,接下来就是判断是点对点直连 provider 还是通过注册中心拿到 provider 信息再连接 provider 了,我们分析一下配置了 url 的情况,如果配置了 url 那么不是直连的地址,就是注册中心的地址
这其实就是整个流程了,简述一下就是先检查配置,通过配置构建一个 map ,然后利用 map 来构建 URL ,再通过 URL 上的协议利用自适应扩展机制调用对应的 protocol.refer 得到相应的 invoker
我给大家总结个流程图,这样大家看着更加清晰
三.服务引用
Dubbo 的服务引用,实际上是为引用的接口创建一个 Proxy,这个 Proxy 的功能就是去执行 refprotocol.refer(interfaceClass, url) 创建出来的 Invoker。当服务提供者有多个时,就创建一个 ClusterInvoker。
Cluster 是一个 SPI 扩展点,默认使用com.alibaba.dubbo.rpc.cluster.support.FailoverCluster
所以,Consumer 端服务调用的逻辑被封装在 refprotocol.refer(interfaceClass, url) 创建出来的 Invoker 上
主要就是获取注册中心实例,然后调用 doRefer 进行真正的 refer。
这里会向注册中心注册自身的信息,生成一个Invoker,底层生成用于远程调用的invoker,然后通过cluster包装一下再得到ClusterInvoker,因此一个服务可能有多个提供者,然后最后注册相应的监听器
拿到了Provider的信息之后就可以通过监听触发 Protocol# refer 了,具体调用哪个 protocol 还是得看 URL的协议的,我们看下这个内部DubboProtocol的refer
而这个connect最终返回 HeaderExchangeClient里面封装的是 NettyClient,然后最终得到的invoker就是对这个client的封装,最终将返回一个Proxy的代理对象
四.回顾
其实整个流程看代码啥的,一开始可能会遇到很多新名词,但是细细一想其实不难,静下心来好好分析,就都很简单了
其实就是通过各种配置参数和协议组装成相应的URL,然后通过自动适配去对相应的实现类进行相应的服务的引入和后续的调用
如果是写死的地址就直接连接,是注册中心就向注册中心注册信息,然后订阅注册中心的相关信息,得到服务提供者的IP端口号等信息,通过netty进行连接,底层会通过directory和cluster进行底层多个服务的屏蔽和负载均衡的处理,得到代理对象Invoker,再通过动态代理封装得到代理类,总之就是不侵入业务代码,能用代理解决的就用代理
不侵入代码的最好办法就是加代理,遇事不决加层代理