Dubbo3源码篇2-服务订阅(本地和远程引用)源码分析

欢迎大家关注 github.com/hsfxuebao ,希望对大家有所帮助,要是觉得可以的话麻烦给点一下Star哈

1. 入口

跟服务发现机制的入口是一样的,也是DubboBootstrap.start()方法:

public DubboBootstrap start() {
    if (started.compareAndSet(false, true)) {
        startup.set(false);
        shutdown.set(false);
        awaited.set(false);

        initialize();
        if (logger.isInfoEnabled()) {
            logger.info(NAME + " is starting...");
        }
        // 1. export Dubbo Services
        // 服务暴露
        exportServices();
        
        // 服务引用
        referServices();
        ..
    return this;
}
private void referServices() {
    if (cache == null) {
        cache = ReferenceConfigCache.getCache();
    }
    // 遍历所有<dubbo:reference/>标签
    configManager.getReferences().forEach(rc -> {
        // TODO, compatible with  ReferenceConfig.refer()
        ReferenceConfig<?> referenceConfig = (ReferenceConfig<?>) rc;
        referenceConfig.setBootstrap(this);
        if (!referenceConfig.isRefreshed()) {
            referenceConfig.refresh();
        }

        // 判断当前<dubbo:reference/>标签引用的实例是否需要立即初始化
        if (rc.shouldInit()) {
            if (rc.shouldReferAsync()) {  // 判断是否是异步引用
                ExecutorService executor = executorRepository.getServiceReferExecutor();
                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                    try {
                        cache.get(rc);
                    } catch (Throwable t) {
                        logger.error("refer async catch error : " + t.getMessage(), t);
                    }
                }, executor);

                asyncReferringFutures.add(future);
            } else {  // 处理同步引用
                cache.get(rc);
            }
        }
    });
}
@SuppressWarnings("unchecked")
public <T> T get(ReferenceConfigBase<T> referenceConfig) {
    // 生成服务标识key,其格式为 group/interface:version
    String key = generator.generateKey(referenceConfig);
    // 获取业务接口class
    Class<?> type = referenceConfig.getInterfaceClass();

    // proxies为一个缓存map,其为一个双层map。该缓存map中存放的是业务接口对应的所有服务
    // 外层map的key为业务接口class,value为一个内层map
    // 内层map的key为服务标识key,value为该服务对应的代理对象

    // 从缓存map中获取当前业务接口的内层map,即获取到当前业务接口的所有服务
    ConcurrentMap<String, Object> proxiesOfType = proxies.computeIfAbsent(type, _t -> new ConcurrentHashMap<>());

    // 返回内层map的value,即当前服务标识对应的代理对象
    return (T) proxiesOfType.computeIfAbsent(key, _k -> {
        // todo 创建代理对象
        Object proxy = referenceConfig.get();
        referredReferences.put(key, referenceConfig);
        return proxy;
    });
}

接下里ReferenceConfig.get()方法:

public synchronized T get() {
    if (destroyed) {
        throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
    }

    if (ref == null) {
        init();  // 创建代理对象
    }

    return ref;
}
protected synchronized void init() {
        ...

        // 创建并初始化一个URL使用的map
        Map<String, String> map = new HashMap<String, String>();
        map.put(SIDE_KEY, CONSUMER_SIDE);

        ...
        // 注册元数据
        serviceMetadata.getAttachments().putAll(map);

        // 创建代理对象
        ref = createProxy(map);

        serviceMetadata.setTarget(ref);
        serviceMetadata.addAttribute(PROXY_CLASS_REF, ref);
        ConsumerModel consumerModel = repository.lookupReferredService(serviceMetadata.getServiceKey());
        consumerModel.setProxyObject(ref);
        consumerModel.init(attributes);

        initialized = true;  // 修改初始化状态

        checkInvokerAvailable();
    }

createProxy()方法:

private T createProxy(Map<String, String> map) {
    if (shouldJvmRefer(map)) {  // 判断是否是本地引用
        URL url = new ServiceConfigURL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
        invoker = REF_PROTOCOL.refer(interfaceClass, url);
        if (logger.isInfoEnabled()) {
            logger.info("Using injvm service " + interfaceClass.getName());
        }
    } else {  // 处理远程引用
        urls.clear();
        // 若<dubbo:reference/>的url属性不空,则当前对provider的调用为“直连”调用,无需注册中心
        if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
            String[] us = SEMICOLON_SPLIT_PATTERN.split(url);
            if (us != null && us.length > 0) {
                for (String u : us) {
                    URL url = URL.valueOf(u);
                    if (StringUtils.isEmpty(url.getPath())) {
                        url = url.setPath(interfaceName);
                    }
                    if (UrlUtils.isRegistry(url)) {
                        urls.add(url.putAttribute(REFER_KEY, map));
                    } else {
                        URL peerURL = ClusterUtils.mergeUrl(url, map);
                        peerURL = peerURL.putAttribute(PEER_KEY, true);
                        urls.add(peerURL);
                    }
                }
            }
        } else { // assemble URL from register center's configuration
            // 处理有注册中心的调用情况
            // if protocols not injvm checkRegistry
            if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())) {
                // 检测注册中心可用性。只要有一个注册中心不可用,就会抛出异常
                checkRegistry();
                // 获取注册中心的标准URL,其仅仅包含registry://...格式的URL,
                // 没有service-discovery-registry://...的URL
                List<URL> us = ConfigValidationUtils.loadRegistries(this, false);

                if (CollectionUtils.isNotEmpty(us)) {
                    // 遍历所有注册中心URL
                    for (URL u : us) {
                        // 获取所有监控中心URL
                        URL monitorUrl = ConfigValidationUtils.loadMonitor(this, u);
                        if (monitorUrl != null) {
                            // 以monitor属性的形式将监控中心URL添加到注册中心URL中
                            u = u.putAttribute(MONITOR_KEY, monitorUrl);
                        }
                        // 以refer属性的形式将消费者URL添加到注册中心URL中
                        urls.add(u.putAttribute(REFER_KEY, map));
                    }
                }
                if (urls.isEmpty()) {
                    throw new IllegalStateException(
                            "No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() +
                                    " use dubbo version " + Version.getVersion() +
                                    ", please config <dubbo:registry address="..." /> to your spring config.");
                }
            }
        }

        if (urls.size() == 1) {
            // 获取provider的委托对象
            invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
        } else {  // 处理注册中心有多个的情况
            List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
            URL registryURL = null;
            for (URL url : urls) {
                // For multi-registry scenarios, it is not checked whether each referInvoker is available.
                // Because this invoker may become available later.
                invokers.add(REF_PROTOCOL.refer(interfaceClass, url));

                if (UrlUtils.isRegistry(url)) {
                    registryURL = url; // use last registry url
                }
            }

            // 若有可用的注册中心
            if (registryURL != null) { // registry url is available
                // for multi-subscription scenario, use 'zone-aware' policy by default
                String cluster = registryURL.getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME);
                // The invoker wrap sequence would be: ZoneAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, routing happens here) -> Invoker
                // 由于注册中心的数量是不可变的,所以这里的Directory是静态的
                invoker = Cluster.getCluster(cluster, false).join(new StaticDirectory(registryURL, invokers));
            } else { // not a registry url, must be direct invoke.
                String cluster = CollectionUtils.isNotEmpty(invokers)
                        ?
                        (invokers.get(0).getUrl() != null ? invokers.get(0).getUrl().getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME) :
                                Cluster.DEFAULT)
                        : Cluster.DEFAULT;
                invoker = Cluster.getCluster(cluster).join(new StaticDirectory(invokers));
            }
        }
    }

    if (logger.isInfoEnabled()) {
        logger.info("Referred dubbo service " + interfaceClass.getName());
    }

    URL consumerURL = new ServiceConfigURL(CONSUMER_PROTOCOL, map.get(REGISTER_IP_KEY), 0, map.get(INTERFACE_KEY), map);
    MetadataUtils.publishServiceDefinition(consumerURL);

    // create service proxy  todo 创建代理对象
    return (T) PROXY_FACTORY.getProxy(invoker, ProtocolUtils.isGeneric(generic));
}

