五Dubbo服务引用源码分析--3服务引用-上

五Dubbo服务引用源码分析--3服务引用-上

5.2 服务引用

在服务引用的部分,同样首先把握整体架构,有个大致流程和方向的把握。在Dubbo官方使用者手册中,对集群容错的架构设计,可以通过其对服务引用的框架结构进行参考:

https://dubbo.gitbooks.io/dubbo-user-book/content/demos/fault-tolerent-strategy.html

image-20210924194915954

上述过程是服务调用call过程,consumer端invoker.invoke,调用provider端的invoker对象的方法,就是RPC调用主干流程。

各节点关系:

  • 这里的 InvokerProvider 的一个可调用 Service 的抽象,Invoker 封装了 Provider 地址及 Service 接口信息
  • Directory 代表多个 Invoker,可以把它看成 List<Invoker> ,但与 List 不同的是,它的值可能是动态变化的,比如注册中心推送变更
  • ClusterDirectory 中的多个 Invoker 伪装成一个 Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个
  • Router 负责从多个 Invoker 中按路由规则选出子集,比如读写分离,应用隔离等
  • LoadBalance 负责从多个 Invoker 中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选

5.2.1 调用的代码流程

上述过程主要是技术设计角度绘出,不是实际调用。该过程只是调用proxy的方法的容错架构设计。不包含refer的初始化过程(初始化过程对directory、registry分别进行初始化)

image-20230313131053773

源码中具体调用过程如下:

image-20210927022757025

在消费端对服务引用初始化的过程中,创建引用的代理对象proxy,其中的结构如下:

image-20230313131120956

代码流程:

proxy0.xxxMethod()
-->InvokerInvocationHandler.invoke
  // RpcInvocation [methodName=sayHello, parameterTypes=[class java.lang.String], arguments=[world], attachments={}]
  -->MockClusterInvoker.invoke(Invocation invocation)
    -->FailoverClusterInvoker.invoke(final Invocation invocation)
      -->RegistryDirectory.list(Invocation invocation) //根据RpcInvocation中的methodName获取Invoker
        -->router过滤
        -->loadBalancer选取一个Invoker
      -->执行filter链
        // RpcInvocation [methodName=sayHello, parameterTypes=[class java.lang.String], arguments=[world], attachments={path=com.alibaba.dubbo.demo.DemoService, interface=com.alibaba.dubbo.demo.DemoService, version=2.0.0, timeout=60000, group=dev}]
        -->DubboInvoker.invoke(Invocation inv)

5.2.2 源码追踪

本文代码使用DatatablesDemo代码。

用applicationContext启动spring,并通过interface加载服务端实现的bean。看出实例对象为dubbo的代理生成的proxy对象

image-20210927154208438

看出该proxy被传入一个invocationHaldler实例InvokerInvocationHandler(该过程就是java的动态代理)

image-20210927154449975

动态代理实现(InvocationHandlerImpl其内含有被代理的实例对象,这里,就是Invoker实例。通过调用invoke方法,调用引用服务端的方法)

java动态代理的创建过程:	
// 3.设置一个来自代理传过来的方法调用请求处理器,处理所有的代理对象上的方法调用
		InvocationHandler handler = new InvocationHandlerImpl(car);
		/*
		  4.根据上面提供的信息,创建代理对象 在这个过程中, 
             a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等同的字节码
		         b.然后根据相应的字节码转换成对应的class, 
             c.然后调用newInstance()创建实例
		 */
		Object o = Proxy.newProxyInstance(classLoader, interfaces, handler);

TAG0 InvokerInvocationHandler.invoke

接下来进入dubbo的consumer调用过程,来看容错架构设计

//该类为动态代理的handler对象
public class InvokerInvocationHandler implements InvocationHandler {

// 该处为MockClusterInvoker对象,就是动态代理invocationhandler中实际代理的对象car
    private final Invoker<?> invoker;

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();	//getPermission
        Class<?>[] parameterTypes = method.getParameterTypes();//方法的参数类型
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(invoker, args);
        }
        if ("toString".equals(methodName) && parameterTypes.length == 0) {
            return invoker.toString();
        }
        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
            return invoker.hashCode();
        }
        if ("equals".equals(methodName) && parameterTypes.length == 1) {
            return invoker.equals(args[0]);
        }
