Dubbo 服务暴露 源码学习(上)(三)

作者: jwfy | 来源:发表于2018-05-03 20:58 被阅读20次

    笔记简述
    何所谓服务暴露,其实就是服务提供方把自己的服务提供出来,使得其他服务使用方能够跨网络(本地就不需要跨网络)调用得到返回结果。
    由于服务暴露整个的过程太长,故分为两小节去学习源码,本小节主要涉及到的内容是从spring开始,dubbo如何无缝衔接并进行一系列属性设置的。真正的服务暴露包含了invoke和export在下一节。
    更多内容可看[目录]Dubbo 源码学习

    目录

    Dubbo 服务暴露 源码学习(上)(三)
    1、前言
    1.1、Dubbo 调用图解
    1.2、Dubbo & Spring
    2、ServiceBean 介绍
    3、源码学习
    3.1、属性
    3.2、配置注入
    3.3、暴露服务启动
    3.4、服务注册中心属性获取
    3.5、服务暴露
    3.5.1、本地服务暴露

    1、前言

    1.1、Dubbo 调用图解

    在学习前还是得知道下dubbo的调用图(来自官网),如下图,共包含了5个模块


    image
    • Provider 服务提供方
    • Registry 服务注册中心(这里可以认为是zookeeper
    • Consumer 服务使用方
    • Container 服务提供方的容器
    • Monitor 服务监控中心

    服务调用流程

    1. 提供服务的容器启动之后,把该服务提交给服务提供方
    2. 服务提供方把该服务细节以约定的协议(此处可认为是dubbo协议)把IP、端口、服务名等等上报给服务注册中心,由服务注册中心统一管理(另外存在心跳检测,便于及时了解服务健康情况;服务均衡负责等也由其管理)
    3. 服务调用方从服务注册中心获取到一个可用的服务提供方的信息
    4. 服务注册中心把合适的服务提供方的细节信息下发到调用方
    5. 服务调用方持有服务提供方的调用信息,可直连服务提供方进行invoke调用操作
    6. 服务提供方以及服务调用方的调用情况,在必备的情况下都可以定时上报到监控中心,从而了解服务调用的数据统计情况

    这样就能够比较清楚的了解到dubbo工作的基本情况是怎么样的,接着把上述过程模块化,细化出来,如下图


    image

    1.2、Dubbo & Spring

    由第一节[] 和之前说的spring解析xml的学习笔记[]( ,我们能够很明显的查看到DubboNamespaceHandler文件

    DubboNamespaceHandler 类

    public class DubboNamespaceHandler extends NamespaceHandlerSupport {
        static {
            Version.checkDuplicate(DubboNamespaceHandler.class);
        }
        public void init() {
            registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
            registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
            registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
            registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
            registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
            registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
            registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
            registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
            registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
            registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
        }
    }
    

    在为外界暴富服务的时候,常使用<dubbo:service interface="com.XXX" ref="xXXX" />,再结合DubboBeanDefinitionParser类的parse细节,可知服务暴露,我们应该关注的类是ServiceBean.class

    PS:如果针对这些解释有疑问,可以再回过头看看【目录】Spring 源码学习

    2、ServiceBean 介绍

    如下图,是ServiceBean类的继承关系图,右边圈出来的是关于dubbo的,下面底部的是和spring有关的。


    image

    很清楚的看到serviceBean也是一个可由Spring管理的很普通的bean

    • 通过BeanNameAware修改bean的名称
    • ApplicationContextAware去获取Spring IOC的容器
    • IntializingBean的afterPropertiesSet去自定义实现bean的实例对象
    • ApplicationListener的onApplicationEvent接收各种事件
    • DisposableBean的destroy去销毁bean

    右侧则和dubbo有关,一层一层的config扩展实例化,包含了在xml配置中的各种参数配置。

    3、源码学习

    3.1、属性

    属性是整个的服务暴露的这个ServiceBean包含的各种属性信息,xml配置信息都会合并到这个属性中

    ServiceConfig 类

    private String              interfaceName;    // 接口类型  
    private Class<?>            interfaceClass;
    private T                   ref;         // 接口实现类引用
    private String              path;     // 服务名称
    private List<MethodConfig>  methods;    // 方法配置
    private ProviderConfig provider;        // 提供方配置
    
    • 方法配置参数methods,一般情况下是没有设置的,也就意味着该接口下的所有的方法都会被暴露出去,如果设置了就意味着设置的方法才会被暴露出去。
    • 提供方配置provider也是负责服务暴露方的一些熟悉信息,例如均衡负责等信息。

    AbstractServiceConfig 类

    protected String               version;    // 服务版本
    protected String               group;      // 服务分组
    protected Boolean              deprecated;      // 服务是否已经deprecated
    protected Integer              delay;     // 延迟暴露
    protected Boolean              export;    // 是否暴露
    protected Integer              weight;     // 权重
    protected String               document;    // 应用文档
    protected Boolean              dynamic;    // 在注册中心上注册成动态的还是静态的服务
    protected String               token;     // 是否使用令牌
    protected String               accesslog;   // 访问日志
    private Integer                executes;   // 允许执行请求数
    protected List<ProtocolConfig> protocols;   // 暴露的协议
    private Boolean                register;   // 是否注册
    

    其中protocols就是常说的dubbo协议了,这里指明list也就是意味着支持可以同时多种协议对外暴露

    AbstractInterfaceConfig 类

    protected String               local;   // 服务接口的本地实现类名
    protected String               stub;   // 服务接口的本地实现类名
    protected MonitorConfig        monitor;   // 服务监控
    protected String               proxy;    // 代理类型
    protected String               cluster;   // 集群方式
    protected String               filter;    // 过滤器
    protected String               listener;   // 监听器
    protected String               owner;   // 负责人
    // 连接数限制,0表示共享连接,否则为该服务独享连接数
    protected Integer              connections;
    protected String               layer;    // 连接数限制
    protected ApplicationConfig    application;    // 应用信息
    protected ModuleConfig         module;    // 模块信息
    protected List<RegistryConfig> registries;    // 注册中心
    private Integer                callbacks;    // callback实例个数限制
    protected String              onconnect;   // 连接事件
    protected String              ondisconnect;   // 断开事件
    // 服务暴露或引用的scope,如果为local,则表示只在当前JVM内查找.
    private String scope;
    

    注册中心registries应该是比较重要的属性信息了,包含了注册中心的数据,demo中就设置了zk的相关属性信息,后期暴露也主要是把服务按照约定的协议推送给注册中心。

    其他继承的类的属性更多的是涉及到系统管理、监控等层级的属性,在此不做过多介绍了

    3.2、配置注入

    在上面说到bean继承了IntializingBean,那肯定就使用了afterPropertiesSet方法

    PS:注意在运行到这个时候,servicebean实例化是已经完成了的。

    public void afterPropertiesSet() throws Exception {
        if (getProvider() == null) {
            // 提供方为null
            Map<String, ProviderConfig> providerConfigMap = applicationContext == null ? null  : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class, false, false);
            // 此处的applicationContext就是通过继承ApplicationContextAware注入的Spring IOC容器
            // 获取所有类型是ProviderConfig的bean信息
            if (providerConfigMap != null && providerConfigMap.size() > 0) {
                Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null  : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
                // 获取ProtocolConfig
                if ((protocolConfigMap == null || protocolConfigMap.size() == 0)
                        && providerConfigMap.size() > 1) { // 兼容旧版本
                        // 如果没有protocolconfig同时有providerconfig
                    List<ProviderConfig> providerConfigs = new ArrayList<ProviderConfig>();
                    for (ProviderConfig config : providerConfigMap.values()) {
                        if (config.isDefault() != null && config.isDefault().booleanValue()) {
                            // config不为空,而且是默认值
                            providerConfigs.add(config);
                        }
                    }
                    if (providerConfigs.size() > 0) {
                        setProviders(providerConfigs);
                    }
                } else {
                    ProviderConfig providerConfig = null;
                    for (ProviderConfig config : providerConfigMap.values()) {
                        if (config.isDefault() == null || config.isDefault().booleanValue()) {
                            if (providerConfig != null) {
                                throw new IllegalStateException("Duplicate provider configs: " + providerConfig + " and " + config);
                            }
                            providerConfig = config;
                        }
                    }
                    if (providerConfig != null) {
                        // 默认的只应该存在一个providerconfig信息
                        setProvider(providerConfig);
                    }
                }
            }
        }
        if (getApplication() == null
                && (getProvider() == null || getProvider().getApplication() == null)) {
            Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);
            if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
                ApplicationConfig applicationConfig = null;
                for (ApplicationConfig config : applicationConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        if (applicationConfig != null) {
                            throw new IllegalStateException("Duplicate application configs: " + applicationConfig + " and " + config);
                        }
                        applicationConfig = config;
                    }
                }
                if (applicationConfig != null) {
                    // 填充ApplicationConfig信息
                    setApplication(applicationConfig);
                }
            }
        }
        if (getModule() == null
                && (getProvider() == null || getProvider().getModule() == null)) {
            Map<String, ModuleConfig> moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, false, false);
            if (moduleConfigMap != null && moduleConfigMap.size() > 0) {
                ModuleConfig moduleConfig = null;
                for (ModuleConfig config : moduleConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        if (moduleConfig != null) {
                            throw new IllegalStateException("Duplicate module configs: " + moduleConfig + " and " + config);
                        }
                        moduleConfig = config;
                    }
                }
                if (moduleConfig != null) {
                    // 填充ModuleConfig信息
                    setModule(moduleConfig);
                }
            }
        }
        if ((getRegistries() == null || getRegistries().size() == 0)
                && (getProvider() == null || getProvider().getRegistries() == null || getProvider().getRegistries().size() == 0)
                && (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().size() == 0)) {
            Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);
            if (registryConfigMap != null && registryConfigMap.size() > 0) {
                List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
                for (RegistryConfig config : registryConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        registryConfigs.add(config);
                    }
                }
                if (registryConfigs != null && registryConfigs.size() > 0) {
                    // 填充注册信息 
                    super.setRegistries(registryConfigs);
                }
            }
        }
        if (getMonitor() == null
                && (getProvider() == null || getProvider().getMonitor() == null)
                && (getApplication() == null || getApplication().getMonitor() == null)) {
            Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);
            if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
                MonitorConfig monitorConfig = null;
                for (MonitorConfig config : monitorConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        if (monitorConfig != null) {
                            throw new IllegalStateException("Duplicate monitor configs: " + monitorConfig + " and " + config);
                        }
                        monitorConfig = config;
                    }
                }
                if (monitorConfig != null) {
                    // 填充监控信息
                    setMonitor(monitorConfig);
                }
            }
        }
        if ((getProtocols() == null || getProtocols().size() == 0)
                && (getProvider() == null || getProvider().getProtocols() == null || getProvider().getProtocols().size() == 0)) {
            Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null  : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
            if (protocolConfigMap != null && protocolConfigMap.size() > 0) {
                List<ProtocolConfig> protocolConfigs = new ArrayList<ProtocolConfig>();
                for (ProtocolConfig config : protocolConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        protocolConfigs.add(config);
                    }
                }
                if (protocolConfigs != null && protocolConfigs.size() > 0) {
                    // 设置使用的协议
                    super.setProtocols(protocolConfigs);
                }
            }
        }
        if (getPath() == null || getPath().length() == 0) {
            if (beanName != null && beanName.length() > 0 
                    && getInterface() != null && getInterface().length() > 0
                    && beanName.startsWith(getInterface())) {
                setPath(beanName);
            }
        }
        if (! isDelay()) {
            // 如果没有设置延迟,则立即暴露出去
            export();
        }
    }
    

    在这一段代码中就是从Spring IOC容器中获取合适的bean注入到ServiceBean中,例如使用的服务信息、服务注册中心、使用的协议、均衡负责的方式(provider的loanBanance)、监控等。

    3.3、暴露服务启动

    服务暴露其实就是export函数,如果设置了延迟,则会在ApplicationListener的事件中去暴露服务。

    public void onApplicationEvent(ApplicationEvent event) {
        if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
         // 当前事件是bean刷新结束
            if (isDelay() && ! isExported() && ! isUnexported()) {
                  // 是延期而且还没有暴富同时 没有 不希望 暴露服务
                if (logger.isInfoEnabled()) {
                    logger.info("The service ready on spring started. service: " + getInterface());
                }
                // 跳转到ServiceConfig类中
                export();
            }
        }
    }
    

    ServiceConfig 类

    public synchronized void export() {
        // 这里面的provider等信息都是之前注入的信息属性
        if (provider != null) {
            if (export == null) {
                export = provider.getExport();
            }
            if (delay == null) {
                delay = provider.getDelay();
            }
        }
        if (export != null && ! export.booleanValue()) {
            return;
        }
        if (delay != null && delay > 0) {
             // 如果设置了延迟,则设置成为守护线程,睡眠延迟的时间数,再执行暴露服务的任务
            Thread thread = new Thread(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(delay);
                    } catch (Throwable e) {
                    }
                    doExport();
                }
            });
            thread.setDaemon(true);
            thread.setName("DelayExportServiceThread");
            thread.start();
        } else {
            doExport();
        }
    }
    

    在doExport方法中更多的是对一些数据的check操作,随后来到了doExportUrls方法

    private void doExportUrls() {
        List<URL> registryURLs = loadRegistries(true);
        // 先获取注册中心的属性信息
        for (ProtocolConfig protocolConfig : protocols) {
            // 存在多个暴露协议,例如DUBBO、HTTP等,依次对外暴露
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }
    

    3.4、服务注册中心属性获取

    AbstractInterfaceConfig 类

    protected List<URL> loadRegistries(boolean provider) {
        checkRegistry();
        // 兼容老版本的dubbo服务
        List<URL> registryList = new ArrayList<URL>();
        if (registries != null && registries.size() > 0) {
            // 循环遍历所有的注册中心配置
            for (RegistryConfig config : registries) {
                String address = config.getAddress();
                // 注册中心的地址
                if (address == null || address.length() == 0) {
                    address = Constants.ANYHOST_VALUE;
                    // ANYHOST_VALUE = "0.0.0.0"
                }
                String sysaddress = System.getProperty("dubbo.registry.address");
                // 从系统属性中获取dubbo.registry.address的值
                if (sysaddress != null && sysaddress.length() > 0) {
                    address = sysaddress;
                    // 真存在这个数据,就替换掉之前的地址数据
                }
                if (address != null && address.length() > 0 
                        && ! RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
                    // RegistryConfig.NO_AVAILABLE = 'N\A',如果地址有效    
                    Map<String, String> map = new HashMap<String, String>();
                    appendParameters(map, application);
                    appendParameters(map, config);
                    map.put("path", RegistryService.class.getName());
                    map.put("dubbo", Version.getVersion());
                    map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
                    // 把应用程序、当前的注册配置信息、以及dubbo、时间戳等信息注入到map中
                    if (ConfigUtils.getPid() > 0) {
                        // 获取当前服务的进程PID
                        map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
                    }
                    if (! map.containsKey("protocol")) {
                        if (ExtensionLoader.getExtensionLoader(RegistryFactory.class).hasExtension("remote")) {
                            // 如果在SPI中包含了支持remote的类,则设置当前的协议为remote,否则是dubbo
                            map.put("protocol", "remote");
                        } else {
                            map.put("protocol", "dubbo");
                        }
                    }
                    List<URL> urls = UrlUtils.parseURLs(address, map);
                    // 这里为啥会生成一个list数据的URL信息呢?
                    // 主要的情况是address可能包含了多个,现实中zk也很多是zk集群了,使用逗号区分开来即可
                    // 其实是通过Pattern.compile("\\s*[|;]+\\s*")的正则方式区分开来的
                    for (URL url : urls) {
                        url = url.addParameter(Constants.REGISTRY_KEY, url.getProtocol());
                        // 添加注册协议,此处为dubbo
                        url = url.setProtocol(Constants.REGISTRY_PROTOCOL);
                        if ((provider && url.getParameter(Constants.REGISTER_KEY, true))
                                || (! provider && url.getParameter(Constants.SUBSCRIBE_KEY, true))) {
                                // 一定是注册方的信息,则添加到这个集合中
                            registryList.add(url);
                        }
                    }
                }
            }
        }
        return registryList;
    }
    

    最后生成的registryList数据可能为registry://127.0.0.1:2182/com.alibaba.dubbo.registry.RegistryService?application=dubbo-demo&client=zkclient&dubbo=2.5.3&group=dubbo-demo&owner=jwfy&pid=2772&registry=zookeeper&timestamp=1525276569763

    仅仅是包含了一些注册的基本数据罢了

    3.5、服务暴露

    现在来到了doExportUrlsFor1Protocol方法,当前protocolConfig为


    image
    private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
        String name = protocolConfig.getName();
        // 获取对外暴露的协议,如果无效,则默认为dubbo
        if (name == null || name.length() == 0) {
            name = "dubbo";
        }
    
        String host = protocolConfig.getHost();
        if (provider != null && (host == null || host.length() == 0)) {
            // 如果提供的provider不为空而且暴露的协议配置host无效,设置为提供方的host数据
            host = provider.getHost();
        }
        boolean anyhost = false;
        if (NetUtils.isInvalidLocalHost(host)) {
           // 如果host无效,例如为null,空字符串,localhost,0.0.0.0,127开头的IP
            anyhost = true;
            try {
                host = InetAddress.getLocalHost().getHostAddress();
                // 获取本机IP
            } catch (UnknownHostException e) {
                logger.warn(e.getMessage(), e);
            }
            if (NetUtils.isInvalidLocalHost(host)) {
                // 还是无效
                if (registryURLs != null && registryURLs.size() > 0) {
                    for (URL registryURL : registryURLs) {
                        try {
                            Socket socket = new Socket();
                            try {
                                SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());
                                socket.connect(addr, 1000);
                                host = socket.getLocalAddress().getHostAddress();
                                // 通过socket套接字的方式获取host信息
                                break;
                            } finally {
                                try {
                                    socket.close();
                                } catch (Throwable e) {}
                            }
                        } catch (Exception e) {
                            logger.warn(e.getMessage(), e);
                        }
                    }
                }
                if (NetUtils.isInvalidLocalHost(host)) {
                    host = NetUtils.getLocalHost();
                }
            }
        }
    
        Integer port = protocolConfig.getPort();
        // 获取端口
        if (provider != null && (port == null || port == 0)) {
            port = provider.getPort();
        }
        final int defaultPort = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).getDefaultPort();
        // 通过SPI获取名称为name的实体类的默认端口信息,dubbo默认为20880
        if (port == null || port == 0) {
            port = defaultPort;
        }
        if (port == null || port <= 0) {
            port = getRandomPort(name);
            if (port == null || port < 0) {
                port = NetUtils.getAvailablePort(defaultPort);
                putRandomPort(name, port);
            }
            logger.warn("Use random available port(" + port + ") for protocol " + name);
        }
    
        Map<String, String> map = new HashMap<String, String>();
        if (anyhost) {
           // 如果起初的host为无效信息,设置anyhost为true
            map.put(Constants.ANYHOST_KEY, "true");
        }
        map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE);
        map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion());
        map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
        if (ConfigUtils.getPid() > 0) {
            map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
        }
        appendParameters(map, application);
        appendParameters(map, module);
        appendParameters(map, provider, Constants.DEFAULT_KEY);
        appendParameters(map, protocolConfig);
        appendParameters(map, this);
        if (methods != null && methods.size() > 0) {
            // 如果当前暴露的接口设置了暴露的方法列表
            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");
                    }
                }
                List<ArgumentConfig> arguments = method.getArguments();
                if (arguments != null && arguments.size() > 0) {
                    for (ArgumentConfig argument : arguments) {
                        //类型自动转换.
                        if(argument.getType() != null && argument.getType().length() >0){
                            Method[] methods = interfaceClass.getMethods();
                            //遍历所有方法
                            if(methods != null && methods.length > 0){
                                for (int i = 0; i < methods.length; i++) {
                                    String methodName = methods[i].getName();
                                    //匹配方法名称,获取方法签名.
                                    if(methodName.equals(method.getName())){
                                        Class<?>[] argtypes = methods[i].getParameterTypes();
                                        //一个方法中单个callback
                                        if (argument.getIndex() != -1 ){
                                            if (argtypes[argument.getIndex()].getName().equals(argument.getType())){
                                                appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                                            }else {
                                                throw new IllegalArgumentException("argument config error : the index attribute and type attirbute not match :index :"+argument.getIndex() + ", type:" + argument.getType());
                                            }
                                        } else {
                                            //一个方法中多个callback
                                            for (int j = 0 ;j<argtypes.length ;j++) {
                                                Class<?> argclazz = argtypes[j];
                                                if (argclazz.getName().equals(argument.getType())){
                                                    appendParameters(map, argument, method.getName() + "." + j);
                                                    if (argument.getIndex() != -1 && argument.getIndex() != j){
                                                        throw new IllegalArgumentException("argument config error : the index attribute and type attirbute not match :index :"+argument.getIndex() + ", type:" + argument.getType());
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }else if(argument.getIndex() != -1){
                            appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                        }else {
                            throw new IllegalArgumentException("argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
                        }
    
                    }
                }
            } // end of methods for
        }
        // 到这里参数已经处理完成了
    
        if (generic) {
            // 暴富的服务是否为GenericService类的子类
            map.put("generic", String.valueOf(true));
            map.put("methods", Constants.ANY_VALUE);
        } else {
            String revision = Version.getVersion(interfaceClass, version);
            // 设置接口的版本号(便于新旧版本的更迭)
            if (revision != null && revision.length() > 0) {
                map.put("revision", revision);
            }
    
            String[] methods = Wrapper.getWrapper(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)), ","));
                // 拼接的数据为 "getStr,print" 这样类似的数据
            }
        }
        if (! ConfigUtils.isEmpty(token)) {
            // 如果设置了校验令牌token
            if (ConfigUtils.isDefault(token)) {
               // 如果token的值为true或者default,随机设置一个UUID
                map.put("token", UUID.randomUUID().toString());
            } else {
                map.put("token", token);
            }
        }
        if ("injvm".equals(protocolConfig.getName())) {
            protocolConfig.setRegister(false);
            map.put("notify", "false");
        }
        // 导出服务
        String contextPath = protocolConfig.getContextpath();
        if ((contextPath == null || contextPath.length() == 0) && provider != null) {
            contextPath = provider.getContextpath();
        }
        URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
        
        /*
        到这里生成的URL属于具体暴富服务的信息数据了,例如dubbo://172.16.109.110:20880/com.jwfy.dubbo.product.ProductService?anyhost=true&application=dubbo-demo&default.loadbalance=random&dubbo=2.5.3&interface=com.jwfy.dubbo.product.ProductService&methods=print,getStr&owner=jwfy&pid=2772&side=provider&timestamp=1525313423899
        
        URL的协议是dubbo,也是暴露出去的协议
        */
    
        if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .hasExtension(url.getProtocol())) {
                // 如果配置工厂类有相应协议,则重新设置其值
            url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
        }
    
        String scope = url.getParameter(Constants.SCOPE_KEY);
        //配置为none不暴露,此处为null,也就是既可以暴露为本地服务也可以暴露为远程服务
        if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
    
            //配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)
            if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                // 真正的暴露服务,见3.5.1
                exportLocal(url);
            }
            //如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露远程服务)
            if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){
                if (logger.isInfoEnabled()) {
                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                }
                if (registryURLs != null && registryURLs.size() > 0
                        && url.getParameter("register", true)) {
                    // 对外暴露服务,必须要注册中心存在
                    for (URL registryURL : registryURLs) {
                        url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
                        URL monitorUrl = loadMonitor(registryURL);
                        // 加载监控中心,后面分析这块内容
                        if (monitorUrl != null) {
                            url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
                        }
                        if (logger.isInfoEnabled()) {
                            logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                        }
                        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                        // 远程服务暴露
                        Exporter<?> exporter = protocol.export(invoker);
                        exporters.add(exporter);
                    }
                } else {
                    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
    
                    Exporter<?> exporter = protocol.export(invoker);
                    exporters.add(exporter);
                }
            }
        }
        this.urls.add(url);
    }
    

    3.5.1、本地服务暴露

    本地服务暴露的入口是exportLocal(url);

    private void exportLocal(URL url) {
        if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
            // 这时候的url.getProtocol 为dubbo,而本地服务是injvm
            // 而且如果服务已经是injvm 则不需要走这个操作暴露了
            URL local = URL.valueOf(url.toFullString())
                    .setProtocol(Constants.LOCAL_PROTOCOL)
                    .setHost(NetUtils.LOCALHOST)
                    .setPort(0);
            // 替换为injvm协议和端口号,这个local数据就变成了
            /*
            injvm://127.0.0.1/com.jwfy.dubbo.product.ProductService?anyhost=true&application=dubbo-demo&default.loadbalance=random&dubbo=2.5.3&interface=com.jwfy.dubbo.product.ProductService&methods=print,getStr&owner=jwfy&pid=2772&side=provider&timestamp=1525313423899
            */
            Exporter<?> exporter = protocol.export(
                    proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
            // 通过getInvoke获取invoke对象,通过export进行暴露操作        
                    
            exporters.add(exporter);
            logger.info("Export dubbo service " + interfaceClass.getName() +" to local registry");
        }
    }
    

    现在应该是来到了真正服务暴露的入口了,Exporter<?> exporter = protocol.export(proxyFactory.getInvoker(ref, (Class) interfaceClass, local));,涉及的东西过多下一节再介绍,上面有关monitor的部分后续单独拆成一个小节去介绍

    相关文章

      网友评论

        本文标题:Dubbo 服务暴露 源码学习(上)(三)

        本文链接:https://www.haomeiwen.com/subject/qvglrftx.html