(二)Dubbo底层实现的原理------服务的调用

Dubbo原理的实现之服务的调用

(一)大概描述一下整个服务调用的过程的准备工作和总体流程,后面一步一步分析

先说一下dubbo调用服务的原理需要的前置步骤,相当于准备工作:

  • dubbo在引用服务的时候是分两种方式的,第一种是我们正常使用的懒汉式的加载方式的,其实就是我们用@Reference注解的时候使用的就是懒汉式的加载方式
  • 第二种是当我们在 Spring 容器调用 ReferenceBean 的 afterPropertiesSet 方法时引用服务,这种调用方式其实是一种饿汉式的加载方式,然后两者调用的开始其实就是从ReferenceBean 的 getObject 方法开始

现在调用完事之后,下一步就是决定引用那种服务,有三种可供选择的嗲用服务的方式:

  1. 第一种是引用本地 (JVM) 服务
  2. 第二是通过直连方式引用远程服务
  3. 第三是通过注册中心引用远程服务
  4. 然后最终都会生成一个Invoker对象,此时的Invoker对象已经具备调用本地或远程服务的能力了,但是不能直接暴露给用户,会对一下业务造成侵入,然后需要使用代理工厂类代理对象去调用Invoker的逻辑。

总的概括一下总的调用流程:

首先是ReferenceConfig类先去调用init()方法,然后会在init方法中调用Protocol的refer()方法生成一个Invoker对象实例,这里是消费服务的关键,然后会在init方法中通过createProxy(map)方法生成一个代理对象,然后在CreateProxy方法中进行判断是采用哪种调用方式(就是上面三种方式之一),并去调用refer()方法生成一个Invoker对象,接下来会把Invoker对象转换为客服端需要的对象(UserService),

引用服务的时序图:
在这里插入图片描述
整个调用过程的简化图:
在这里插入图片描述

(二)源码分析的流程:

1. 处理配置

  • 其实就是通过@Reference注解获取到消费者配置对象,然后主要用于检测 ConsumerConfig 实例是否存在
  • 获取接口的版本信息
  • 将 ApplicationConfig、ConsumerConfig、ReferenceConfig 等对象的字段信息添加到 map 中
  • 遍历 MethodConfig 列表 看有没有给每个方法中配置调用方法失败的时候的重试次数,这里其实就是dubbo重试的最小粒度其实是方法,然后放到构建Invoker对象的URL参数中字段的map中去
  • 获取服务消费者 ip 地址