2. 本地引用

if (shouldJvmRefer(map)) {  // 判断是否是本地引用
    URL url = new ServiceConfigURL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
    invoker = REF_PROTOCOL.refer(interfaceClass, url);
    if (logger.isInfoEnabled()) {
        logger.info("Using injvm service " + interfaceClass.getName());
    }
}

判断是否是本地引用:

protected boolean shouldJvmRefer(Map<String, String> map) {
    URL tmpUrl = new ServiceConfigURL("temp", "localhost", 0, map);
    boolean isJvmRefer;
    // 若<dubbo:reference/>的injvm属性为null
    if (isInjvm() == null) {
        // if a url is specified, don't do local reference
        // 若<dubbo:reference/>的url属性不空,则为直连引用,不属于本地引用
        if (url != null && url.length() > 0) {
            isJvmRefer = false;
        } else {
            // by default, reference local service if there is
            // 若<dubbo:reference/>的injvm与url属性均为空,则继续判断
            isJvmRefer = InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl);
        }
    } else {
        isJvmRefer = isInjvm();
    }
    return isJvmRefer;
}

第一行new 了一个tempUrl,后面的就是判断要不要injvm,也就是本地引用,如果我们这里配置了scope=injvm或者injvm=true就会执行到isJvmRefer = isInjvm().booleanValue(); 也就isJvmRefer 这个变量设置成true。

接着就是执行进isJvmRefer =true这个代码段中,首先是封装一个url,其中protocol是injvm,host是127.0.0.1。接着调用invoker = REF_PROTOCOL.refer(interfaceClass, url);这句,我们先看下这个REF_PROTOCOL对象:

private static final Protocol REF_PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

很显然是Protocol接口对象,根据dubbo spi 扩展点的自适应特性,会在url找protocol属性的值,这里url的protocol属性值是injvm,也就是
com.apache.dubbo.rpc.protocol.injvm.InjvmProtocol:接下里看一下它的refer方法:

2.1 InjvmProtocol.refer方法

// AbstractProtocol.refer
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
    return protocolBindingRefer(type, url);
}
// InjvmProtocol
@Override
public <T> Invoker<T> protocolBindingRefer(Class<T> serviceType, URL url) throws RpcException {
    return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap);
}

这里new了一个InjvmInvoker 类并且返回 ,参数分别是 1-咱们那个接口class 类,2-url ,3-接口的全类名,4-exporterMap 缓存了本地暴露的服务,这个exporterMap 对象,我们在本地服务暴露的时候,最后将serviceKey 与exporter缓存了exporterMap 这个map中。

这个InjvmInvoker 先放这,我们回到com.apache.dubbo.config.ReferenceConfig#createProxy 这个方法继续看。

2.2 com.apache.dubbo.config.ReferenceConfig#createProxy

远程引用的代码先忽略,接着就是(T) proxyFactory.getProxy(invoker); 这句话,创建service代理对象。其实这个proxyFactory 我们在服务暴露的时候见过了,我们再来看下:

private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

其实根据dubbo spi 扩展点自适应特性,当你指定proxy属性值的时候 用你指定的实现,如果没有话使用默认的实现,这里也就是javassist,JavassistProxyFactory,我们看下JavassistProxyFactory 类

2.3 com.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory#getProxy

public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
    // getProxy() 创建代理类proxy的class
    // newInstance() 创建这个class的实例,即代理对象
    return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}

这里是我那 Proxy.getProxy 生成的一个代理类。 比如说我这个接口是这个样子的:

public interface IHelloProviderService {
    String getName(Integer id);
}

生成的代理类

package com.alibaba.dubbo.common.bytecode;

import com.alibaba.dubbo.common.bytecode.ClassGenerator;
import com.alibaba.dubbo.rpc.service.EchoService;
import com.xuzhaocai.dubbo.provider.IHelloProviderService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class proxy0
implements ClassGenerator.DC,
EchoService,
IHelloProviderService {
    public static Method[] methods;
    private InvocationHandler handler;

    @Override
    public String getName(Integer n) {
        Object[] arrobject = new Object[]{n};
        Object object = this.handler.invoke(this, methods[0], arrobject);
        return (String)object;
    }

    public Object $echo(Object object) {
        Object[] arrobject = new Object[]{object};
        Object object2 = this.handler.invoke(this, methods[1], arrobject);
        return object2;
    }
    public proxy0() {
    }
    public proxy0(InvocationHandler invocationHandler) {
        this.handler = invocationHandler;
    }
}

我们可以看到Proxy生成的代理类实现那个接口,然后在实现接口方法里面将参数封装到数据里面,然后调用了invocationHandler的invoke方法。我们看下这个invocationHandler 。

2.4 com.apache.dubbo.rpc.proxy.InvokerInvocationHandler

public class InvokerInvocationHandler implements InvocationHandler {
    private static final Logger logger = LoggerFactory.getLogger(InvokerInvocationHandler.class);
    private final Invoker<?> invoker;
    private ConsumerModel consumerModel;
    private URL url;
    private String protocolServiceKey;

    public static Field stackTraceField;

    public InvokerInvocationHandler(Invoker<?> handler) {
        this.invoker = handler;
        this.url = invoker.getUrl();
        String serviceKey = this.url.getServiceKey();
        this.protocolServiceKey = this.url.getProtocolServiceKey();
        if (serviceKey != null) {
            this.consumerModel = ApplicationModel.getConsumerModel(serviceKey);
        }
    }

    // 远程调用
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 若当前调用方法为Object的方法,则直接调用该本地方法
        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 rpcInvocation = new RpcInvocation(method, invoker.getInterface().getName(), protocolServiceKey, args);
        String serviceKey = url.getServiceKey();
        rpcInvocation.setTargetServiceUniqueName(serviceKey);

