三Dubbo服务暴露源码分析--1服务端启动

三Dubbo服务暴露源码分析--1服务端启动

分析provider端暴露原理,首先从官方文档找到总体的逻辑,在把握全局逻辑后,找到源码研究切入点,并顺藤摸瓜,后面的细节也就呼之欲出。

image-20221214145145081

image-20221214145238667

上述过程可知,provider端服务暴露的起点,是serviceConfig类。

服务暴露逻辑:

1 serviceconfig获取provider的实际服务类ref,通过ProxyFactory.getInvoker,通过Wrappers.getWrapper,用字节码方式,构建字节码并反射创建代理类wrapper,然后创建匿名AbstractProxyInvoker类;

2 服务暴露关键在Invoker--->Exporter。以protocol协议(默认DubboProtocol协议)protocol.export()打开socket监听,并接收客户端发来的请求,从而实现provider端服务暴露。

本文代码以DubboDemo为例,进行源码分析。

image-20221213181123758 image-20230310181634064

3.0 服务端启动(初始化)

provider服务启动,创建applicationContext对象,加载配置文件provider.xml,并启动。

(这部分内容,看[2.5简单原理、与spring融合](# 2.5简单原理、与spring融合) )

从dubbo的spring.handlers文件看,xml中配置的Dubbo的命名空间处理器为DubboNamespaceHandler

image-20221214155920870

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:protocol/servece等的配置解析,分别添加了parser解析器,并提供了config的配置类。

可知,在provider端的applicationContext的初始化过程中,会加载RegistryConfig、ApplicationConfig、ProtocolConfig、ServiceBean等类作为bean加入spring容器。

其中,provider服务暴露的核心类为ServiceBean。

image-20230310181713496
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener, BeanNameAware {
//TAG1 setApplicationContext
  	//aware方法
  	public void setApplicationContext(ApplicationContext applicationContext) 
//TAG2 afterPropertiesSet
		//初始化方法
    public void afterPropertiesSet() throws Exception {
//TAG3 onApplicationEvent
      //监听事件
    public void onApplicationEvent(ApplicationEvent event) {
        if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
        	if (isDelay() && ! isExported() && ! isUnexported()) {
                if (logger.isInfoEnabled()) {
                    logger.info("The service ready on spring started. service: " + getInterface());
                }
                export();
            }
        }
    }

从上面ServiceBean的继承结构来看,首先实现ApplicationContextAware接口,会在实例化后、初始化前,执行aware的设置;然后实现bean周期接口InitializingBean,提供自定义afterPropertiesSet初始化,会在bean初始化阶段调用;然后实现ApplicationListener,会监听applicationContext发布的事件,通过onApplicationEvent方法,可知监听ContextRefreshedEvent刷新事件,然后执行export操作。

spring的bean的周期方法流程如下图所示:

image-20230310181737111
TAG1 setApplicationContext
ServiceBean
	public void setApplicationContext(ApplicationContext applicationContext) {
  	//设置Dubbo的serviceConfig内为启动时,创建的spring容器ClassPathXmlApplicationContext对象
		this.applicationContext = applicationContext;
		SpringExtensionFactory.addApplicationContext(applicationContext);
		if (applicationContext != null) {
      //设置常量
		    SPRING_CONTEXT = applicationContext;
		    try {
	            Method method = applicationContext.getClass().getMethod("addApplicationListener", new Class<?>[]{ApplicationListener.class}); // 兼容Spring2.0.1
          //获取applicationContext内addApplicationListener,添加当前serviceBean作为listener,加入config
	            method.invoke(applicationContext, new Object[] {this});
	            supportedApplicationListener = true;
	        } catch (Throwable t) {
                if (applicationContext instanceof AbstractApplicationContext) {
    	            try {
    	                Method method = AbstractApplicationContext.class.getDeclaredMethod("addListener", new Class<?>[]{ApplicationListener.class}); // 兼容Spring2.0.1
                        if (! method.isAccessible()) {
                            method.setAccessible(true);
                        }
    	                method.invoke(applicationContext, new Object[] {this});
                        supportedApplicationListener = true;
    	            } catch (Throwable t2) {
    	            }
	            }
	        }
		}
	}

servicebean的setApplicationContext方法,主要设置dubbo的context容器;其次,将当前servicebean作为listener,加入spring容器中,监听ContextRefreshedEvent事件。

TAG2 afterPropertiesSet

然后,在spring容器启动过程,解析provider.xml的bean的周期方法执行时,执行自定义的初始化方法。

public void afterPropertiesSet() throws Exception {
/**……………………………………………………………………………………………………设置providerConfig…………………………………………………………………………………………………… */
        if (getProvider() == null) { //1
          //从applicationContext中获取providerconfig为null,跳过设置(xml中dubbo:provider没有配置)
            Map<String, ProviderConfig> providerConfigMap = applicationContext == null ? null  : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class, false, false);
            if (providerConfigMap != null && providerConfigMap.size() > 0) { //2
                Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null  : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
                if ((protocolConfigMap == null || protocolConfigMap.size() == 0)
                        && providerConfigMap.size() > 1) { // 兼容旧版本 3
                    List<ProviderConfig> providerConfigs = new ArrayList<ProviderConfig>();
                    for (ProviderConfig config : providerConfigMap.values()) { //4
                        if (config.isDefault() != null && config.isDefault().booleanValue()) {
                            providerConfigs.add(config);
                        }
                    } //4
                    if (providerConfigs.size() > 0) {
                        setProviders(providerConfigs);
                    }
                } //3
              else { //3
                    ProviderConfig providerConfig = null;
                    for (ProviderConfig config : providerConfigMap.values()) { //4
                        if (config.isDefault() == null || config.isDefault().booleanValue()) { //5 
                            if (providerConfig != null) {
                                throw new IllegalStateException("Duplicate provider configs: " + providerConfig + " and " + config);
                            }
                            providerConfig = config;
                        } //5
                    } //4
                    if (providerConfig != null) {
                        setProvider(providerConfig);
                    }
                } //3 
            }//2 
        } //1
/**……………………………………………………………………………………………………设置applicationContext…………………………………………………………………………………………………… */
        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) {
                    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) {
                    setModule(moduleConfig);
                }
            }
        }
  
