Dubbo 服務暴露 源碼學習(上)(三)

筆記簡述
何所謂服務暴露,其實就是服務提供方把自己的服務提供出來,使得其他服務使用方能夠跨網絡(本地就不需要跨網絡)調用得到返回結果。
由于服務暴露整個的過程太長,故分為兩小節去學習源碼,本小節主要涉及到的內容是從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的部分后續單獨拆成一個小節去介紹

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,908評論 18 139
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,948評論 6 342
  • 斷裂了 無法拼接 開花了 沒有果實 分岔了 不能再修飾 是黑色 是金黃 是白色 什么樣的 什么人的 有你你都在 意...
    一憶光年閱讀 296評論 8 3
  • 【007】5班5組的各位伙伴們,這是咱們6月第二次作業雨的作業與評論匯總情況。感謝大家都付出了自己寶貴的注意力去關...
    淡泊人生的智慧閱讀 410評論 0 1
  • 世界雖然寬廣 卻只有一首我的 小小愿望詩 你也能露出笑容 希望遙遠的 某個地方不知名的你 如果好好訴說的話...
    Q點調皮閱讀 564評論 0 0