dubbo源码解析之client(六)

源码分析基于dubbo 2.7.1

ReferenceBean继承了ReferenceConfig, 还实现了FactoryBean。
spring启动时,会通过FactoryBean.getObject创建bean。这里会调用到ReferenceConfig.createProxy

    private T createProxy(Map map) {
        if (shouldJvmRefer(map)) {
            ...
        } else {
            ...

            if (urls.size() == 1) {
                // 订阅
                invoker = refprotocol.refer(interfaceClass, urls.get(0));
            } else {
                List> invokers = new ArrayList>();
                URL registryURL = null;
                for (URL url : urls) {
                    // 订阅
                    invokers.add(refprotocol.refer(interfaceClass, url));
                    if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                        registryURL = url; // use last registry url
                    }
                }
                if (registryURL != null) { // registry url is available
                    
                    URL u = registryURL.addParameter(Constants.CLUSTER_KEY, RegistryAwareCluster.NAME);
                    // 集群支持
                    invoker = cluster.join(new StaticDirectory(u, invokers));
                } else { // not a registry url, must be direct invoke.
                    invoker = cluster.join(new StaticDirectory(invokers));
                }
            }
        }

        ...
        // 生成代理类
        return (T) proxyFactory.getProxy(invoker);
    }

服务订阅

refprotocol.refer会调用到RegistryProtocol.doRefer方法

    private  Invoker doRefer(Cluster cluster, Registry registry, Class type, URL url) {
    
        RegistryDirectory directory = new RegistryDirectory(type, url);
        directory.setRegistry(registry);
        directory.setProtocol(protocol);
        ...
        directory.buildRouterChain(subscribeUrl);
        // 订阅
        directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
                PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));

        Invoker invoker = cluster.join(directory);
        ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
        return invoker;
    }

directory.subscribe:

    public void subscribe(URL url) {
        ...  
        registry.subscribe(url, this);
    }

directory通过registry完成订阅操作,同时RegistryDirectory还实现了NotifyListener接口,把自身作为监听者。

RegistryDirectory.notify会调用refreshOverrideAndInvoker,再调用refreshInvoker创新Invoker。
refreshInvoker通过toInvokers创建Invoker

    private Map> toInvokers(List urls) {
            ...
            Invoker invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
            if (invoker == null) { // Not in the cache, refer again
                try {
                    boolean enabled = true;
                    ...
                    if (enabled) {
                        // 创建invoker
                        invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl);
                    }
                } catch (Throwable t) {
                    ...
                }
                if (invoker != null) { // Put new invoker in cache
                    newUrlInvokerMap.put(key, invoker);
                }
            } else {
                newUrlInvokerMap.put(key, invoker);
            }
            return newUrlInvokerMap;
    }

RegistryDirectory订阅服务,监听服务端的变化,创建Invoker,然后存放在urlInvokerMap属性中,以便后续使用。

启动客户端

protocol.refer会调用DubboProtocol.refer。

    public  Invoker refer(Class serviceType, URL url) throws RpcException {
        ...  
        // 创建Invoker
        DubboInvoker invoker = new DubboInvoker(serviceType, url, getClients(url), invokers);
        invokers.add(invoker);

        return invoker;
    }

getClients会检查是否能共用Client,如果可以,使用shareClient,否则initClient。

initClient会调用Exchangers.connect方法。
这里server端一样,经过Exchangers(HeaderExchanger), Transporter(NettyTransporter), 然后生成NettyClient。这里不再复述。

动态代理

proxyFactory.getProxy默认通过JavassistProxyFactory实现。

    public  T getProxy(Invoker invoker, Class[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

Proxy根据需代理的interfaces生成代码字符串,通过Javassist生成动态类。
这里直接看生成动态类,生成动态类的方法体为

Object[] args = new Object[1]; 
args[0] = ($w)$1; 
Object ret = handler.invoke(this, methods[0], args); 
return (java.lang.String)ret;

handler就是JavassistProxyFactory newInstance时传入的参数。

集群容错

回顾一下Invoker invoker = cluster.join(directory),它通过directory获取了invoker。
cluster默认为FailoverCluster

public  Invoker join(Directory directory) throws RpcException {
    return new FailoverClusterInvoker(directory);
}

但FailoverCluster处理前,要经过装饰者MockClusterWrapper

public  Invoker join(Directory directory) throws RpcException {
    return new MockClusterInvoker(directory,
            this.cluster.join(directory));
}

可以看到,这里Invoker也有多层装饰:InvokerInvocationHandler ---> MockClusterInvoker ---> FailoverClusterInvoker。

InvokerInvocationHandler用于toString/hashCode/equals等特殊方法。

FailoverClusterInvoker实现重试机制,还有其他重试策略FailoverClusterInvoker、FailfastClusterInvoker、FailbackClusterInvoker、AvailableClusterInvoker等。

看看默认的FailoverClusterInvoker.doInvoke

public Result doInvoke(Invocation invocation, final List> invokers, LoadBalance loadbalance) throws RpcException {
    
    // 重试次数
    int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
    if (len <= 0) {
        len = 1;
    }
    // retry loop.
    RpcException le = null; // last exception.
    // 已调用的invoked
    List> invoked = new ArrayList>(copyinvokers.size()); // invoked invokers.
    Set providers = new HashSet(len);
    for (int i = 0; i < len; i++) {
        ...
        // 选择invoker
        Invoker invoker = select(loadbalance, invocation, copyinvokers, invoked);
        invoked.add(invoker);
        RpcContext.getContext().setInvokers((List) invoked);
        try {
            // 调用invoker,发送网络请求
            Result result = invoker.invoke(invocation);
            
            return result;
        } catch (RpcException e) {
            if (e.isBiz()) { // biz exception.
                throw e;
            }
            le = e;
        } catch (Throwable e) {
            le = new RpcException(e.getMessage(), e);
        } finally {
            providers.add(invoker.getUrl().getAddress());
        }
    }
    throw new RpcException(...);
}

注意,如果是server端抛出的业务异常,不会重发请求,只有rpc异常(如连接超时),才会重发请求。

FailoverClusterInvoker的父类AbstractClusterInvoker实现了服务路由,负载均衡等机制

    public Result invoke(final Invocation invocation) throws RpcException {
        ...
        // 服务路由
        List> invokers = list(invocation);
        // 负载均衡
        LoadBalance loadbalance = initLoadBalance(invokers, invocation);
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        return doInvoke(invocation, invokers, loadbalance);
    }

服务路由

服务路由机制可以筛选出符合路由规则的服务提供者。
Dubbo 目前提供了三种服务路由实现,分别为条件路由 ConditionRouter、脚本路由 ScriptRouter 和标签路由 TagRouter。
list方法通过directory获取Invoker,通过路由规则过滤Invoker。这里不再深入解析了。有需要在自行查看。

负载均衡

Dubbo 提供了4种负载均衡实现,分别是基于权重随机算法的 RandomLoadBalance、基于最少活跃调用数算法的 LeastActiveLoadBalance、基于 hash 一致性的 ConsistentHashLoadBalance,以及基于加权轮询算法的 RoundRobinLoadBalance。
这里也不再深入解析了

你可能感兴趣的:(dubbo源码解析之client(六))