Dubbo源码分析之四:服务的调用

在调用服务之前,先得获得服务的引用。
ReferenceBean 就是服务的引用。它实现了一个FactoryBean接口,在我们需要一个服务时,FactoryBean接口的getObject() 方法会被调用。

public Object getObject() throws Exception {
      return get(); //返回服务的代理。
}
。。。
// get() ->init()->createProxy(map)
// 过程主要是一些参数的检查,初始化等准备工作
// 我们重点看看createProxy 方法,参数为服务需要的所有配置信息

private T createProxy(Map<String, String> map) {
        URL tmpUrl = new URL("temp", "localhost", 0, map);
        final boolean isJvmRefer;
        if (isInjvm() == null) {
            if (url != null && url.length() > 0) { //指定URL的情况下,不做本地引用
                isJvmRefer = false;
            } else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {
                //默认情况下如果本地有服务暴露,则引用本地服务.
                isJvmRefer = true;
            } else {
                isJvmRefer = false;
            }
        } else {
            isJvmRefer = isInjvm().booleanValue();
        }

        if (isJvmRefer) {
            URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
            invoker = refprotocol.refer(interfaceClass, url);
            if (logger.isInfoEnabled()) {
                logger.info("Using injvm service " + interfaceClass.getName());
            }
        } else {
            if (url != null && url.length() > 0) { // 用户指定URL,指定的URL可能是对点对直连地址,也可能是注册中心URL
                String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
                if (us != null && us.length > 0) {
                    for (String u : us) {
                        URL url = URL.valueOf(u);
                        if (url.getPath() == null || url.getPath().length() == 0) {
                            url = url.setPath(interfaceName);
                        }
                        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                            urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                        } else {
                            urls.add(ClusterUtils.mergeUrl(url, map));
                        }
                    }
                }
            } else { // 通过注册中心配置拼装URL
                List<URL> us = loadRegistries(false);
                if (us != null && us.size() > 0) {
                    for (URL u : us) {
                        URL monitorUrl = loadMonitor(u);
                        if (monitorUrl != null) {
                            map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                        }
                        urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                    }
                }
                if (urls == null || urls.size() == 0) {
                    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) {
                invoker = refprotocol.refer(interfaceClass, urls.get(0));
            } else {
                List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
                URL registryURL = null;
                for (URL url : urls) {
                    // 我们重点看下这里,invokers 存放所有可用的服务调用者
                    // 然后集群策略会根据这些调用者做一些负载均衡处理。
                    // 见下一段
                    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); 
                    // 加入集群,返回一个集群调用者。它内部会做一些负载均衡的处理。
                    // 默认的集群策略为自动恢复,默认的负载均衡为随机,如果调用者只有2个,则为轮询。
                    invoker = cluster.join(new StaticDirectory(u, invokers));
                }  else { // 不是 注册中心的URL
                    invoker = cluster.join(new StaticDirectory(invokers));
                }
            }
        }

        。。。// 省略

        // 创建服务代理
        return (T) proxyFactory.getProxy(invoker);
    }

接下来 我们看看 refprotocol.refer(interfaceClass, url)
refprotocol 其实是一个代理,代理谁不知道,只有在调用时才能确定。
这个代理类是动态生成,动态编译的。后面有机会我们再讲。
通过这个动态代理的使用,最终会调用到 RegistryProtocol.refer 方法。

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);
        //根据URL获得对应的注册器。
        // 如果是ZK,则 registry 为ZookeeperRegistry
        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);
    }

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
        // RegistryDirectory 可以把它理解成注册资源,其中包含了消费者,服务,路由等相关信息。
        // 调用者需要这些信息来进行调用
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
        directory.setRegistry(registry); // 注册器
        directory.setProtocol(protocol); // 协议
        URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0, type.getName(), directory.getUrl().getParameters());
        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));
        // 默认的cluster 为FailFastCluster
        // 返回 FailfastClusterInvoker        
        return cluster.join(directory);
    }

接下来,我们看看 FailfastClusterInvoker 。FailfastClusterInvoker 继承 AbstractClusterInvoker 实现了Invoker 接口。
我们看看AbstractClusterInvoker.invoke

public Result invoke(final Invocation invocation) throws RpcException {

        checkWheatherDestoried();// 检查此调用者是否被销毁

        LoadBalance loadbalance; // 负载均衡算法 

        // 获得对应的Invoker。主要从RegistryDirectory 中获取。
        List<Invoker<T>> invokers = list(invocation);

        if (invokers != null && invokers.size() > 0) {
             // 确定负载均衡算法,默认为随机
            loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
                    .getMethodParameter(invocation.getMethodName(),Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
        } else {
            loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);
        }
        //如果是异步操作默认添加invocation id
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        // 调用FailfastClusterInvoker.doInvoke
        return doInvoke(invocation, invokers, loadbalance);
    }

我们看看 list(invocation) 是怎么获取的。
调用关系为
list(invocation) -> AbstractDirectory.list(invocation) ->RegistryDirectory.doList(invocation)