        // invoker.getUrl() returns consumer url.
        RpcServiceContext.setRpcContext(url);

        if (consumerModel != null) {
            rpcInvocation.put(Constants.CONSUMER_MODEL, consumerModel);
            rpcInvocation.put(Constants.METHOD_MODEL, consumerModel.getMethodModel(method));
        }

        return invoker.invoke(rpcInvocation).recreate();
    }
}

在这里invocationHandler对象里面invoker就是咱们前面InjvmInvoker 对象。我们可以看到这个InvokerInvocationHandler#invoke 方法里面帮我们获取了执行的方法名与方法参数类型,如果是toString,hashCode,equals 方法直接调用InjvmInvoker 的,并将方法与参数封装成一个RpcInvocation ,然后调用了InjvmInvoker 的invoke方法, 我们先来看下new RpcInvocation(method, args);

image.png

2.5 com.apache.dubbo.rpc.protocol.injvm.InjvmInvoker

这个InjvmInvoker 继承 AbstractInvoker ,然后AbstractInvoker 实现Invoker 接口,实现了Result invoke(Invocation invocation) throws RpcException;方法,与 Class<T> getInterface()方法。

class InjvmInvoker<T> extends AbstractInvoker<T> {

    private final String key;

    private final Map<String, Exporter<?>> exporterMap;

    private final ExecutorRepository executorRepository = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();

    InjvmInvoker(Class<T> type, URL url, String key, Map<String, Exporter<?>> exporterMap) {
        super(type, url);
        this.key = key;
        this.exporterMap = exporterMap;
    }

    @Override
    public boolean isAvailable() {
        InjvmExporter<?> exporter = (InjvmExporter<?>) exporterMap.get(key);
        if (exporter == null) {
            return false;
        } else {
            return super.isAvailable();
        }
    }

    @Override
    public Result doInvoke(Invocation invocation) throws Throwable {
        Exporter<?> exporter = InjvmProtocol.getExporter(exporterMap, getUrl());
        if (exporter == null) {
            throw new RpcException("Service [" + key + "] not found.");
        }
        RpcContext.getServiceContext().setRemoteAddress(LOCALHOST_VALUE, 0);
        // Solve local exposure, the server opens the token, and the client call fails.
        URL serverURL = exporter.getInvoker().getUrl();
        boolean serverHasToken = serverURL.hasParameter(Constants.TOKEN_KEY);
        if (serverHasToken) {
            invocation.setAttachment(Constants.TOKEN_KEY, serverURL.getParameter(Constants.TOKEN_KEY));
        }
        if (isAsync(exporter.getInvoker().getUrl(), getUrl())) {
            ((RpcInvocation) invocation).setInvokeMode(InvokeMode.ASYNC);
            // use consumer executor
            ExecutorService executor = executorRepository.createExecutorIfAbsent(getUrl());
            CompletableFuture<AppResponse> appResponseFuture = CompletableFuture.supplyAsync(() -> {
                Result result = exporter.getInvoker().invoke(invocation);
                if (result.hasException()) {
                    return new AppResponse(result.getException());
                } else {
                    return new AppResponse(result.getValue());
                }
            }, executor);
            // save for 2.6.x compatibility, for example, TraceFilter in Zipkin uses com.alibaba.xxx.FutureAdapter
            FutureContext.getContext().setCompatibleFuture(appResponseFuture);
            AsyncRpcResult result = new AsyncRpcResult(appResponseFuture, invocation);
            result.setExecutor(executor);
            return result;
        } else {
            return exporter.getInvoker().invoke(invocation);
        }
    }

我们看到invoke方法没有在InjvmInvoker类中,我们看下它的父类中invoke方法:

@Override
public Result invoke(Invocation inv) throws RpcException {
    // if invoker is destroyed due to address refresh from registry, let's allow the current invoke to proceed
    if (isDestroyed()) {
        logger.warn("Invoker for service " + this + " on consumer " + NetUtils.getLocalHost() + " is destroyed, "
                + ", dubbo version is " + Version.getVersion() + ", this invoker should not be used any longer");
    }

    RpcInvocation invocation = (RpcInvocation) inv;

    // prepare rpc invocation
    prepareInvocation(invocation);

    // do invoke rpc invocation and return async result
    AsyncRpcResult asyncResult = doInvokeAndReturn(invocation);  // 远程调用

    // wait rpc result if sync  同步调用,这里将阻塞
    waitForResultIfSync(asyncResult, invocation);

    return asyncResult;
}
private AsyncRpcResult doInvokeAndReturn(RpcInvocation invocation) {
    AsyncRpcResult asyncResult;
    try {
        // todo 
        asyncResult = (AsyncRpcResult) doInvoke(invocation);
    }
    ..
    // set server context
    RpcContext.getServiceContext().setFuture(new FutureAdapter<>(asyncResult.getResponseFuture()));

    return asyncResult;
}

可以看到前面判断了一下销毁没有,然后从Rpc上下文中get到一些kv然后设置进去。我们看后面一句doInvoke(invocation);,这个才是真正的调用,子类实现了这个方法,这里也就是咱们的InjvmInvoker类:

@Override
public Result doInvoke(Invocation invocation) throws Throwable {
    Exporter<?> exporter = InjvmProtocol.getExporter(exporterMap, getUrl());
    if (exporter == null) {
        throw new RpcException("Service [" + key + "] not found.");
    }
    RpcContext.getServiceContext().setRemoteAddress(LOCALHOST_VALUE, 0);
    // Solve local exposure, the server opens the token, and the client call fails.
    URL serverURL = exporter.getInvoker().getUrl();
    boolean serverHasToken = serverURL.hasParameter(Constants.TOKEN_KEY);
    if (serverHasToken) {
        invocation.setAttachment(Constants.TOKEN_KEY, serverURL.getParameter(Constants.TOKEN_KEY));
    }
    if (isAsync(exporter.getInvoker().getUrl(), getUrl())) {
        ((RpcInvocation) invocation).setInvokeMode(InvokeMode.ASYNC);
        // use consumer executor
        ExecutorService executor = executorRepository.createExecutorIfAbsent(getUrl());
        CompletableFuture<AppResponse> appResponseFuture = CompletableFuture.supplyAsync(() -> {
            Result result = exporter.getInvoker().invoke(invocation);
            if (result.hasException()) {
                return new AppResponse(result.getException());
            } else {
                return new AppResponse(result.getValue());
            }
        }, executor);
        // save for 2.6.x compatibility, for example, TraceFilter in Zipkin uses com.alibaba.xxx.FutureAdapter
        FutureContext.getContext().setCompatibleFuture(appResponseFuture);
        AsyncRpcResult result = new AsyncRpcResult(appResponseFuture, invocation);
        result.setExecutor(executor);
        return result;
    } else {
        return exporter.getInvoker().invoke(invocation);
    }
}

首先InjvmProtocol.getExporter(exporterMap, getUrl());这行代码其实是从exporterMap 这个缓存中获取我们这个url中的服务名对应的实现类,我们可以回想下,我们在本地服务暴露的时候,然后以serviceKey为key ,exporter为value缓存到了这个map中,如果获取的这个exporter是null的话抛出异常,如果不是null的话,往RpcContext中设置RemoteAddress 的ip是127.0.0.1 ,port是0。最后调用了exporter.getInvoker().invoke(invocation);,我们可以回顾下当初服务暴露的时候invoker是这样子创建的:

image.png 然后最后调用的是wrapper.invokeMethod的方法。我们再来看下这个wrapper的结构是啥样子的:

public class Wrapper$1 {


