Dubbo服务消费端远程调用过程剖析

1 Dubbo服务消费端远程调用过程概述

(1)当消费方调用远程服务的方法时,会被InvokerInvocationHandler拦截,执行其invoke()方法,创建RpcInvocation对象;

(2)接着会获取Invokers以及负载均衡策略;

(3)最后执行DubboInvoker的doInvoke()方法发起远程调用。

2 远程调用执行入口

服务消费端启动时,会为需要调用的远程服务类创建代理类(通过ReferenceConfig的get()方法),其中代理类的方法拦截器为InvokerInvocationHandler。代码如下所示。

    public  T getProxy(Invoker invoker, Class[] interfaces) {
        try {
            return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
        } catch (Throwable fromJavassist) {
            // try fall back to JDK proxy factory
            try {
                T proxy = jdkProxyFactory.getProxy(invoker, interfaces);
                logger.error(PROXY_FAILED, "", "", "Failed to generate proxy by Javassist failed. Fallback to use JDK proxy success. " +
                    "Interfaces: " + Arrays.toString(interfaces), fromJavassist);
                return proxy;
            } catch (Throwable fromJdk) {
                logger.error(PROXY_FAILED, "", "", "Failed to generate proxy by Javassist failed. Fallback to use JDK proxy is also failed. " +
                    "Interfaces: " + Arrays.toString(interfaces) + " Javassist Error.", fromJavassist);
                logger.error(PROXY_FAILED, "", "", "Failed to generate proxy by Javassist failed. Fallback to use JDK proxy is also failed. " +
                    "Interfaces: " + Arrays.toString(interfaces) + " JDK Error.", fromJdk);
                throw fromJavassist;
            }
        }
    }

所以,当消费方调用远程服务的方法时,会被InvokerInvocationHandler拦截,执行其invoke()方法。其中method为调用的方法,args为参数。此处创建的RpcInvocation对象会一直传递下去,直到发起远程调用。invoke()方法代码如下所示。

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(invoker, args);
        }
        String methodName = method.getName();
        Class[] parameterTypes = method.getParameterTypes();
        if (parameterTypes.length == 0) {
            if ("toString".equals(methodName)) {
                return invoker.toString();
            } else if ("$destroy".equals(methodName)) {
                invoker.destroy();
                return null;
            } else if ("hashCode".equals(methodName)) {
                return invoker.hashCode();
            }
        } else if (parameterTypes.length == 1 && "equals".equals(methodName)) {
            return invoker.equals(args[0]);
        }
        RpcInvocation rpcInvocation = new RpcInvocation(serviceModel, method.getName(), invoker.getInterface().getName(), protocolServiceKey, method.getParameterTypes(), args);

        if (serviceModel instanceof ConsumerModel) {
            rpcInvocation.put(Constants.CONSUMER_MODEL, serviceModel);
            rpcInvocation.put(Constants.METHOD_MODEL, ((ConsumerModel) serviceModel).getMethodModel(method));
        }
        return InvocationUtil.invoke(invoker, rpcInvocation);
    }

3 远程调用细节

3.1 AbstractClusterInvoker

上述 InvocationUtil.invoke() 方法最终会调用AbstractClusterInvoker(Invoker的子类)的invoke() 方法。在该方法中,会根据负载均衡策略等条件,从所有服务提供者Invoker中选择其中一个Invoker来执行远程调用。具体代码如下所示。

    public Result invoke(final Invocation invocation) throws RpcException {
        checkWhetherDestroyed();

        InvocationProfilerUtils.enterDetailProfiler(invocation, () -> "Router route.");

        // 1、获取Invocation对应Invokers(路由)
        List> invokers = list(invocation);
        InvocationProfilerUtils.releaseDetailProfiler(invocation);

        checkInvokers(invokers, invocation);

        // 2、获取负载均衡策略。根据url参数找LoadBalance扩展,默认RandomLoadBalance
        LoadBalance loadbalance = initLoadBalance(invokers, invocation);
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);

        InvocationProfilerUtils.enterDetailProfiler(invocation, () -> "Cluster " + this.getClass().getName() + " invoke.");
        try {
            // 3、执行远程调用。子类实现,会有不同的集群容错方式
            return doInvoke(invocation, invokers, loadbalance);
        } finally {
            InvocationProfilerUtils.releaseDetailProfiler(invocation);
        }
    }

4 参考文献

(1)Dubbo源码之rpc的调用流程分析

你可能感兴趣的:(源码研究-Dubbo,3.2.7,dubbo,Dubbo消费端远程调用)