/**……………………………………………………………………………………………………设置registryConfigs…………………………………………………………………………………………………… */
        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) {
                  //向serviceconfig中设置配置的registryConfigs
                    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);
                }
            }
        }
/**……………………………………………………………………………………………………设置protocolConfigs…………………………………………………………………………………………………… */
        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())) {
//TAG2.1 setPath 设置服务接口path
                setPath(beanName);
            }
        }
  			//如果provider不是延迟暴露,立即执行export方法
        if (! isDelay()) {
            export();
        }
    }

初始化方法,主要是设置servicebean的相关属性,包括registryconfig等。

//TAG2.1 setPath

image-20221214165026646

TAG3 onApplicationEvent

在applicationContext启动完成后,发布ContextRefreshedEvent事件,servicebean执行对应的监听方法

    public void onApplicationEvent(ApplicationEvent event) {
      //监听ContextRefreshedEvent事件
        if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
        	if (isDelay() && ! isExported() && ! isUnexported()) {
                if (logger.isInfoEnabled()) {
                    logger.info("The service ready on spring started. service: " + getInterface());
                }
//TAG3.1 export 暴露接口
                export();
            }
        }
    }
TAG3.1 export->doExport->doExportUrls
ServiceConfig
 public synchronized void export() {
        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(); //实际暴露
        }
    }

    protected synchronized void doExport() {
        if (unexported) {
            throw new IllegalStateException("Already unexported!");
        }
        if (exported) {
            return;
        }
        exported = true; //设置已经暴露标志为true
        if (interfaceName == null || interfaceName.length() == 0) {
            throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
        }
/**……………………………………………………………………………………………………………………………………………………创建providerConfig……………………………………………………………………………………………… */
        checkDefault();
      /**     
      private void checkDefault() {
        if (provider == null) {
            provider = new ProviderConfig();
        }
        appendProperties(provider);  调用providerconfig的setter方法,设置属性
    }*/
      
				//如果provider不为null,用providerConfig内的属性,设置serviceConfig内的各个属性
        if (provider != null) {
            if (application == null) {
              //dubbo的applicationconfig--dubbo的应用配置
                application = provider.getApplication();
            }
            if (module == null) {
                module = provider.getModule();
            }
            if (registries == null) {
                registries = provider.getRegistries();
            }
            if (monitor == null) {
                monitor = provider.getMonitor();
            }
            if (protocols == null) {
                protocols = provider.getProtocols();
            }
        }
        if (module != null) {
            if (registries == null) {
                registries = module.getRegistries();
            }
            if (monitor == null) {
                monitor = module.getMonitor();
            }
        }
        if (application != null) {
            if (registries == null) {
                registries = application.getRegistries();
            }
            if (monitor == null) {
                monitor = application.getMonitor();
            }
        }
        if (ref instanceof GenericService) {
            interfaceClass = GenericService.class;
            generic = true;
        } else {
            try {
                interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                        .getContextClassLoader());
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            checkInterfaceAndMethods(interfaceClass, methods);
            checkRef();
            generic = false;
        }
        if(local !=null){
            if(local=="true"){
                local=interfaceName+"Local";
            }
            Class<?> localClass;
            try {
                localClass = ClassHelper.forNameWithThreadContextClassLoader(local);
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            if(!interfaceClass.isAssignableFrom(localClass)){
                throw new IllegalStateException("The local implemention class " + localClass.getName() + " not implement interface " + interfaceName);
            }
        }
        if(stub !=null){
            if(stub=="true"){
                stub=interfaceName+"Stub";
            }
            Class<?> stubClass;
            try {
                stubClass = ClassHelper.forNameWithThreadContextClassLoader(stub);
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            if(!interfaceClass.isAssignableFrom(stubClass)){
                throw new IllegalStateException("The stub implemention class " + stubClass.getName() + " not implement interface " + interfaceName);
            }
        }
      //为dubbo的applicationconfig设置属性
        checkApplication();
        checkRegistry();
        checkProtocol();
      //ServiceConfig属性设置
        appendProperties(this);
        checkStubAndMock(interfaceClass);
        if (path == null || path.length() == 0) {
            path = interfaceName;
        }
//TAG3.1 doExportUrls
        doExportUrls();
    }

TAG3.1 doExportUrls
ServiceConfig

    private void doExportUrls() {
//TAG3.1.1 loadRegistries
        List<URL> registryURLs = loadRegistries(true);
        for (ProtocolConfig protocolConfig : protocols) {
//TAG3.1.2 doExportUrlsFor1Protocol
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }
TAG3.1.1 loadRegistries(构造注册中心url)
AbstractInterfaceConfig
    protected List<URL> loadRegistries(boolean provider) {
        checkRegistry();
        List<URL> registryList = new ArrayList<URL>();
  			//registries为registryConfig
        if (registries != null && registries.size() > 0) {
            for (RegistryConfig config : registries) {
              //address为zookeeper://localhost:2181
                String address = config.getAddress();
                if (address == null || address.length() == 0) {
                	address = Constants.ANYHOST_VALUE;
                }
                String sysaddress = System.getProperty("dubbo.registry.address");
                if (sysaddress != null && sysaddress.length() > 0) {
                    address = sysaddress;
                }
/**……………………………………………………………………………………………………………………构造注册中心的URL……………………………………………………………………………………………………………………………… */
                if (address != null && address.length() > 0 
                        && ! RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
                  //构造URL时,存放参数对的map
                    Map<String, String> map = new HashMap<String, String>();
                  //将dubbo:application内参数放入map
                    appendParameters(map, application);
                  //将registryConfig内参数存入map
                    appendParameters(map, config);
                  //存registryconfig的path---path -> com.alibaba.dubbo.registry.RegistryService
                    map.put("path", RegistryService.class.getName());
                    map.put("dubbo", Version.getVersion());
                    map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
                    if (ConfigUtils.getPid() > 0) {
                        map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
                    }
                  //存protocol参数
                    if (! map.containsKey("protocol")) {
                        if (ExtensionLoader.getExtensionLoader(RegistryFactory.class).hasExtension("remote")) {
                            map.put("protocol", "remote");
                        } else {
                          //默认为dubbo的protocol协议
                            map.put("protocol", "dubbo");
                        }
                    }
//STATE1 map(上面)
//STATE2 urls(下面)
                    List<URL> urls = UrlUtils.parseURLs(address, map);
                    for (URL url : urls) {
                        url = url.addParameter(Constants.REGISTRY_KEY, url.getProtocol());
                        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;
    }

image-20221214171300730

在执行到该处时,registries为RegistryConfig。

//STATE1 map(上面)

image-20221214174036259

//STATE2 urls(下面)

image-20221214174111915

image-20221214174239587

构造registry注册中心的url地址。

registry://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-provider&dubbo=2.5.3&organization=dubbox&owner=programmer&pid=1702&registry=zookeeper&timestamp=1671010514655
TAG3.1.2 doExportUrlsFor1Protocol(构造服务端url:dubbo://192.168.0.101:20880)

image-20221214174946968

这里,protocols为provider.xml中配置的protocol属性解析的配置类

注意:
这里需要for轮询protocols,是因为Dubbo允许多协议配置,可以在不同服务上支持不同协议,或同一服务上同时支持多种协议。
ServiceConfig
 private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
        String name = protocolConfig.getName();
        if (name == null || name.length() == 0) {
            name = "dubbo";
        }

        String host = protocolConfig.getHost();
        if (provider != null && (host == null || host.length() == 0)) {
            host = provider.getHost();
        }
        boolean anyhost = false;
  			//host为null,即为无效localhost
        if (NetUtils.isInvalidLocalHost(host)) { //1 
            anyhost = true;
            try {
              //获取本地host:127.0.0.1
                host = InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException e) {
                logger.warn(e.getMessage(), e);
            }
/**………………………………如果host仍旧为localhost,首先从registryURL中链接注册中心并获取host;此时仍旧为localhost,就遍历网卡,获取第一个合理的IP地址…………………… */
          //localhost为无效localhost地址
            if (NetUtils.isInvalidLocalHost(host)) { //2
                if (registryURLs != null && registryURLs.size() > 0) { //3
                  //registry://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-provider&dubbo=2.5.3&organization=dubbox&owner=programmer&pid=16863&registry=zookeeper&timestamp=1671157653495
                    for (URL registryURL : registryURLs) { //4
                        try { //5
                            Socket socket = new Socket();
                            try { //6
                              //从registryURL中获取注册中心地址addr为localhost/127.0.0.1:2181
                                SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());
                              //连接zookeeper,并获取host为127.0.0.1
                                socket.connect(addr, 1000);
                                host = socket.getLocalAddress().getHostAddress();
                                break;
                            } //6
                          finally {//6
                                try {
                                    socket.close();
                                } catch (Throwable e) {
                                }
                            }//6
                        } //5
                      catch (Exception e) {
                            logger.warn(e.getMessage(), e);
                        }
                    }//4
                }//3
                if (NetUtils.isInvalidLocalHost(host)) {
                  //遍历本地网卡,返回第一个合理的IP----192.168.0.101
                    host = NetUtils.getLocalHost();
                }
            }//2
        }//1

/**………………………………………………………………………………………………………………dubbo服务端口………………………………………………………………………………………………………………………………………………………… */
        Integer port = protocolConfig.getPort(); //xml中配置的protocol端口20880
        if (provider != null && (port == null || port == 0)) {
            port = provider.getPort();
        }
        final int defaultPort = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).getDefaultPort();
        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容器
        Map<String, String> map = new HashMap<String, String>();
        if (anyhost) {
            map.put(Constants.ANYHOST_KEY, "true");
        }
        map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE);//配置side参数:provider,代表服务端
        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()));
        }
  			//dubbo的applicationconfig的参数,加入map
        appendParameters(map, application);
        appendParameters(map, module);
        appendParameters(map, provider, Constants.DEFAULT_KEY);
        appendParameters(map, protocolConfig);
        appendParameters(map, this);
  		//方法配置    private List<MethodConfig>  methods;
        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) {
            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);
            }