    public static String[] pns;// 字段名
    public static Map pts;//<字段名,字段类型>
    public static String[] mns;//方法名
    public static String[] dmns;//自己方法的名字

    public static Class[] mts;//方法参数类型

    public String[] getPropertyNames(){ return pns; }
    public boolean hasProperty(String n){ return pts.containsKey(n); }


    public Class getPropertyType(String n){ return (Class)pts.get(n); }

    public String[] getMethodNames(){ return mns; }
    public String[] getDeclaredMethodNames(){ return dmns; }




    public void setPropertyValue(Object o, String n, Object v){

        com.xuzhaocai.dubbo.provider.IHelloProviderService w;
        try{
            w = (( com.dubbo.provider.IHelloProviderService)$1);
        }catch(Throwable e) {
            throw new IllegalArgumentException(e);
        }
        if( $2.equals("字段名")){
            w."字段名"= $3;
            return ;
        }
    }


    public Object getPropertyValue(Object o, String n){
        com.xuzhaocai.dubbo.provider.IHelloProviderService w;
        try{
            w = (( com.dubbo.provider.IHelloProviderService)$1);
        }catch(Throwable e){
            throw new IllegalArgumentException(e);
        }
        if( $2.equals("字段名")){
            return ($w) w."字段名";

        }

        return null;

    }

    public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws InvocationTargetException{

        com.dubbo.provider.IHelloProviderService w;
        try{
             w = (( com.dubbo.provider.IHelloProviderService)$1);
        }catch(Throwable e){
            throw new IllegalArgumentException(e);
        }
        try{
            if("方法名".equals($2)  && 方法参数个数 == $3.length  &&  $3[1].getName().equals("方法第几个参数的name")){
                w.方法名(参数);
            }

            if("方法名".equals($2)  && 方法参数个数 == $3.length  &&  $3[1].getName().equals("方法第几个参数的name")){
                w.方法名(参数);
            }
        } catch(Throwable e) {
            throw new java.lang.reflect.InvocationTargetException(e);
        }

        throw new NoSuchMethodException("Not found method "+$2+" in class 你传进来那个实现类");

    }
}

其实到最后就是调用的服务提供者的方法。

3. 远程引用(创建Proxy流程)

3.1 com.apache.dubbo.config.ReferenceConfig#createProxy

接着ReferenceConfig#createProxy 方法下下半部分接着分析。

private T createProxy(Map<String, String> map) {
    if (shouldJvmRefer(map)) {  // 判断是否是本地引用
        ...
    } else {  // 处理远程引用
        urls.clear();
        // 若<dubbo:reference/>的url属性不空,则当前对provider的调用为“直连”调用,无需注册中心
        if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
            String[] us = SEMICOLON_SPLIT_PATTERN.split(url);
            if (us != null && us.length > 0) {
                for (String u : us) {
                    URL url = URL.valueOf(u);
                    if (StringUtils.isEmpty(url.getPath())) {
                        url = url.setPath(interfaceName);
                    }
                    if (UrlUtils.isRegistry(url)) {
                        urls.add(url.putAttribute(REFER_KEY, map));
                    } else {
                        URL peerURL = ClusterUtils.mergeUrl(url, map);
                        peerURL = peerURL.putAttribute(PEER_KEY, true);
                        urls.add(peerURL);
                    }
                }
            }
        } else { // assemble URL from register center's configuration
            // 处理有注册中心的调用情况
            // if protocols not injvm checkRegistry
            if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())) {
                // 检测注册中心可用性。只要有一个注册中心不可用,就会抛出异常
                checkRegistry();
                // 获取注册中心的标准URL,其仅仅包含registry://...格式的URL,
                // 没有service-discovery-registry://...的URL
                List<URL> us = ConfigValidationUtils.loadRegistries(this, false);

                if (CollectionUtils.isNotEmpty(us)) {
                    // 遍历所有注册中心URL
                    for (URL u : us) {
                        // 获取所有监控中心URL
                        URL monitorUrl = ConfigValidationUtils.loadMonitor(this, u);
                        if (monitorUrl != null) {
                            // 以monitor属性的形式将监控中心URL添加到注册中心URL中
                            u = u.putAttribute(MONITOR_KEY, monitorUrl);
                        }
                        // 以refer属性的形式将消费者URL添加到注册中心URL中
                        urls.add(u.putAttribute(REFER_KEY, map));
                    }
                }
                if (urls.isEmpty()) {
                    throw new IllegalStateException(
                            "No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() +
                                    " use dubbo version " + Version.getVersion() +
                                    ", please config <dubbo:registry address="..." /> to your spring config.");
                }
            }
        }

        if (urls.size() == 1) {
            // 获取provider的委托对象
            invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
        } else {  // 处理注册中心有多个的情况
            List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
            URL registryURL = null;
            for (URL url : urls) {
                // For multi-registry scenarios, it is not checked whether each referInvoker is available.
                // Because this invoker may become available later.
                invokers.add(REF_PROTOCOL.refer(interfaceClass, url));

                if (UrlUtils.isRegistry(url)) {
                    registryURL = url; // use last registry url
                }
            }

            // 若有可用的注册中心
            if (registryURL != null) { // registry url is available
                // for multi-subscription scenario, use 'zone-aware' policy by default
                String cluster = registryURL.getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME);
                // The invoker wrap sequence would be: ZoneAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, routing happens here) -> Invoker
                // 由于注册中心的数量是不可变的,所以这里的Directory是静态的
                invoker = Cluster.getCluster(cluster, false).join(new StaticDirectory(registryURL, invokers));
            } else { // not a registry url, must be direct invoke.
                String cluster = CollectionUtils.isNotEmpty(invokers)
                        ?
                        (invokers.get(0).getUrl() != null ? invokers.get(0).getUrl().getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME) :
                                Cluster.DEFAULT)
                        : Cluster.DEFAULT;
                invoker = Cluster.getCluster(cluster).join(new StaticDirectory(invokers));
            }
        }
    }
    ... 
    // create service proxy  todo 创建代理对象
    return (T) PROXY_FACTORY.getProxy(invoker, ProtocolUtils.isGeneric(generic));
}

这里我们就找着重要的说,分为两个大部分,首先是获取url,将获取的url添加到urls,接着就是判断urls的size:

  • 如果是一个的话直接调用 refprotocol.refer(interfaceClass, urls) 获得invoker ,
  • 如果是多个的话就会循环调用然后塞到invokers,之后将多个invoker 进行join合并成一个。

