RPC框架(七)dubbo源码分析--dubbo服务消费者初始化

一、概述

在分析标签解析的时候知道框架会把dubbo:reference解析成一个ReferenceBean,它是一个FactoryBean,消费者的初始化在它的init方法中执行,这个方法在两种情况下会被调用:

  • 消费者设置了立即初始化(init属性设置成true),那么bean加载时会立刻调用消费者初始化
  • 消费者bean被使用者调用时,调用getObject->get->init

消费者初始化时主要做的事情就是引用对应的远程服务,执行了以下步骤:

  • 监听注册中心
  • 连接服务提供端
  • 创建消费端服务代理

二、监听注册中心

消费者在初始化时也会生成一个registryUrl,消费端的信息:side=consumer、dubbo版本、时间戳、应用名等拼成query串放在registryUrl的refer参数中,消费者初始化时引用远程服务也由Protocol组件来处理,加载自适应的Protocol实现:

com.alibaba.dubbo.config.ReferenceConfig.createProxy(Map<String, String>)

主要代码如下:

if (urls.size() == 1) {
    invoker = refprotocol.refer(interfaceClass, urls.get(0));
} else {
    List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
    URL registryURL = null;
    for (URL url : urls) {
        invokers.add(refprotocol.refer(interfaceClass, url));
        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            registryURL = url; // 用了最后一个registry url
        }
    }
    if (registryURL != null) { // 有 注册中心协议的URL
        // 对有注册中心的Cluster 只用 AvailableCluster
        URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
        invoker = cluster.join(new StaticDirectory(u, invokers));
    } else { // 不是 注册中心的URL
        invoker = cluster.join(new StaticDirectory(invokers));
    }
}

传入的url参数是注册中心URL,同样地也会加载到ProtocolListenerWrapper和ProtocolFilterWrapper这两个Wrapper扩展点,然后依次调用ProtocolListenerWrapper->ProtocolFilterWrapper->RegistryProtocol实例的refer方法:

@SuppressWarnings("unchecked")
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
    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);
    }

    // group="a,b" or group="*"
    Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
    String group = qs.get(Constants.GROUP_KEY);
    if (group != null && group.length() > 0) {
        if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
                || "*".equals(group)) {
            return doRefer(getMergeableCluster(), registry, type, url);
        }
    }
    return doRefer(cluster, registry, type, url);
}

registry协议的url两个wrapper不会进行任何处理直接进入RegistryProtocol.refer。在这个方法中先把消费者注册到注册中心,zookeeper注册中心由ZookeeperRegistry来处理,在zookeeper服务器上生成一个节点,节点路径是/dubbo/interfaceName/customs/interfaceName/customs/{customUrl},存储了服务消费方ip、group、接口名称、版本、应用名称等,customUrl是consumer://10.240.176.159/com.netease.haitao.payment.remote.api.coupon.SendCouponRemoteApi?application=…这种形式的,在注册之前生成,把本机ip、接口名称等添加到URL中。

消费端本地会缓存远程服务提供者(每个提供者对应一个Invoker对象)、注册中心配置、路由配置信息。监听注册中心路径是/dubbo/interfaceClass/providers和/dubbo/interfaceClass/providers和/dubbo/{interfaceClass}/configurators, /dubbo/${interfaceClass}/routers的节点,当提供者、配置、路由信息发生变化之后注册中心会通知消费者刷新本地缓存。Dubbo框架通过在消费端缓存提供者的信息消除对注册中心的强依赖,即使注册中心挂了服务依然可用。

com.alibaba.dubbo.registry.integration.RegistryProtocol.doRefer(Cluster, Registry, Class, URL)

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
    RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
    directory.setRegistry(registry);
    directory.setProtocol(protocol);
    // REFER_KEY的所有属性
    Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
    //生成消费者url
    URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
    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)));
    }
    directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
            Constants.PROVIDERS_CATEGORY
                    + "," + Constants.CONFIGURATORS_CATEGORY
                    + "," + Constants.ROUTERS_CATEGORY));
    return cluster.join(directory);
}

框架为每个接口消费者创建一个RegistryDirectory对象,缓存接口所有的提供端Invoker以及注册中心接口相关的接口配置configurators,服务提供者Invoker保存在RegistryDirectory的methodInvokerMap中,key是方法名称,value是一个Invoker列表存储该服务接口的所有提供者,一般情况下,假如服务接口中有a、b两个接口,那么map中的内容是:{a=invokers,b=invokers,c=invokers},其中invokers列表中的内容相同,服务被调用时会根据方法名称从这个map中查找提供者对应Invoker。当providers节点发生变化时通知消费端更新缓存的providers对应的Invoker列表。