2. 引用服务

  • 前提是需要+
  • 创建Invoker对象实例,在服务提供方,Invoker 用于调用服务提供类。在服务消费方,Invoker 用于执行远程调用。Invoker 是由 Protocol 实现类构建而来
    • 是在DubboProtocol 的 refer () 方法中通过DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers); 创建的,这里走的是dubbo协议,也可能是走Injvm协议,或者是别的协议
    • getClients(URL对象)里面主要就是这个方法用于获取客户端实例,实例类型为 ExchangeClient。ExchangeClient 实际上并不具备通信能力,它需要基于更底层的客户端实例进行通信,比如使用Netty的NettyClient通信、或者Mina的MinaClient 来进行通信
    • 然后是RegistryProtocol 的 refer() 方法,首先为 url 设置协议头,然后根据 url 参数加载注册中心实例。然后获取 group 配置,根据 group 配置决定 doRefer 第一个参数的类型
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
    // 取 registry 参数值,并将其设置为协议头
    url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
    // 获取注册中心实例
    Registry registry = registryFactory.getRegistry(url);
    if (RegistryService.class.equals(type)) {
        return proxyFactory.getInvoker((T) registry, type, url);
    }

    // 将 url 查询字符串转为 Map
    Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
    // 获取 group 配置
    String group = qs.get(Constants.GROUP_KEY);
    if (group != null && group.length() > 0) {
        if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
                || "*".equals(group)) {
            // 通过 SPI 加载 MergeableCluster 实例,并调用 doRefer 继续执行服务引用逻辑
            return doRefer(getMergeableCluster(), registry, type, url);
        }
    }
    
    // 调用 doRefer 继续执行服务引用逻辑
    return doRefer(cluster, registry, type, url);
}
  • 然后是RegistryProtocol 的doRefer(Cluster cluster, Registry registry, Class type, URL url)方法,doRefer 方法创建一个 RegistryDirectory 实例,然后生成服务者消费者链接,并向注册中心进行注册。注册完毕后,紧接着订阅 providers、configurators、routers 等节点下的数据。完成订阅后,RegistryDirectory 会收到这几个节点下的子节点信息。由于一个服务可能部署在多台服务器上,这样就会在 providers 产生多个节点,这个时候就需要 Cluster 将多个服务节点合并为一个,并生成一个 Invoker
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
    // 创建 RegistryDirectory 实例
    RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
    // 设置注册中心和协议
    directory.setRegistry(registry);
    directory.setProtocol(protocol);
    Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
    // 生成服务消费者链接
    URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);

    // 注册服务消费者,在 consumers 目录下新节点
    if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
            && url.getParameter(Constants.REGISTER_KEY, true)) {
        registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
                Constants.CHECK_KEY, String.valueOf(false)));
    }

    // 订阅 providers、configurators、routers 等节点数据
    directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
            Constants.PROVIDERS_CATEGORY
                    + "," + Constants.CONFIGURATORS_CATEGORY
                    + "," + Constants.ROUTERS_CATEGORY));

    // 一个注册中心可能有多个服务提供者,因此这里需要将多个服务提供者合并为一个
    Invoker invoker = cluster.join(directory);
    ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
    return invoker;
}
  • 然后创建Invoker成功

  • 创建代理,代理对象生成的入口方法为 ProxyFactory 的 getProxy,并对服务接口进行生成代理对象, Proxy 的 getProxy 方法获取 Proxy 子类,然后创建 InvokerInvocationHandler 对象,并将该对象传给 newInstance 生成 Proxy 实例

  • 这个具体是用JDK的动态代理生成还是使用JavassistProxyFactory生成,下面是具体的实现,这个invoker和interfaces其实是在上一大步创建好的Invoker中,通过下面代码块获取到接口串,然后进行遍历切分

  • Class<?>[] interfaces = null; String config = invoker.getUrl().getParameter("interfaces");

  • 然后把获取到的Invoker和Interfaces数组传给具体实现生成代理类的具体实现是JavassistProxyFactory还是JdkProxyFactory,然后都是通过下面的代码进行生成的,

public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }
  • 然后就已经拿到了代理生成远程的接口的实现对象类了,然后就可以直接使用了
Dubbo 是一个分布式服务框架,主要用于解决分布式系统中服务治理的问题,它采用了一些比较流行的技术,如 RPC、负载均衡、服务注册与发现等,下面是 Dubbo实现原理概述: 1. 远程调用 Dubbo 的核心功能是远程调用,它采用了高效的 RPC 协议来实现远程调用Dubbo 在设计时考虑了远程调用的效率和可靠性,因此采用了 Netty 进行底层网络通信,同时也支持多种序列化协议,如 Hessian、JSON、Protocol Buffer 等,使得 Dubbo 可以适应多种场景。 2. 服务治理 Dubbo 提供了一整套服务治理方案,包括服务注册与发现、负载均衡、容错机制等。Dubbo服务注册与发现采用了 ZooKeeper 进行实现,通过 ZooKeeper 可以实现服务的动态上下线和动态扩容。Dubbo 的负载均衡支持多种算法,如随机、轮询、最少活跃等,可以根据实际情况选择不同的负载均衡算法。Dubbo 的容错机制包括了多种容错策略,如失败自动切换、失败自动恢复、失败快速失败等,可以提高系统的可靠性。 3. Spring 集成 Dubbo 广泛使用 Spring 框架,通过 Spring 集成可以方便地将 Dubbo 引入到现有的 Spring 项目中。Dubbo 提供了一系列的 Spring 配置标签,可以方便地配置 Dubbo 的各种参数。 总之,Dubbo实现原理主要包括远程调用服务治理和 Spring 集成三个方面,通过这些功能可以实现分布式系统中的服务治理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值