下面我们就看下 refprotocol.refer(interfaceClass, urls) 这个是怎样生成invoker的。 这里这个refprotocol是 private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();是Protocol接口自适应类,这里我们protocol是registry,所以这个refprotocol.refer其实最后就调到了RegistryProtocol。

下面我们看下com.apache.dubbo.registry.integration.RegistryProtocol#refer方法。

3.2 com.apache.dubbo.registry.integration.RegistryProtocol#refer

public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
    url = getRegistryUrl(url);
    Registry registry = registryFactory.getRegistry(url);
    if (RegistryService.class.equals(type)) {
        return proxyFactory.getInvoker((T) registry, type, url);
    }

    // group="a,b" or group="*"
    Map<String, String> qs = (Map<String, String>) url.getAttribute(REFER_KEY);
    String group = qs.get(GROUP_KEY);
    if (group != null && group.length() > 0) {
        if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
            return doRefer(Cluster.getCluster(MergeableCluster.NAME), registry, type, url, qs);
        }
    }
    // 获取cluster属性,即集群容错策略,默认failover策略
    Cluster cluster = Cluster.getCluster(qs.get(CLUSTER_KEY));
    return doRefer(cluster, registry, type, url, qs);  // 继续订阅
}

protected <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url, Map<String, String> parameters) {
    Map<String, Object> consumerAttribute = new HashMap<>(url.getAttributes());
    consumerAttribute.remove(REFER_KEY);
    URL consumerUrl = new ServiceConfigURL(parameters.get(PROTOCOL_KEY) == null ? DUBBO : parameters.get(PROTOCOL_KEY),
        null,
        null,
        parameters.get(REGISTER_IP_KEY),
        0, getPath(parameters, type),
        parameters,
        consumerAttribute);
    url = url.putAttribute(CONSUMER_URL_KEY, consumerUrl);
    // 创建一个具有“注册中心迁移”功能的invoker
    ClusterInvoker<T> migrationInvoker = getMigrationInvoker(this, cluster, registry, type, url, consumerUrl);
    // 拦截invoker,为invoker添加迁移监听器
    return interceptInvoker(migrationInvoker, url, consumerUrl, url);
}
  • 第一行是将url 中parameters 里面的registry的值获取出来,如果你是用zk作为注册中心,获取出来的就是zookeeper,然后设置到url的protocol属性上,之后就是将parameters 的 registry remove掉了,这时候url的protocol 就是zookeeper了

  • 第二行 就是根据 url的protocol来获取RegistryFactory 的实现类,也就是ZookeeperRegistryFactory ,然后就是调用它的getRegistry()方法,获取Registry ,总而言之就是获取 注册中心的Registry 对象。

  • 接着就是如果你这个接口类型是RegistryService 的话,就找proxyFactory 来生成invoker。

  • 再往下走就是解析将url中refer 属性值取出来,然后解析成map,其实这个refer就是在ReferenceConfig#createProxy 中塞进去的,就是一些属性,然后获取group 属性,进行分组。

  •   Cluster cluster = Cluster.getCluster(qs.get(CLUSTER_KEY));
    
  • 走到最后就是调用了doRefer()方法,我么看下这个doRefer方法。

3.2.1 Cluster接口

@SPI(Cluster.DEFAULT) // 默认是failover 失败重试
public interface Cluster {

    String DEFAULT = "failover";

    /**
     * Merge the directory invokers to a virtual invoker.
     *
     * @param <T>
     * @param directory
     * @return cluster invoker
     * @throws RpcException
     */
    @Adaptive
    <T> Invoker<T> join(Directory<T> directory) throws RpcException;

    static Cluster getCluster(String name) {
        return getCluster(name, true);
    }

    static Cluster getCluster(String name, boolean wrap) {
        if (StringUtils.isEmpty(name)) {
            name = Cluster.DEFAULT;
        }
        return ExtensionLoader.getExtensionLoader(Cluster.class).getExtension(name, wrap);
    }
}

这里用户没有特殊配置的话,就是使用FailoverCluster 子类的join方法。但是使用dubbo spi的时候,还有包装机制,会将FailoverCluster对象外层包上一个MockClusterWrapper,所以我们需要先看下MockClusterWrapper:

public class MockClusterWrapper implements Cluster {

    private Cluster cluster;

    public MockClusterWrapper(Cluster cluster) {
        this.cluster = cluster;
    }

    @Override
    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
        return new MockClusterInvoker<T>(directory,
                this.cluster.join(directory));
    }

}

接下来,我们重点看一下join方法: 我们看到先是调用了cluster.join()方法,然后将返回的invoker对象用MockClusterInvoker 包了一层,这里这个cluster 才是真正的FailoverCluster对象,我们来看看它干了啥。

public class FailoverCluster extends AbstractCluster {

    public final static String NAME = "failover";

    @Override
    public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
        return new FailoverClusterInvoker<>(directory);
    }

}

我们可以看到new FailoverClusterInvoker 对象返回了,这个FailoverClusterInvoker 其实是一个失败重试invoker。

我们一会在来看FailoverClusterInvoker 内部实现机制,因为我们现在已经得到了invoker对象,现在我们一层一层返回去,这时候我们发现退回到了最初com.apache.dubbo.config.ReferenceConfig#createProxy方法。

image.png

上面这个红框框起来的是我们刚才调用的,这是urls就一个是情况,多个的url的时候我们看到是循环来获得invoker,然后取得最后一个invoker赋值给registryURL,最后将多个invoker合成了一个。接着再往下走:

3.2.2 interceptInvoker()

这个是重点的方法,服务订阅,路由配置都是在这里,我们具体来看一下:

protected <T> Invoker<T> interceptInvoker(ClusterInvoker<T> invoker, URL url, URL consumerUrl, URL registryURL) {
    List<RegistryProtocolListener> listeners = findRegistryProtocolListeners(url);
    if (CollectionUtils.isEmpty(listeners)) {
        return invoker;
    }

    // 遍历所有监听器,为订阅过程添加这些监听功能
    for (RegistryProtocolListener listener : listeners) {
        listener.onRefer(this, invoker, consumerUrl, registryURL);
    }
    return invoker;
}

接下来看listener.onRefer()方法,由于代码比较深,我们直接说调用路径,有兴趣的可以直接从Dubbo3.0源码注释github地址 拉下代码,里面都有注释。

listener.onRefer() -> MigrationRuleListener#onRefer->MigrationRuleHandler#doMigrate -> MigrationRuleHandler#refreshInvoker -> MigrationInvoker#migrateToApplicationFirstInvoker ->MigrationInvoker#refreshInterfaceInvoker -> RegistryProtocol#getInvoker -> RegistryProtocol#doCreateInvoker :