//TAG1 MockClusterInvoker/FailoverClusterInvoker.invoke
        //proxy代理对象调用方法,代理到invoker.invoke
        return invoker.invoke(new RpcInvocation(method, args)).recreate();//根据方法名、参数,创建远程调用对象
    }

此时invoker对象,是MockClusterInvoker--->FailoverClusterInvoker,在failover内部,包含一个directory对象,其是一个invoker的list对象,并且具有NotifyListener功能,在zookeeper上订阅节点发生变化时,会刷新invoker的list对象。

image-20230103145657199

TAG1 MockClusterInvoker/FailoverClusterInvoker.invoke

B1 此时,进入MockClusterInvoker类

public class MockClusterInvoker<T> implements Invoker<T> {
     
    private final Directory<T> directory;
		//mockClusterInvoker
    private final Invoker<T> invoker;
  
/**……………………………………………………………………………………invoker.invoke调用逻辑……………………………………………………………………………………………………………… */
  //
        @Override
    public Result invoke(Invocation invocation) throws RpcException {
        Result result = null;

        String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
  /**………………………………………………………………方法参数中不包含force/fail,正常调用…………………………………………………… */
        if (value.length() == 0 || value.equalsIgnoreCase("false")) {
//TAG1.1 FailoverClusterInvoker.invoke
            //转发给内部的invoker类,调用
            result = this.invoker.invoke(invocation);
        } 
  /**………………………………………………………………方法参数中包含force--不调用,直接服务降级…………………………………………………… */
      else if (value.startsWith("force")) {
            if (logger.isWarnEnabled()) {
                logger.info("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
            }
            //force:direct mock
            result = doMockInvoke(invocation, null);
        } 
  /**………………………………………………………………方法参数中包含fail--调用失败,服务降级处理…………………………………………………… */
      else {
            //fail-mock
            try {
                result = this.invoker.invoke(invocation);
            } catch (RpcException e) {
                if (e.isBiz()) {
                    throw e;
                } else {
                    if (logger.isWarnEnabled()) {
                        logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
                    }
                    result = doMockInvoke(invocation, e);
                }
            }
        }
        return result;
    }

image-20211018170032542

MockClusterInvoker是针对集群部署的注册中心,提供的可以根据URL中mock参数,支持服务降级的集群管理功能的invoker(此处invoker是cluster.join返回的逻辑整合为一个invoker)。mockClusterInvoker内部持有的invoker类型为FailoverClusterInvoker类。

TAG1.1 FailoverClusterInvoker.invoke

FailoverClusterInvoker采用模板方法,基本实现由抽象类AbstractClusterInvoker实现。

image-20211018170625379

进入AbstractClusterInvoker(此时就进入到集群,cluster)

public abstract class AbstractClusterInvoker<T> implements Invoker<T> {
         protected final Directory<T> directory;
     
// AbstractClusterInvoker.invoke(invocation)
     public Result invoke(final Invocation invocation) throws RpcException {
        checkWhetherDestroyed();
        LoadBalance loadbalance; //负载均衡
    // binding attachments into invocation.
        Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
        if (contextAttachments != null && contextAttachments.size() != 0) {
            ((RpcInvocation) invocation).addAttachments(contextAttachments);
        }
       
//TAG1.1.1 list(invocation)
          //从directory中获取可以正常执行的invoker
        List<Invoker<T>> invokers = list(invocation);
        if (invokers != null && invokers.size() > 0) { //2 
          //如果有可用invoker,根据invoker.url的loadbalance参数,决定负载均衡的自适应类
            loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
                    .getMethodParameter(invocation.getMethodName(), Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
        } else {
          //如果没有invoker服务,采用默认的负载均衡类 --random
            loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);
        } //2 
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
       
//TAG1.1.2 doInvoke(invocation, invokers, loadbalance)
        return doInvoke(invocation, invokers, loadbalance);
    }

AbstractClusterInvoker.invoke(invocation)方法的逻辑如下:

1 从directory中获取方法getPermission所对应的invokers;//TAG1.1.1 list(invocation)

2 默认loadbalance负载均衡使用random;如果返回invokers不为空,使用invoker内url参数上的loadBanance-key所设置的值;

3 然后,传入invokers、loadbalance,从返回的invokers中根据负载均衡策略(random随机)中选择一个invoker执行调用逻辑,//TAG1.1.2 doInvoke(invocation, invokers, loadbalance)。

TAG1.1.1 registryDirectory.list(invocation)
      protected List<Invoker<T>> list(Invocation invocation) throws RpcException {
        //从directory中获取invoker服务
        List<Invoker<T>> invokers = directory.list(invocation);
        return invokers;
    }

list内,RegistryDirectory.list(invocation)获取可用服务invokers

image-20211018190359683

image-20211018190542932

进入模板设计模式的AbstractDirectory中

public abstract class AbstractDirectory<T> implements Directory<T> {
  //zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-consumer&dubbo=2.0.2&organization=dubbox&owner=programmer&pid=42298&qos.enable=false&refer=application%3Ddemotest-consumer%26dubbo%3D2.0.2%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D42298%26qos.enable%3Dfalse%26register.ip%3D192.168.0.101%26side%3Dconsumer%26timestamp%3D1672565435211&timestamp=1672565435930
         private final URL url; //注册中心
  //consumer://192.168.0.101/com.alibaba.dubbo.demo.DemoService?application=demotest-consumer&category=providers,configurators,routers&dubbo=2.0.2&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=42298&qos.enable=false&side=consumer&timestamp=1672565435211
     private volatile URL consumerUrl;//消费者url
  	//mockInvokerSelector---router路由对象
     private volatile List<Router> routers;

从这里看出,Router在Directory内

image-20211027183905622

image-20211018191657309

分析directory的抽象类内模板方法list

    public abstract class AbstractDirectory<T> implements Directory<T> {

			public List<Invoker<T>> list(Invocation invocation) throws RpcException {
        if (destroyed) {
            throw new RpcException("Directory already destroyed .url: " + getUrl());
        }
        
//TAG1.1.1.1 AbstractDirectory.doList(invocation)
         //先通过directory的模板方法获取可以正常执行的Invoker
        List<Invoker<T>> invokers = doList(invocation);
         
         //router位于directory内
        List<Router> localRouters = this.routers; // local reference
/**………………………………………………………………………………如果存在路由router…………………………………………………………………………………………………… */
        if (localRouters != null && localRouters.size() > 0) {
            for (Router router : localRouters) {
                try {
                    if (router.getUrl() == null || router.getUrl().getParameter(Constants.RUNTIME_KEY, true)) {
                      
//TAG1.1.1.2 router.route(invokers, getConsumerUrl(), invocation)
                         //通过router路由过滤invoker
                        invokers = router.route(invokers, getConsumerUrl(), invocation);
                    }
                } catch (Throwable t) {
                    logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
                }
            }
        }
        return invokers;
    }

AbstractDirectory.list逻辑,是通过doList的模版方法获取invoker的list,然后通过router.route路由过滤invoker。

TAG1.1.1.1 RegistryDirectory.doList()

doList由RegistryDirectory实现(主要作用就是从RegistryDirectory.methodInvokerMap的map缓存中,查找methodName对应的的所有Invoker)

此时,registryDirectory中属性,经过consumer端初始化后,如下所示:

image-20230313131212555

RegistryDirectory内各属性的意义,见[模块详解章节](#4.3 zk订阅)

RegistryDirectory.doList
 @Override
    public List<Invoker<T>> doList(Invocation invocation) {
        if (forbidden) {
            // 1. No service provider 2. Service providers are disabled
            throw new RpcException(RpcException.FORBIDDEN_EXCEPTION,
                "No provider available from registry " + getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " +  NetUtils.getLocalHost()
                        + " use dubbo version " + Version.getVersion() + ", please check status of providers(disabled, not registered or in blacklist).");
        }
        List<Invoker<T>> invokers = null;
        Map<String, List<Invoker<T>>> localMethodInvokerMap = this.methodInvokerMap; // methodName:invoker的map缓存
        if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) {
            String methodName = RpcUtils.getMethodName(invocation);
            Object[] args = RpcUtils.getArguments(invocation);//参数
            if (args != null && args.length > 0 && args[0] != null
                    && (args[0] instanceof String || args[0].getClass().isEnum())) {
              //根据方法名+第一个参数,从methodInvokerMap获取对应的invoker
                invokers = localMethodInvokerMap.get(methodName + "." + args[0]); // The routing can be enumerated according to the first parameter
            }
            if (invokers == null) {
                invokers = localMethodInvokerMap.get(methodName);
            }
            if (invokers == null) {
                invokers = localMethodInvokerMap.get(Constants.ANY_VALUE);
            }
            if (invokers == null) {
                Iterator<List<Invoker<T>>> iterator = localMethodInvokerMap.values().iterator();
                if (iterator.hasNext()) {
                    invokers = iterator.next();
                }
            }
        }
        return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers;
    }

上面过程是从methodInvokerMap中取出invokers。

methodInvokerMap内是以方法名为key,以List为value的map。从map中取出对应的方法的invokers服务,如果没有取到,根据methodname+arg、methodName、ANY、iterator遍历的次序逐个获取。(map中invoker实例,是RegistryDirectory\(InvokerDelegate类型的invoker)。详见[invoker章节内容](#1 RegistryDirectory\)InvokerDelegate)。

image-20230313131241463

该invokerDelegate有三个参数,provideUrl存储,注册中心下发的provider的服务地址。可以在refer调用服务时候,根据provideUrl、methodparm等参数,重新构造调用服务地址。

三个参数,providerUrl代表当前服务,注册在注册中心中,服务提供者provider的真实地址。

其中,delegate代理类内传入的invoker是[ListenerInvokerWrapper](#2 ListenerInvokerWrapper)的invoker,其内添加了invoker调用和销毁监听器listener。

然后,返回InvokerDelegate类型的invoker实例

image-20230313131302281
TAG1.1.1.2 (Router)MockInvokerSelector.route()

此时,将invokers返回后,从AbstractDirectory的list继续执行list,到达Router进入路由。

image-20211019152006323

image-20211019152122754

getConsumerUrl()=consumer://192.168.0.101/com.alibaba.dubbo.demo.DemoService?application=demotest-consumer&category=providers,configurators,routers&dubbo=2.0.2&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=42298&qos.enable=false&side=consumer&timestamp=1672565435211

此时的router为MockInvokerSelector(也是router)

image-20211019151826741

//route路由方法,根据dubbo中配置的路由规则(如读写分离等),获取对应的可用的invokers服务

进入route方法

public class MockInvokersSelector implements Router {

  //参数: url为consumer的url
    @Override
    public <T> List<Invoker<T>> route(final List<Invoker<T>> invokers,
                                      URL url, final Invocation invocation) throws RpcException {
        if (invocation.getAttachments() == null) {
          //创建正常的invoker
            return getNormalInvokers(invokers);
        } else {
            String value = invocation.getAttachments().get(Constants.INVOCATION_NEED_MOCK);
            if (value == null)
                return getNormalInvokers(invokers);
/**…………………………………………………………方法调用中,如果需要mock进行本地伪装……………………………………………………………… */
            else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) {
                return getMockedInvokers(invokers);
            }
        }
        return invokers;
    }

MockInvokerSelector.route,根据参数invocation,是否有mock,选择创建normal还是mocked的invoker。

MockInvokersSelector.getNormalInvokers,创建正常的invokers,

MockInvokersSelector
   private <T> List<Invoker<T>> getNormalInvokers(final List<Invoker<T>> invokers) {
  		//判断invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)是否是mock协议头的invoker服务类
        if (!hasMockProviders(invokers)) {
            return invokers;
        } else {
          //如果获取invokers中不是mock://协议头的invoker,加入list中
            List<Invoker<T>> sInvokers = new ArrayList<Invoker<T>>(invokers.size());
            for (Invoker<T> invoker : invokers) {
                if (!invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)) {
                    sInvokers.add(invoker);
                }
            }
            return sInvokers;
        }
    }

判断invokers中是否有mock过的提供者,如果没有,直接返回。

回到AbstarctClusterInvoker类,此时从directory执行list获取可用的服务类,已经完成,然后需要执行对应的调用invoke。

总结:

directory中找出本次集群中的全部Invokers;

router中,将上一步的Invokers挑选出正常执行的invokers。(getNomalInvokers)。

posted @ 2023-03-13 14:50  LeasonXue  阅读(35)  评论(0编辑  收藏  举报