Dubbo 服务引用原理解析

1.概述

上一篇博客中介绍了dubbo服务暴露的原理,还不清楚的读者可以先看上一篇博客。博客地址如下:

《Dubbo服务暴露原理解析,带你手撕源码》

本文将探究服务消费者如何引用服务,分析dubbo中服务引用的相关源码。同样的,为了聚焦在服务引用的过程,编写如下的测试代码,把关注点放在服务引用的过程上。

代码如下:

// 客户端
@Test
public void invokeRemote() {
    
    
    ReferenceConfig<UserService> referenceConfig = new ReferenceConfig();
    //  应用名称
    ApplicationConfig app = new ApplicationConfig("client");
    referenceConfig.setApplication(app);
    RegistryConfig registryConfig = new RegistryConfig("zookeeper://127.0.0.1:2181");
    referenceConfig.setRegistry(registryConfig);
    // URL地址
    referenceConfig.setUrl("dubbo://127.0.0.1:8080/coderead.dubbo.test.dubbotest.UserService");
    // 指定接口
    referenceConfig.setInterface(UserService.class);
    // 获取接口服务 (动态代理)
    UserService userService = referenceConfig.get();
  
    System.out.println(userService.getUser(1111));
}

上面的代码模拟了一个服务消费者获得服务引用接口并调用。

使用上一篇博客中的代码,开启一个服务。

客户端的测试代码,首先构造一个服务引用配置对象,设置一些必要的服务引用需要的配置信息,例如配置注册中心,设置服务接口信息等。

然后通过referenceConfig.get() 得到服务引用,也就是userService接口的代理对象。

最后通过服务引用代理对象调用getUser方法进行远程调用。

扫描二维码关注公众号,回复: 13147064 查看本文章

2.服务引用源码分析

ReferenceConfig.get()
在这里插入图片描述
ref 是服务引用配置对象的成员属性,代表这一个服务接口代理引用。
在这里插入图片描述
如果为空,那么会执行init()方法构建一个ref,然后返回。

ReferenceConfig.init()

该方法代码很长,忽略掉和本文主题内容无关的部分,主要是会调用createProxy创建服务接口代理引用。

之前的部分是设置一些必要的参数,map参数内容如下
在这里插入图片描述
ReferenceConfig.createProxy(Map<String, String> map)

分两种情况,如果服务的提供者就在同一个jvm,那么直接使用本地服务引用

否则走远程服务引用的逻辑。

远程服务引用,首先更新urls。

根据urls的大小,如果只有1个的话,那么invoker就是调用Protocol.refer得到的结果。

如果是多个的话,将分别的调用Protocol.refer得到多个invoker。将多个invoker合并成一个cluster invoker。

重点应该放在如何得到invoker,也就是Protocol.refer做了什么
在这里插入图片描述
在这里插入图片描述
QosProtocolWrapper.refer(Class< T > type, URL url)

首先来到QosProtocolWrapper类,从这里开始会出现一系列的装饰者+责任链模式的代码。
在这里插入图片描述
QosProtocolWrapper,ProtocolFilterWrapper,ProtocolListenerWrapper,DubboProtocol这4个类都实现了Protocol接口,内部有属性Protocol,比如:
在这里插入图片描述
QosProtocolWrapper.refer的代码如下:
在这里插入图片描述
如果当前url是个注册类型的url的话,那么会开启Qos服务,然后以责任链的方式,调用下一个Protocol的refer方法。Qos服务不是本文的重点,暂时掠过。

ProtocolFilterWrapper.refer(Class< T > type, URL url)

同样也是判断是否是注册类型的url,如果是的话,那么调用下一个Protocol的refer方法

否则调用完Protocol的refer方法之后,还需要将invokr封装成一个invoker链条,添加上过滤器的功能。
在这里插入图片描述
ProtocolListenerWrapper.refer(Class< T > type, URL url)

和上面的类似,将invoker封装成ListenerInvokerWrapper
在这里插入图片描述
AbstractProtocol.refer(Class< T > type, URL url)

基于协议构建服务引用,封装成AsyncToSyncInvoker对象。
在这里插入图片描述
这里使用的是Dubbo协议,所以会调用DubboProtocol子类实现的protocolBindingRefer方法

DubboProtocol.protocolBindingRefer(Class< T > serviceType, URL url)
在这里插入图片描述
创建一个rpc invoker,重点在于getClients方法,得到的clients在封装成DubboInvokder。

然后添加早invokers集合当中,返回DubboInvokder。

getClients方法如下:
在这里插入图片描述
从url中得到参数,连接数。

如果没有配置连接数参数为0,默认使用的是共享连接,否则的话,每次都使用新的rpc连接。

共享连接通过getSharedClient获得,否则使用initClient初始化一个客户端。下面就来看看这两个方法具体的差别。

getSharedClient
在这里插入图片描述
从url当中获取ip地址+端口号。

基于ip地址+端口号 从referenceClientMap当中寻找,是否有缓存的客户端服务引用。

有的话,那么检查这些服务引用是否可用。如果可用的话,修改引用数量,然后返回即可。

如果不可用,或者缓存当中没有,那么就会继续下面的代码。

并发处理,双重检查,为了避免构建重复的引用。

如果缓存当中没有,那么基于连接数量构建服务引用。也就是走这一段代码
在这里插入图片描述
在这里插入图片描述
如果不为空的话,那么说明是部分客户端服务引用关闭了,那么就需要重新的构建服务引用。对应下面的代码
在这里插入图片描述
两种情况都差不多,都会调用buildReferenceCountExchangeClient(URL url)

DubboProtocol.buildReferenceCountExchangeClient(Url url)
在这里插入图片描述
这里只做了一层额外的封装,下面看看初始化客户端的逻辑。

DubboProtocol.initClient(URL url)
在这里插入图片描述
调用Exchangers.connect,下面又是一连串的connect责任链调用。

HeaderExchanger.connect(URL url, ExchangeHandler handler)
在这里插入图片描述
参数校验

HeaderExchanger.connect(URL url, ExchangeHandler handler)

构建DecodeHandler,解码处理器
在这里插入图片描述
Transporters.connect(URL url, ChannelHandler… handlers)
在这里插入图片描述
NettyTransporter.connect(URL url, ChannelHandler handler)
在这里插入图片描述
构建Netty客户端

NettyClient构造函数
在这里插入图片描述
调用其父类的构造函数
在这里插入图片描述
doOpen()开启netty客户端

connect()使用netty客户端连接远程服务
在这里插入图片描述
在这里插入图片描述
以上就是服务引用的全过程。

猜你喜欢

转载自blog.csdn.net/gongsenlin341/article/details/115471691