protected <T> ClusterInvoker<T> doCreateInvoker(DynamicDirectory<T> directory, Cluster cluster, Registry registry, Class<T> type) {
    directory.setRegistry(registry);
    directory.setProtocol(protocol);
    // all attributes of REFER_KEY
    Map<String, String> parameters = new HashMap<String, String>(directory.getConsumerUrl().getParameters());
    URL urlToRegistry = new ServiceConfigURL(
        parameters.get(PROTOCOL_KEY) == null ? DUBBO : parameters.get(PROTOCOL_KEY),
        parameters.remove(REGISTER_IP_KEY), 0, getPath(parameters, type), parameters);
    if (directory.isShouldRegister()) {
        directory.setRegisteredConsumerUrl(urlToRegistry);
        // 将当前consumer注册到注册中心
        registry.register(directory.getRegisteredConsumerUrl());
    }
    // 将所有RouterFactory激活扩展类创建的router添加到directory
    directory.buildRouterChain(urlToRegistry);
    // 服务订阅
    directory.subscribe(toSubscribeUrl(urlToRegistry));
    // 将多个invoker伪装为一个具有复合功能的invoker
    return (ClusterInvoker<T>) cluster.join(directory);
}

3.2.2.1 registry.register()

ZookeeperRegistry#doSubscribe方法如下:

public void doSubscribe(final URL url, final NotifyListener listener) {
    try {
        // 处理<dubbo:reference/>的interface属性为"*"的情况
        if (ANY_VALUE.equals(url.getServiceInterface())) {
            String root = toRootPath();
            ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.computeIfAbsent(url, k -> new ConcurrentHashMap<>());
            ChildListener zkListener = listeners.computeIfAbsent(listener, k -> (parentPath, currentChilds) -> {
                for (String child : currentChilds) {
                    child = URL.decode(child);
                    if (!anyServices.contains(child)) {
                        anyServices.add(child);
                        subscribe(url.setPath(child).addParameters(INTERFACE_KEY, child,
                                Constants.CHECK_KEY, String.valueOf(false)), k);
                    }
                }
            });
            zkClient.create(root, false);
            List<String> services = zkClient.addChildListener(root, zkListener);
            if (CollectionUtils.isNotEmpty(services)) {
                for (String service : services) {
                    service = URL.decode(service);
                    anyServices.add(service);
                    subscribe(url.setPath(service).addParameters(INTERFACE_KEY, service,
                            Constants.CHECK_KEY, String.valueOf(false)), listener);
                }
            }
        } else {  // 处理<dubbo:reference/>的interface属性为普通值的情况
            CountDownLatch latch = new CountDownLatch(1);
            List<URL> urls = new ArrayList<>();

            // 遍历configurators、routers与providers三个路径
            for (String path : toCategoriesPath(url)) {
                ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.computeIfAbsent(url, k -> new ConcurrentHashMap<>());
                ChildListener zkListener = listeners.computeIfAbsent(listener, k -> new RegistryChildListenerImpl(url, path, k, latch));
                if (zkListener instanceof RegistryChildListenerImpl) {
                    ((RegistryChildListenerImpl) zkListener).setLatch(latch);
                }
                // 创建持久节点
                zkClient.create(path, false);
                // 为创建的节点添加子节点列表变更的watcher监听
                List<String> children = zkClient.addChildListener(path, zkListener);

                if (children != null) {
                    // toUrlsWithEmpty
                    urls.addAll(toUrlsWithEmpty(url, path, children));
                }
            }
            // 主动调用notify更新本地invoker
            notify(url, listener, urls);
            // tells the listener to run only after the sync notification of main thread finishes.
            latch.countDown();
        }
    } catch (Throwable e) {
        throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
    }
}

完成服务的注册 和订阅,并且更新本地的invoker列表:重点看notify(url, listener, urls); 直接看org.apache.dubbo.registry.integration.RegistryDirectory#notify:

public synchronized void notify(List<URL> urls) {
    if (isDestroyed()) {
        return;
    }

    Map<String, List<URL>> categoryUrls = urls.stream()
            .filter(Objects::nonNull)  // 过滤掉为null的元素
            .filter(this::isValidCategory)  // 过滤掉不可用分类元素
            .filter(this::isNotCompatibleFor26x)  // 过滤掉不兼容2.6版本的元素
            .collect(Collectors.groupingBy(this::judgeCategory));

    // 处理configurators分类节点
    List<URL> configuratorURLs = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, Collections.emptyList());
    this.configurators = Configurator.toConfigurators(configuratorURLs).orElse(this.configurators);

    // 处理routers分类节点
    List<URL> routerURLs = categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList());
    // 读取routers子节点下的路由规则,并添加到directory中
    toRouters(routerURLs).ifPresent(this::addRouters);

    // providers
    // 处理providers分类节点
    List<URL> providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());
    /**
     * 3.x added for extend URL address
     */
    ExtensionLoader<AddressListener> addressListenerExtensionLoader = ExtensionLoader.getExtensionLoader(AddressListener.class);
    List<AddressListener> supportedListeners = addressListenerExtensionLoader.getActivateExtension(getUrl(), (String[]) null);
    if (supportedListeners != null && !supportedListeners.isEmpty()) {
        for (AddressListener addressListener : supportedListeners) {
            providerURLs = addressListener.notify(providerURLs, getConsumerUrl(),this);
        }
    }
    // 刷新invoker
    refreshOverrideAndInvoker(providerURLs);
}

更新本地的invoker列表:

// 更新本地invoker列表
private void refreshInvoker(List<URL> invokerUrls) {
    Assert.notNull(invokerUrls, "invokerUrls should not be null");

    if (invokerUrls.size() == 1
            && invokerUrls.get(0) != null
            && EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {  // 判断是否有empty://开头
        this.forbidden = true; // Forbid to access  禁止远程调用
        this.invokers = Collections.emptyList();
        routerChain.setInvokers(this.invokers);
        destroyAllInvokers(); // Close all invokers  将缓存中的所有invoker删除
    } else {
        this.forbidden = false; // Allow to access  允许远程访问
        // 缓存map,更新本地invoker列表,就是更新这里
        // key为URL,value为该URL对应的invoker实例(invoker委托对象)
        // 先将缓存map发生变更前的值进行暂存
        Map<URL, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
        if (invokerUrls == Collections.<URL>emptyList()) {
            invokerUrls = new ArrayList<>();
        }

        // 这段代码可以增加provider的可用性,
        // 如果provider和注册中心网络抖动,导致注册不成功,但是之前注册成功并且更新到本地缓存了,这种情况我们可以直接调用
        if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
            invokerUrls.addAll(this.cachedInvokerUrls);
        } else {
            this.cachedInvokerUrls = new HashSet<>();
            this.cachedInvokerUrls.addAll(invokerUrls);//Cached invoker urls, convenient for comparison
        }

        // 走到这里invokerUrls仍为空,则说明真的是没有任何可用的invoker
        if (invokerUrls.isEmpty()) {
            return;
        }

        // 从缓存map中获取相应的invoker,若存在,则返回,并将其从缓存map中删除
        // 若不存在,则创建一个invoker
        Map<URL, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map

        /**
         * If the calculation is wrong, it is not processed.
         *
         * 1. The protocol configured by the client is inconsistent with the protocol of the server.
         *    eg: consumer protocol = dubbo, provider only has other protocol services(rest).
         * 2. The registration center is not robust and pushes illegal specification data.
         *
         */
        if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) {
            logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls
                    .toString()));
            return;
        }

        // 获取到最新的可用invoker列表
        List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));
        // pre-route and build cache, notice that route cache should build on original Invoker list.
        // toMergeMethodInvokerMap() will wrap some invokers having different groups, those wrapped invokers not should be routed.
        routerChain.setInvokers(newInvokers);
        this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
        // 将缓存map更新为新map
        this.urlInvokerMap = newUrlInvokerMap;

        try {
            // 删除老map中剩余的invoker
            destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
        } catch (Exception e) {
            logger.warn("destroyUnusedInvokers error. ", e);
        }

        // notify invokers refreshed
        this.invokersChanged();
    }
}

