首页 > 编程语言 >Dubbo源码-11-服务引用流程

Dubbo源码-11-服务引用流程

时间:2022-12-02 16:14:01浏览次数:68  
标签:11 Dubbo map url URL 源码 new null consumer

一 入口

public static void main(String[] args) {
        // 引用远程服务 此实例很重 封装了与注册中心的连接以及与提供者的连接
        ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();
        reference.setApplication(new ApplicationConfig("native-consumer")); // 配置应用信息
        reference.setRegistry(new RegistryConfig("zookeeper://localhost:2181")); // 配置注册中心信息
        reference.setInterface(DemoService.class); // 引用的远程服务的接口抽象
        // 和本地bean一样使用service 此代理对象内部封装了所有通讯细节 服务端获得了远程服务的代理对象 像调用本地方法一样
        DemoService demoService = reference.get();
        String ret = demoService.sayHello("world");
        System.out.println("ret=" + ret);
    }

二 配置信息

public synchronized T get() {
        if (this.destroyed) throw new IllegalStateException("Already destroyed!");
        if (this.ref == null)
            this.init();
        return ref;
    }
private void init() {
        if (this.initialized) return; // 判断是否已经完成远程服务的初始化
        this.initialized = true; // 标识完成远程服务的初始化
        if (interfaceName == null || interfaceName.length() == 0)
            throw new IllegalStateException("<dubbo:reference interface=\"\" /> interface not allow null!");
        // get consumer's global configuration
        this.checkDefault(); // 尝试在VM参数中加载ConsumerConfig配置项
        appendProperties(this); // 尝试在VM参数中加载ReferenceConfig配置项
        if (super.getGeneric() == null && this.getConsumer() != null) {
            super.setGeneric(this.getConsumer().getGeneric());
        }
        if (ProtocolUtils.isGeneric(super.getGeneric())) { // 泛化调用
            this.interfaceClass = GenericService.class;
        } else {
            try {
                this.interfaceClass = Class.forName(interfaceName, true, Thread.currentThread().getContextClassLoader());
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            super.checkInterfaceAndMethods(interfaceClass, this.methods); // 如果methods不为空 就校验interfaceClass是否存在对应的实现
        }
        String resolve = System.getProperty(interfaceName);
        String resolveFile = null;
        if (resolve == null || resolve.length() == 0) {
            resolveFile = System.getProperty("dubbo.resolve.file");
            if (resolveFile == null || resolveFile.length() == 0) {
                File userResolveFile = new File(new File(System.getProperty("user.home")), "dubbo-resolve.properties");
                if (userResolveFile.exists()) {
                    resolveFile = userResolveFile.getAbsolutePath();
                }
            }
            if (resolveFile != null && resolveFile.length() > 0) {
                Properties properties = new Properties();
                FileInputStream fis = null;
                try {
                    fis = new FileInputStream(new File(resolveFile));
                    properties.load(fis);
                } catch (IOException e) {
                    throw new IllegalStateException("Unload " + resolveFile + ", cause: " + e.getMessage(), e);
                } finally {
                    try {
                        if (null != fis) fis.close();
                    } catch (IOException e) {
                        logger.warn(e.getMessage(), e);
                    }
                }
                resolve = properties.getProperty(interfaceName);
            }
        }
        if (resolve != null && resolve.length() > 0) {
            url = resolve;
            if (logger.isWarnEnabled()) {
                if (resolveFile != null) {
                    logger.warn("Using default dubbo resolve file " + resolveFile + " replace " + interfaceName + "" + resolve + " to p2p invoke remote service.");
                } else {
                    logger.warn("Using -D" + interfaceName + "=" + resolve + " to p2p invoke remote service.");
                }
            }
        }
        // 当前引用配置部分配置项如果缺失尝试把consumer的配置项赋值过来
        if (this.consumer != null) {
            if (this.application == null)
                this.application = this.consumer.getApplication();
            if (this.module == null)
                module = this.consumer.getModule();
            if (this.registries == null)
                registries = this.consumer.getRegistries();
            if (monitor == null)
                monitor = consumer.getMonitor();
        }
        if (this.module != null) {
            if (this.registries == null)
                this.registries = this.module.getRegistries();
            if (this.monitor == null)
                this.monitor = this.module.getMonitor();
        }
        if (this.application != null) {
            if (this.registries == null)
                this.registries = this.application.getRegistries();
            if (this.monitor == null)
                this.monitor = this.application.getMonitor();
        }
        super.checkApplication();
        checkStub(this.interfaceClass);
        checkMock(this.interfaceClass);
        Map<String, String> map = new HashMap<String, String>();
        Map<Object, Object> attributes = new HashMap<Object, Object>();
        map.put(Constants.SIDE_KEY, Constants.CONSUMER_SIDE);
        map.put(Constants.DUBBO_VERSION_KEY, Version.getProtocolVersion());
        map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
        if (ConfigUtils.getPid() > 0) {
            map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
        }
        if (!super.isGeneric()) {
            String revision = Version.getVersion(interfaceClass, version);
            if (revision != null && revision.length() > 0) {
                map.put("revision", revision);
            }

            String[] methods = Wrapper.getWrapper(this.interfaceClass).getMethodNames(); // 远程服务接口中的方法定义
            if (methods.length == 0) {
                logger.warn("NO method found in service interface " + interfaceClass.getName());
                map.put("methods", Constants.ANY_VALUE);
            } else {
                map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
            }
        }
        map.put(Constants.INTERFACE_KEY, interfaceName);
        appendParameters(map, this.application);
        appendParameters(map, module);
        appendParameters(map, consumer, Constants.DEFAULT_KEY);
        appendParameters(map, this);
        String prefix = StringUtils.getServiceKey(map); // com.alibaba.dubbo.demo.DemoService
        if (this.methods != null && !this.methods.isEmpty()) {
            for (MethodConfig method : methods) {
                appendParameters(map, method, method.getName());
                String retryKey = method.getName() + ".retry";
                if (map.containsKey(retryKey)) {
                    String retryValue = map.remove(retryKey);
                    if ("false".equals(retryValue)) {
                        map.put(method.getName() + ".retries", "0");
                    }
                }
                appendAttributes(attributes, method, prefix + "." + method.getName());
                checkAndConvertImplicitConfig(method, map, attributes);
            }
        }

        String hostToRegistry = ConfigUtils.getSystemProperty(Constants.DUBBO_IP_TO_REGISTRY);
        if (hostToRegistry == null || hostToRegistry.length() == 0) {
            hostToRegistry = NetUtils.getLocalHost();
        } else if (isInvalidLocalHost(hostToRegistry)) {
            throw new IllegalArgumentException("Specified invalid registry ip from property:" + Constants.DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
        }
        map.put(Constants.REGISTER_IP_KEY, hostToRegistry);

        //attributes are stored by system context.
        StaticContext.getSystemContext().putAll(attributes);
        /**
         * <p>map就是一个配置项 包含了远程服务的配置信息 根据配置信息构建远程服务的代理对象<ul>
         *     <li>side -> consumer</li>
         *     <li>application -> demo-service</li>
         *     <li>register.ip -> 192.168.0.3</li>
         *     <li>methods -> sayHello</li>
         *     <li>qos.port -> 33333</li>
         *     <li>dubbo -> 2.0.2</li>
         *     <li>pid -> 37888</li>
         *     <li>interface -> com.alibaba.dubbo.demo.DemoService</li>
         *     <li>timestamp -> 1652886744792</li>
         * </ul></p>
         */
        this.ref = this.createProxy(map);
        ConsumerModel consumerModel = new ConsumerModel(getUniqueServiceName(), this, ref, interfaceClass.getMethods());
        ApplicationModel.initConsumerModel(getUniqueServiceName(), consumerModel);
    }

三 createProxy(...)

/**
     * - 创建Invoker对象
     *     - 远程方式
     *         - 注册中心
     *             - zk
     *                 - RegistryProtocol持有ZookeeperRegistry监听指定路径
     *                     - configurators
     *                     - routers
     *                     - providers
     *                 - RegistryProtocol获取到providers的子路径构建URL
     *                 - DubboProtocol构建Invoker对象
     * - 根据Invoker创建目标服务的代理对象
     */
    @SuppressWarnings({"unchecked", "rawtypes", "deprecation"})
    private T createProxy(Map<String, String> map) {
        URL tmpUrl = new URL("temp", "localhost", 0, map);
        final boolean isJvmRefer; // false
        if (super.isInjvm() == null) {
            if (this.url != null && this.url.length() > 0) { // if a url is specified, don't do local reference // 指定了url配置 不做本地引用
                isJvmRefer = false;
            } else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) { // 检测生产者自定义的配置是否需要本地引用
                // by default, reference local service if there is
                isJvmRefer = true;
            } else {
                isJvmRefer = false;
            }
        } else {
            isJvmRefer = isInjvm().booleanValue();
        }

        if (isJvmRefer) { // 本地引用 生成本地引用URL 构建InjvmInvoker实例
            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 (this.url != null && this.url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address. // 远程直连方式调用生产者服务
                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 { // assemble URL from register center's configuration // 远程调用 通过注册中心
                /**
                 * 加载注册中心的url
                 * 启动消费者时指定的远程注册中心配置封装成URL
                 * 可能存在多协议/多注册中心
                 * url
                 *     - registry://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=native-consumer&dubbo=2.0.2&pid=82343&qos.port=33333&registry=zookeeper&timestamp=1669709199146
                 */
                List<URL> us = super.loadRegistries(false); //
                if (us != null && !us.isEmpty()) {
                    for (URL u : us) {
                        URL monitorUrl = super.loadMonitor(u);
                        if (monitorUrl != null) {
                            map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                        }
                        // refer参数
                        this.urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(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 (this.urls.size() == 1) { // 服务直连或者配置了单个注册中心
                /**
                 * Protocol自适应扩展
                 *     - 默认实现dubbo
                 *     - URL协议类型
                 * 这里url
                 *     - registry://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=native-consumer&dubbo=2.0.2&pid=82516&qos.port=33333&refer=application%3Dnative-consumer%26dubbo%3D2.0.2%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D82516%26qos.port%3D33333%26register.ip%3D198.18.0.1%26side%3Dconsumer%26timestamp%3D1669709307576&registry=zookeeper&timestamp=1669709314764
                 * 因此实现是RegistryProtocol
                 */
                this.invoker = refprotocol.refer(interfaceClass, this.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; // use last registry url
                    }
                }
                if (registryURL != null) { // registry url is available
                    // use AvailableCluster only when register's cluster is available
                    URL u = registryURL.addParameterIfAbsent(Constants.CLUSTER_KEY, AvailableCluster.NAME);
                    invoker = cluster.join(new StaticDirectory(u, invokers));
                } else { // not a registry url
                    invoker = cluster.join(new StaticDirectory(invokers));
                }
            }
        }

        Boolean c = super.check;
        if (c == null && this.consumer != null) {
            c = consumer.isCheck();
        }
        if (c == null) {
            c = true; // default true
        }
        if (c && !invoker.isAvailable()) { // invoker可用性检测
            // make it possible for consumer to retry later if provider is temporarily unavailable
            initialized = false; // com.alibaba.dubbo.demo.DemoService
            final String serviceKey = (this.group == null ? "" : group + "/") + this.interfaceName + (this.version == null ? "" : ":" + this.version);
            Set<ConsumerInvokerWrapper> consumerInvoker = ProviderConsumerRegTable.getConsumerInvoker(serviceKey);
            if (consumerInvoker != Collections.<ConsumerInvokerWrapper>emptySet()) {
                //since create proxy error , so we must be the first consumer. Simply clear ConcurrentHashSet
                consumerInvoker.clear();
            }
            throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + serviceKey + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
        }
        if (logger.isInfoEnabled()) {
            logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
        }
        // create service proxy
        /**
         * 根据invoker生成代理类
         *
         * ProxyFactory自适应扩展
         *     - 默认实现javassist
         *     - URL中配置项proxy
         * invoker中的URL
         *     - zookeeper://localhost:2181/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=native-consumer&check=false&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=82897&qos.port=33333&register.ip=198.18.0.1&remote.timestamp=1669707577585&side=consumer&timestamp=1669709866593
         * 实现是JavassistProxyFactory
         */
        return (T) proxyFactory.getProxy(this.invoker);
    }

两个大步骤

  • 创建Invoker对象
  • 根据Invoker对象创建目标服务的代理对象

1 创建Invoker对象

创建Invoker对象(Protocol的RegistryProtocol实现中)

2 代理对象

通过Invoker对象创建目标服务的代理对象(ProxyFactory的JavassistProxyFactory)

四 流程图

标签:11,Dubbo,map,url,URL,源码,new,null,consumer
From: https://www.cnblogs.com/miss-u/p/16944742.html

相关文章

  • Dubbo源码-12-Cluster
    一接口声明@SPI(FailoverCluster.NAME)publicinterfaceCluster{/***Mergethedirectoryinvokerstoavirtualinvoker.**@param<T>......
  • 拼音助手2022最新版2.2.11.26
    最新版本:版本:V2.2.11.26修复高分辨率缩放显示下右键菜单显示异常的问题。(感谢用户反馈)版本:V2.2.11.24添加“一”、“不”变调到多音字库,方便多音校对。(感谢用户反馈......
  • Bert源码学习
    文章目录​​前言​​​​1.bert模型网络modeling.py​​​​1.1整体架构BertModel(object):​​​​1.2embedding层​​​​1.2.1embedding_lookup​​​​1.2.2词向......
  • flex小结11
    1advanceddatagrid中的分组grouping应用,比如: <![CDATA[   importmx.rpc.events.ResultEvent;   importmx.collections.ArrayCollection;      [Bindable]......
  • Vue2(笔记11) - Vue核心 - 列表过滤(watch 和 computed实现)、列表排序
    列表过滤需求:输入关键词,过滤列表信息watch方法实现<divid="root"><h2>{{title}}</h2><inputtype="text"placeholder="请输入名字"v-model="keywords"><ul>......
  • 源码注释中的"方法签名"是什么意思?
    我们经常可以在源码注释中看到methodsignature,也就是方法签名,那它指的是方法中的哪部分呢?好比@Async中的第二段注释中《Java语言程序设计》一书中对方法的描述中有......
  • 直播系统app源码,设置样式(字体样式、行列宽高、对齐方式、边框)
    直播系统app源码,设置样式(字体样式、行列宽高、对齐方式、边框)1.字体样式 fromopenpyxlimportWorkbookfromopenpyxl.stylesimportFontwb=Workbook()ws=wb.act......
  • 二进制安装mariadb-10.6.11
    二进制安装MariaBD1.源下载#官方源下载不方便这里使用清华源wgethttps://mirrors.tuna.tsinghua.edu.cn/mariadb/mariadb-10.6.11/bintar-linux-systemd-x86_64/maria......
  • Mysql 源码解读-执行器
    Mysql源码解读-执行器一条sql执行过程中,首先进行词法分析和语法分析,然后将由优化器进行判断,如何执行更有效率,生成执行计划,后面的任务就交给了执行器。在执行的过程中,执......
  • 11Linux网络配置
    网络配置Linux网络配置原理图指定固定IPubuntu:(26条消息)Ubuntu网络配置文件_ayang1986的博客-CSDN博客_ubuntu网络配置文件......