/**…………………………………………………………Wrapper.getWrapper创建provider亿服务接口的wrapper包装类,并获取方法名…………………………………………………………………… */
            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 {
              //将provider的服务端口内方法,放入参数容器中
                map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
            }
        }
        if (! ConfigUtils.isEmpty(token)) {
            if (ConfigUtils.isDefault(token)) {
                map.put("token", UUID.randomUUID().toString());
            } else {
                map.put("token", token);
            }
        }
  			//如果protocolconfig配置的协议为injvm(本地),此时设置protoconfig为非注册状态,则当前服务不会响应notify通知;配置为dubbo,则表示当前服务可以被通知
  			//将上面参数,加入map参数容器中
        if ("injvm".equals(protocolConfig.getName())) {
            protocolConfig.setRegister(false);
            map.put("notify", "false");
        }
        // 导出服务--com.alibaba.dubbo.demo.DemoService(provider端服务的path)
        String contextPath = protocolConfig.getContextpath();
        if ((contextPath == null || contextPath.length() == 0) && provider != null) {
            contextPath = provider.getContextpath();
        }
/**……………………………………………………………………………………………………构造服务端暴露服务URL…………………………………………………………………………………………………………………………………………… */
  	//dubbo://192.168.0.101:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demotest-provider&dubbo=2.5.3&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=20987&side=provider&timestamp=1671167576621----上述表示服务端暴露服务的URL
  //dubbo为当前protocol的协议;//192.168.0.101:20880/com.alibaba.dubbo.demo.DemoService为暴露服务的address+contextpath;后面键值对,为map中所添加的参数,包括所暴露的接口、方法、side等具体信息
        URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);