3.2.2.2 directory.buildRouterChain(urlToRegistry)

路由相关的,我们后面在分析

3.2.2.3 cluster.join(directory)

我们分析一下这个方法org.apache.dubbo.rpc.cluster.support.wrapper.AbstractCluster#join:

@Override
public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
    if (directory instanceof StaticDirectory) {
        return doJoin(directory);
    }
    return buildClusterInterceptors(doJoin(directory), directory.getUrl().getParameter(REFERENCE_INTERCEPTOR_KEY));
}

private <T> Invoker<T> buildClusterInterceptors(AbstractClusterInvoker<T> clusterInvoker, String key) {
//        AbstractClusterInvoker<T> last = clusterInvoker;
        AbstractClusterInvoker<T> last = buildInterceptorInvoker(new ClusterFilterInvoker<>(clusterInvoker));

        if (Boolean.parseBoolean(ConfigurationUtils.getProperty(CLUSTER_INTERCEPTOR_COMPATIBLE_KEY, "false"))) {
            return build27xCompatibleClusterInterceptors(clusterInvoker, last);
        }
        return last;
    }

org.apache.dubbo.rpc.cluster.support.FailoverCluster#doJoin:

public class FailoverCluster extends AbstractCluster {

    public final static String NAME = "failover";

    @Override
    public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
        return new FailoverClusterInvoker<>(directory);
    }

}

接着就是创建 proxy代理类了

3.3 Proxy代理类生成过程

(T) proxyFactory.getProxy(invoker)

我们先来看看这个ProxyFactory这个接口,三个抽象方法,这个getInvoker方法我们在服务暴露那几篇已经探讨过了,根据dubbo spi 的自适应规则,先去找url中PROXY_KEY 的值,如果有值的话使用我们自定义的,如果没有值的话就使用@SPI注解里面实现类JavassistProxyFactory。