// RegistryDirectory.doList
public List<Invoker<T>> doList(Invocation invocation) {
        if (forbidden) { // 如果被禁止消费
            throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "Forbid consumer " +  NetUtils.getLocalHost() + " access service " + getInterface().getName() + " from registry " + getUrl().getAddress() + " use dubbo version " + Version.getVersion() + ", Please check registry access list (whitelist/blacklist).");
        }
        List<Invoker<T>> invokers = null;
        // methodInvokerMap 为方法到调用器的映射
        Map<String, List<Invoker<T>>> localMethodInvokerMap = this.methodInvokerMap; // local reference
        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())) {
                invokers = localMethodInvokerMap.get(methodName + "." + args[0]); // 可根据第一个参数枚举路由
            }
            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 中。那么其中的数据是这么来的呢?答案在
RegistryProtocol.doRefer 中的 directory.subscribe 。上面也提到过。

// RegistryDirectory
public void subscribe(URL url) {
        setConsumerUrl(url);
        registry.subscribe(url, this); //订阅,回调notify方法
    }

// notify() -> refreshInvoker() -> toInvokers()
// methodInvokerMap 的赋值就在 refreshInvoker 中

而 invoker 也会被初始化为 InvokerDelegete。InvokerDelegete的创建在toInvokers方法中。
InvokerDelegete 是 RegistryDirectory 中内部类。也就是一个代理。
最终会根据 protocol 调用最终的 Invoker。
例如:dubbo
protocol=>DubboProtocol
invoker=> DubboInvoker

// DubboProtocol
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {

        // modified by lishen
        // 序列优化。主要是SerializationOptimizer
        // 将需要序列化的类放入SerializationOptimizer
        optimizeSerialization(url);

        // create rpc invoker.
        // 创建RPC 调用者
        // 底层通信主要看选择了那种client
        DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
        invokers.add(invoker);
        return invoker;
    }

 // 获得client,连接
 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){
                // 获得共享连接,没有则创建(调initClient(url))  
                // 能不能共享主要看 url.address           
                clients[i] = getSharedClient(url);
            } else {
                clients[i] = initClient(url); // 创建client
            }
        }
        return clients;
    }

    /**
     * 创建新连接.
     */
    private ExchangeClient initClient(URL url) {

        // client type setting.
        String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));

        String version = url.getParameter(Constants.DUBBO_VERSION_KEY);
        boolean compatible = (version != null && version.startsWith("1.0."));
        // 设置DubboCodec 编码器 (序列化的操作就在里面)
        url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() && compatible ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
        //默认开启heartbeat
        url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));

        // BIO存在严重性能问题,暂时不允许使用
        if (str != null && str.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
            throw new RpcException("Unsupported client type: " + str + "," +
                    " supported client type is " + StringUtils.join(ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions(), " "));
        }

        ExchangeClient client ;
        try {
            //设置连接应该是lazy的 
            if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)){
                client = new LazyConnectExchangeClient(url ,requestHandler);
            } else {
                // 重点看这里,获得一个连接
                client = Exchangers.connect(url ,requestHandler);
            }
        } catch (RemotingException e) {
            throw new RpcException("Fail to create remoting client for service(" + url
                    + "): " + e.getMessage(), e);
        }
        return client;
    }

创建一个连接,最终定为到了Exchangers.connect(url ,requestHandler);

// Exchangers
    public static ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
        if (url == null) {
            throw new IllegalArgumentException("url == null");
        }
        if (handler == null) {
            throw new IllegalArgumentException("handler == null");
        }
        // 如果没有编码器,则设置一个ExchangeCodec编码器
        // 但前面已经设置过了
        // 后面会讲
        url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
        // 获得一个连接
        return getExchanger(url).connect(url, handler);
    }

    public static Exchanger getExchanger(URL url) {
        String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER); // 默认为header
        return getExchanger(type);
    }

    public static Exchanger getExchanger(String type) {
        // 最终获得 HeaderExchanger
        // 怎么获得前面也讲过,主要通过类路径扫描定义文件
        // header -> HeaderExchanger
        return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
    }

我们在看看HeaderExchanger

// HeaderExchanger
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
    // 根据底层client 封装一个HeaderExchangeClient
    // 重点看Transporters.connect。 
        return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
    }

Transporters.connect 最终会调用
ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension()
如果没有特别指定,将最终使用NettyTransporter。
netty为默认的通信方式。(见Transporter.class 的注解)
在NettyTransporter中会直接new NettyClient

// NettyClient
// 此方法将由父类的构造方法调用,然后再调用connect()
@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() {
                // getcodec()获取到的是DubboCodes编码器。序列化的操作就在对应的Codes类中

                NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
                ChannelPipeline pipeline = Channels.pipeline();
                pipeline.addLast("decoder", adapter.getDecoder()); //解码
                pipeline.addLast("encoder", adapter.getEncoder());// 编码
                pipeline.addLast("handler", nettyHandler); //处理器
                return pipeline;
            }
        });
    }

doOpen() 调用完回调用doConnect,来完成通道的建立。
并保存在 NettyClient.this.channel = newChannel;
数据的发送在父类的send方法。
发送方法的调用来源于DubboInvoker.doInvoke 方法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值