com.alibaba.dubbo.registry.integration.RegistryDirectory.refreshInvoker(List)

/**
 * 根据invokerURL列表转换为invoker列表。转换规则如下:
 * 1.如果url已经被转换为invoker,则不在重新引用,直接从缓存中获取,注意如果url中任何一个参数变更也会重新引用
 * 2.如果传入的invoker列表不为空,则表示最新的invoker列表
 * 3.如果传入的invokerUrl列表是空,则表示只是下发的override规则或route规则,需要重新交叉对比,决定是否需要重新引用。
 *
 * @param invokerUrls 传入的参数不能为null
 */
// TODO: 2017/8/31 FIXME 使用线程池去刷新地址,否则可能会导致任务堆积
private void refreshInvoker(List<URL> invokerUrls) {
    if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null
            && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
        this.forbidden = true; // 禁止访问
        this.methodInvokerMap = null; // 置空列表
        destroyAllInvokers(); // 关闭所有Invoker
    } else {
        this.forbidden = false; // 允许访问
        Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
        if (invokerUrls.size() == 0 && this.cachedInvokerUrls != null) {
            invokerUrls.addAll(this.cachedInvokerUrls);
        } else {
            this.cachedInvokerUrls = new HashSet<URL>();
            this.cachedInvokerUrls.addAll(invokerUrls);//缓存invokerUrls列表,便于交叉对比
        }
        if (invokerUrls.size() == 0) {
            return;
        }
        Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// 将URL列表转成Invoker列表
        Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 换方法名映射Invoker列表
        // state change
        //如果计算错误,则不进行处理.
        if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) {
            logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls.toString()));
            return;
        }
        this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
        this.urlInvokerMap = newUrlInvokerMap;
        try {
            destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // 关闭未使用的Invoker
        } catch (Exception e) {
            logger.warn("destroyUnusedInvokers error. ", e);
        }
    }
}

接下来要给每个提供者创建一个对应的Invoker,refreshInvoker方法调用toInvokers,在toInvokers中加载自适应Protocol实现,并且调用它的refer方法,此时url参数是从注册中心获取到的携带提供者服务信息的providerUrl,根据扩展点加载规则,会依次调用ProtocolListenerWrapper->ProtocolFilterWrapper-> DubboProtocol的refer方法,在这两个Wrapper中添加对应的InvokerListener并且构建Invoker-Filter链,最后在DubboProtocol.refer中创建一个DubboInvoker对象,该Invoker对象持有服务Class、providerUrl、负责和提供端通信的ExchangeClient,Invoker对象保存在DubboProtocol的invokers集合中。

com.alibaba.dubbo.registry.integration.RegistryDirectory.toInvokers(List)

/**
 * 将urls转成invokers,如果url已经被refer过,不再重新引用。
 *
 * @param urls
 * @param overrides
 * @param query
 * @return invokers
 */
private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
    Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<String, Invoker<T>>();
    if (urls == null || urls.size() == 0) {
        return newUrlInvokerMap;
    }
    Set<String> keys = new HashSet<String>();
    String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY);
    for (URL providerUrl : urls) {
        //如果reference端配置了protocol,则只选择匹配的protocol
        if (queryProtocols != null && queryProtocols.length() > 0) {
            boolean accept = false;
            String[] acceptProtocols = queryProtocols.split(",");
            for (String acceptProtocol : acceptProtocols) {
                if (providerUrl.getProtocol().equals(acceptProtocol)) {
                    accept = true;
                    break;
                }
            }
            if (!accept) {
                continue;
            }
        }
        if (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
            continue;
        }
        if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
            logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() + " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost()
                    + ", supported protocol: " + ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
            continue;
        }
        URL url = mergeUrl(providerUrl);

        String key = url.toFullString(); // URL参数是排序的
        if (keys.contains(key)) { // 重复URL
            continue;
        }
        keys.add(key);
        // 缓存key为没有合并消费端参数的URL,不管消费端如何合并参数,如果服务端URL发生变化,则重新refer
        Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
        Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
        if (invoker == null) { // 缓存中没有,重新refer
            try {
                boolean enabled = true;
                if (url.hasParameter(Constants.DISABLED_KEY)) {
                    enabled = !url.getParameter(Constants.DISABLED_KEY, false);
                } else {
                    enabled = url.getParameter(Constants.ENABLED_KEY, true);
                }
                if (enabled) {
                    invoker = new InvokerDelegete<T>(protocol.refer(serviceType, url), url, providerUrl);
                }
            } catch (Throwable t) {
                logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
            }
            if (invoker != null) { // 将新的引用放入缓存
                newUrlInvokerMap.put(key, invoker);
            }
        } else {
            newUrlInvokerMap.put(key, invoker);
        }
    }
    keys.clear();
    return newUrlInvokerMap;
}