//TAG3.1.2.1ConfiguratorFactory--override动态配置协议处理(URL的协议头override的处理)
        if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .hasExtension(url.getProtocol())) {
            url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
        }
  
/**……………………………………………………………………………………………………根据scope暴露服务(本地或远程)……………………………………………………………………………………………………………………… */
//TAG3.1.2.2. 暴露服务--scope
				//scope为null,需要暴露本地和远程服务(none:不暴露;remote:只远程;local:本地;null:本地+远程)
        String scope = url.getParameter(Constants.SCOPE_KEY);
        //配置为none不暴露(scope为none,不暴露服务;为null,或者其他,此处暴露)
        if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) { //1 

            //配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)
            if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) { //2
//TAG1 本地暴露
                exportLocal(url);
            }//2
            //如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露远程服务)
            if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){ //2
                if (logger.isInfoEnabled()) {
                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                }
                if (registryURLs != null && registryURLs.size() > 0
                        && url.getParameter("register", true)) { //3
                    for (URL registryURL : registryURLs) { //4
                        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);
                        }
//TAG2 远程暴露
                        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));

                        Exporter<?> exporter = protocol.export(invoker);
                        exporters.add(exporter);
                    }//4
                } //3
              else {
                    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);

                    Exporter<?> exporter = protocol.export(invoker);
                    exporters.add(exporter);
                }
            }//2
        }//1
        this.urls.add(url);
    }