@SPI("javassist")
public interface ProxyFactory {
    @Adaptive({Constants.PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker) throws RpcException;
    // 是否范化调用
    @Adaptive({Constants.PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException;
    @Adaptive({Constants.PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}

JavassistProxyFactory 继承父类AbstractProxyFactory ,然后这个两个getProxy方法都是在父类实现的

image.png 这个一个参数的getProxy调用两个参数的getProxy,然后范化参数是false,表示不范化调用。

image.png 我们可以看到先从invoker的url里面获取interfaces,这个一看就就是多个interface的,我们这里是null,直接走了是null的情况,将我们自己业务接口class 与EchoService class 放到了interfaces中,下面那个是范化调用的封装,我们暂时先不管它,接着就是调用子类的getProxy(invoker,Class<?>[] types) 方法。

image.png

image.png

我们可以直接看下生成的proxy代码。

image.png

我们看到在方法调用的时候 其实是调用的invoker中的invoke方法,由这个invoker来帮你处理,这篇主要是讲了Proxy代理创建的大体流程,然后最后得到我们在业务调用方法的时候其实是调用的invoker的invoke方法,我们在下节主要是讲下这个invoker 里面的invoke方法是怎样子执行的。

4. 远程调用前的工作

第3节中讲解了服务引用的时候使用RegistryProtocol 获得invoker,然后使用invoker封装生成代理类,其实我们在使用RegistryProtocol doRefer方法中创建了RegistryDirectory,然后又向注册中心订阅了providers,routers,configurators,最后使用cluster的join方法生成了invoker,如果你不指定使用那个FailoverCluster,dubbo默认的invoker是FailoverClusterInvoker,我们可以看一下FailoverClusterInvoker 的invoke方法(在实际调用的时候就是走的invoke方法),invoke方法实际上是在FailoverClusterInvoker 父类AbstractClusterInvoker实现的,在父类定义了doInvoke抽象方法由子类实现,AbstractClusterInvoker 的invoke 方法中处理了传输参数的从RpcContext取出来封装到invocation中,获得了loadbalance的具体实现(这里默认使用的是random,也就是随机),获得所有服务提供者invoker集合,最后交由子类的doInvoke来做具体的一些集群容错处理,比如说快速失败,失败重试等等。

我们来看下AbstractClusterInvoker 的invoke方法:

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

        // binding attachments into invocation.
//        Map<String, Object> contextAttachments = RpcContext.getClientAttachment().getObjectAttachments();
//        if (contextAttachments != null && contextAttachments.size() != 0) {
//            ((RpcInvocation) invocation).addObjectAttachmentsIfAbsent(contextAttachments);
//        }

        // 通过路由策略,将不符合路由规则的invoker过滤掉,获取所有提供者的集合
        List<Invoker<T>> invokers = list(invocation);
        // 获取负载均衡策略,并创建相应的负载均衡实例,默认的random
        LoadBalance loadbalance = initLoadBalance(invokers, invocation);
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        // 调用具体的集群容错策略中的doInvoke()
        return doInvoke(invocation, invokers, loadbalance);
    }

然后再来看下FailoverClusterInvoker 的doInvoke失败重试是怎样实现的:

/**
 * 进行调用
 * @param invocation 封装调用信息实体
 * @param invokers 服务提供者invoker集合
 * @param loadbalance 具体某个负载均衡策略
 * @return 执行结果
 * @throws RpcException
 */
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
    List<Invoker<T>> copyInvokers = invokers;
    // 检测invokers列表是否为空
    checkInvokers(copyInvokers, invocation);
    // 获取RPC调用的方法名
    String methodName = RpcUtils.getMethodName(invocation);
    // 获取retries属性值
    int len = calculateInvokeTimes(methodName);
    // retry loop.
    RpcException le = null; // last exception.
    // 存放所有已经尝试调用过的invoker,这些invoker中,除了最后一个外,其它的都是不可用的
    List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); // invoked invokers.
    Set<String> providers = new HashSet<String>(len);

    for (int i = 0; i < len; i++) {
        //Reselect before retry to avoid a change of candidate `invokers`.
        //NOTE: if `invokers` changed, then `invoked` also lose accuracy.
        if (i > 0) {
            // 检测委托对象invoker是否被销毁
            checkWhetherDestroyed();
            // 更新本地invoker列表
            copyInvokers = list(invocation);
            // check again 重新检测invokers列表是否为空
            checkInvokers(copyInvokers, invocation);
        }
        // 负载均衡
        Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
        // 将选择出的invoker写入到invoked集合
        invoked.add(invoker);
        RpcContext.getServiceContext().setInvokers((List) invoked);
        try {
            // 远程调用
            Result result = invokeWithContext(invoker, invocation);
            //重试过程中,将最后一次调用的异常信息以 warn 级别日志输出
            if (le != null && logger.isWarnEnabled()) {
                logger.warn("Although retry the method " + methodName
                        + " in the service " + getInterface().getName()
                        + " was successful by the provider " + invoker.getUrl().getAddress()
                        + ", but there have been failed providers " + providers
                        + " (" + providers.size() + "/" + copyInvokers.size()
                        + ") from the registry " + directory.getUrl().getAddress()
                        + " on the consumer " + NetUtils.getLocalHost()
                        + " using the dubbo version " + Version.getVersion() + ". Last error is: "
                        + le.getMessage(), le);
            }
            return result;
        } catch (RpcException e) {
            // 如果是业务性质的异常,不再重试,直接抛出
            if (e.isBiz()) { // biz exception.
                throw e;
            }
            // 其他性质的异常统一封装成RpcException
            le = e;
        } catch (Throwable e) {
            le = new RpcException(e.getMessage(), e);
        } finally {
            // 将提供者的地址添加到providers
            providers.add(invoker.getUrl().getAddress());
        }
    }  // end-for
    // 最后抛出异常
    throw new RpcException(le.getCode(), "Failed to invoke the method "
            + methodName + " in the service " + getInterface().getName()
            + ". Tried " + len + " times of the providers " + providers
            + " (" + providers.size() + "/" + copyInvokers.size()
            + ") from the registry " + directory.getUrl().getAddress()
            + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
            + Version.getVersion() + ". Last error is: "
            + le.getMessage(), le.getCause() != null ? le.getCause() : le);
}

我们可以看到就是直接使用for循环完成的失败重试,如果是业务异常就不会再走下次循环结束重试,如果是非业务异常就进行失败重试,然后根据负载均衡算法(这个后面会有文章单独讲解)从服务提供者invoker集合中找一个合适的,然后执行服务提供者invoker的invoke方法进行调用。

那么问题来了,服务提供者invoker集合是怎么来的? 这个问题也是我们本篇要讲解的?

根据我们上面的介绍,invokers是AbstractClusterInvoker的invoke方法获得的,也就是下面这段代码:

/ 通过路由策略,将不符合路由规则的invoker过滤掉,获取所有提供者的集合
List<Invoker<T>> invokers = list(invocation);

我们看下list方法

/**
 * 获取所有服务提供者的invoker
 * @param invocation
 * @return
 * @throws RpcException
 */
protected List<Invoker<T>> list(Invocation invocation) throws RpcException {
    // 从directory 中获取所有的invoker
    return getDirectory().list(invocation);
}
public Directory<T> getDirectory() {
    return directory;
}

directory对象就在 3.2.2.3 小结中创建的对象。

我们接着看下directory 的list方法,这是在RegistryDirectory父类AbstractDirectory中实现的,然后子类实现doList方法(可以发现都是一个套路,哈哈哈哈):

public List<Invoker<T>> list(Invocation invocation) throws RpcException {
    // 判断是否销毁
    if (destroyed) {
        throw new RpcException("Directory already destroyed .url: " + getUrl());
    }
    // 调用子类实现
    return doList(invocation);
}

可以看出来是子类实现的doList方法获得的invokers,接着往下看看子类(我们这边指的是RegistryDirectory)是怎样实现的:

@Override
public List<Invoker<T>> doList(Invocation invocation) {
    ...
    List<Invoker<T>> invokers = null;
    try {
        // Get invokers from cache, only runtime routers will be executed.
        // todo
        invokers = routerChain.route(getConsumerUrl(), invocation);
    } catch (Throwable t) {
        logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
    }

    return invokers == null ? Collections.emptyList() : invokers;
}
public List<Invoker<T>> route(URL url, Invocation invocation) {

    AddrCache<T> cache = this.cache.get();
    if (cache == null) {
        throw new RpcException(RpcException.ROUTER_CACHE_NOT_BUILD, "Failed to invoke the method "
            + invocation.getMethodName() + " in the service " + url.getServiceInterface()
            + ". address cache not build "
            + " on the consumer " + NetUtils.getLocalHost()
            + " using the dubbo version " + Version.getVersion()
            + ".");
    }
    // 获取属性invokers集合值,这个是本地缓存的invoker集合
    BitList<Invoker<T>> finalBitListInvokers = new BitList<>(invokers, false);
    // 处理状态路由
    for (StateRouter stateRouter : stateRouters) {
        if (stateRouter.isEnable()) {
            RouterCache<T> routerCache = cache.getCache().get(stateRouter.getName());
            finalBitListInvokers = stateRouter.route(finalBitListInvokers, routerCache, url, invocation);
        }
    }

    List<Invoker<T>> finalInvokers = new ArrayList<>(finalBitListInvokers.size());

    for(Invoker<T> invoker: finalBitListInvokers) {
        finalInvokers.add(invoker);
    }

    // 处理普通路由
    for (Router router : routers) {
        finalInvokers = router.route(finalInvokers, url, invocation);
    }
    return finalInvokers;
}

我们重点看一下RouterChain的属性invokers是怎么设置进去的。

public class RouterChain<T> {
    
    private volatile List<Invoker<T>> invokers = Collections.emptyList();
}

我们可以找到RegistryDirectory#refreshInvoker中:

// 更新本地invoker列表
private void refreshInvoker(List<URL> invokerUrls) {
        ...
        // 获取到最新的可用invoker列表
        List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));
        // 设置routerChain的invokers的值
        routerChain.setInvokers(newInvokers);
        this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
        // 将缓存map更新为新map
        this.urlInvokerMap = newUrlInvokerMap;
        ...
       
        this.invokersChanged();
    
}

我们在refer的时候,向注册中心订阅了providers,routers,configurators组,当有变动的时候,注册中心就会调用咱们这个NotifyListener 的notify来进行通知,然后调用RegistryDirectory#refreshInvoker方法。

回到 第4小节刚开始的代码中,我们应该走

// 远程调用
Result result = invokeWithContext(invoker, invocation);

但是远程调用的内容较多,我们放到下一篇文章进行解析。敬请期待

5. 服务订阅整体流程图

image.png image.png

参考文章

Dubbo3.0源码注释github地址
dubbo源码系列
dubbo源码分析专栏

猜你喜欢

转载自juejin.im/post/7113388947744489479