三、连接服务提供端

构建DubboInvoker时,会构建一个或多个ExchangeClient用来处理和提供端的连接,默认情况下一个url只会创建一个ExchangeClient负责和对应的提供端建立连接,如果应用方配了多个connections则会创建多个。如果lazy属性没有设置成true(默认false),则此时ExchangeClient会马上和服务端建立连接,此时组件调用顺序:Exchanger.connect->Transporter.connect ->Client,最终会调用Netty框架和提供端建立连接,创建NettyClient对象。

com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol

public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
    // create rpc invoker.
    DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
    invokers.add(invoker);
    return invoker;
}
private ExchangeClient[] getClients(URL url) {
    //是否共享连接
    boolean service_share_connect = false;
    int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
    //如果connections不配置,则共享连接,否则每服务每连接
    if (connections == 0) {
        service_share_connect = true;
        connections = 1;
    }

    ExchangeClient[] clients = new ExchangeClient[connections];
    for (int i = 0; i < clients.length; i++) {
        if (service_share_connect) {
            clients[i] = getSharedClient(url);
        } else {
            clients[i] = initClient(url);
        }
    }
    return clients;
}

和NettyServer一样,NettyClient在也会注册IO事件处理链NettyCodecAdapter.decoder->NettyCodecAdapter.encoder->NettyHandler到Netty框架,分别负责请求响应编解码、响应处理。

com.alibaba.dubbo.remoting.transport.netty.NettyClient.doOpen()

@Override
protected void doOpen() throws Throwable {
    NettyHelper.setNettyLoggerFactory();
    bootstrap = new ClientBootstrap(channelFactory);
    // config
    // @see org.jboss.netty.channel.socket.SocketChannelConfig
    bootstrap.setOption("keepAlive", true);
    bootstrap.setOption("tcpNoDelay", true);
    bootstrap.setOption("connectTimeoutMillis", getTimeout());
    final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
    bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
        public ChannelPipeline getPipeline() {
            NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
            //io事件处理链
            ChannelPipeline pipeline = Channels.pipeline();
            pipeline.addLast("decoder", adapter.getDecoder());
            pipeline.addLast("encoder", adapter.getEncoder());
            pipeline.addLast("handler", nettyHandler);
            return pipeline;
        }
    });
}

四、创建消费端服务代理

回到RegistryProtocol .doRefer方法的最后,由Cluster组件来创建一个Invoker并返回,此处的Cluster也是自适应的实现,示例配置的容错模式是failfast,返回扩展链MockClusterWrapper->FailfastCluster(默认是failover),经过扩展链处理创建MockClusterInvoker->FailfastClusterInvoker对象链, FailfastClusterInvoker的directory属性引用上面创建的RegistryDirectory对象。

public class MockClusterWrapper implements Cluster {

   private Cluster cluster;

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

   public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
       return new MockClusterInvoker<T>(directory,
               this.cluster.join(directory));
   }
}
/**
 * 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作。
 * <p>
 * <a href="http://en.wikipedia.org/wiki/Fail-fast">Fail-fast</a>
 *
 * @author william.liangf
 */
public class FailfastCluster implements Cluster {

    public final static String NAME = "failfast";

    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
        return new FailfastClusterInvoker<T>(directory);
    }
}

返回生成的Invoker实例之后,由ProxyFactory生成一个持有该Invoker实例的代理,代理回调时会激活该Invoker(MockClusterWrapper类型)调用它的invoke方法:
com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory

@SuppressWarnings("unchecked")
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
    return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
/**
 * InvokerHandler
 *
 * @author william.liangf
 */
public class InvokerInvocationHandler implements InvocationHandler {

    private final Invoker<?> invoker;

    public InvokerInvocationHandler(Invoker<?> handler) {
        this.invoker = handler;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        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]);
        }
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }
}

该代理会被注册到spring IOC容器中,如果想要获取消费者bean时,容器会返回这个代理。

五、总结

  • 监听注册中心 :根据配置文件中的地址将服务消费者注册到注册中心。服务消费者监听注册中心的变化,如果有变化更新服务提供者的缓存信息(服务消费者会缓存服务提供都的信息)。

  • 连接服务提供端:调用Netty框架和提供端建立连接,创建NettyClient对象。NettyClient在也会注册IO事件处理链NettyCodecAdapter.decoder->NettyCodecAdapter.encoder->NettyHandler到Netty框架,分别负责请求响应编解码、响应处理。

  • 创建消费端服务代理:生成代理对象,该代理会被注册到spring IOC容器中,如果想要获取消费者bean时,容器会返回这个代理。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值