doExportUrlsFor1Protocol方法,主要完成如下任务:

1 获取host、port、classpath、method、协议等信息,存入map;
2 构造provider端服务暴露的url;
3 从暴露服务的url中获取scope,来决定本地暴露还是远程暴露-------核心内容
//TAG3.1.2.1ConfiguratorFactory--override动态配置协议处理(URL的协议头为override)
@SPI
public interface ConfiguratorFactory {

    /**
     * get the configurator instance.
     * 
     * @param url - configurator url.
     * @return configurator instance.
     */
    @Adaptive("protocol")
    Configurator getConfigurator(URL url);

}
image-20221216133826364
//TAG3.1.2.2. 暴露服务--scope
URL中scope定义的暴露规则:none:不暴露;remote:只远程;local:本地;null:本地+远程

本地暴露:暴露在JVM中,不需要网络通信;

远程暴露:将IP、port等信息暴露给远程客户端,调用时需要网络通信。

Dubbo的provider端的服务,可以是服务者,也可以是消费者,因此存在调用自身服务的情况,如果调用自身服务,就不必通过网络通信,会增加执行时长并会因为网络的不确定性,导致服务的不稳定。因此,将这部分可能被自身调用的服务,进行本地暴露给JVM。

posted @ 2023-03-13 14:40  LeasonXue  阅读(107)  评论(0编辑  收藏  举报