dubbo源码学习二

2020-07-17 10:20:08 浏览数 (1)

昨天我们已经知道dubbo的入口可以通过自定义标签找到DubboNamespaceHandler找到解析自定义标签的相关类了。还有4个我们需要关注:ConfigCenterBean、ServiceBean、ReferenceBean、new AnnotationBeanDefintionParser()。

下面我们来逐一了解:

1.ConfigCenterBean

从继承关系来看,我们知道ConfigCenterBean继承ConfigCenterConfig,同时实现了三个接口:ApplicationContextAware、EnvironmentAware、DisposableBean。而我们知道实现Aware接口的Bean在被初始化之后,可以拿到一些对应的资源。也就是实现ApplicationContextAware、EnviromentAware会在bean初始化之后,会被注入到ApplicationContext和Enviroment中。而DisposableBean则是进行Bean销毁的扩展入口,方便在销毁之前做一些个性化的操作。因此我们在里面可以重写destory方法,同时对ApplicationContext和Enviroment进行了set和get:

ConfigCenterBean

代码语言:javascript复制
//配置中心Bean
public class ConfigCenterBean extends ConfigCenterConfig implements ApplicationContextAware, DisposableBean, EnvironmentAware {
    //上下文信息
    private transient ApplicationContext applicationContext;

    private Boolean includeSpringEnv = false;

    //设置上下文,同将上下文添加到Spring扩展工厂中
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        SpringExtensionFactory.addApplicationContext(applicationContext);
    }

    //复写销毁方法
    @Override
    public void destroy() throws Exception {

    }

    //设置环境变量配置信息
    @Override
    public void setEnvironment(Environment environment) {
        if (includeSpringEnv) {
            // Get PropertySource mapped to 'dubbo.properties' in Spring Environment.
            //获取配置资源映射到dubbo.properties
            setExternalConfig(getConfigurations(getConfigFile(), environment));
            //在Spring Environment里获取配置资源映射到application.dubbo.properties
            setAppExternalConfig(getConfigurations(StringUtils.isNotEmpty(getAppConfigFile()) ? getAppConfigFile() : ("application."   getConfigFile()), environment));
        }
    }

    //获取配置
    private Map<String, String> getConfigurations(String key, Environment environment) {
        //通过传入的环境变量拿到配置
        Object rawProperties = environment.getProperty(key, Object.class);
        Map<String, String> externalProperties = new HashMap<>();
        try {
            //如果配置是map形式的,则将map放入到扩展配置中
            if (rawProperties instanceof Map) {
                externalProperties.putAll((Map<String, String>) rawProperties);
             //如果是String的,则解析配置,同时将其放入到map中
            } else if (rawProperties instanceof String) {
                externalProperties.putAll(ConfigurationUtils.parseProperties((String) rawProperties));
            }
            //配置环境信息,同时扩展配置为空,则将配置资源放入到map中 
            //externalProperties.put(k, (String) v);
            if (environment instanceof ConfigurableEnvironment && externalProperties.isEmpty()) {
                ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment;
                PropertySource propertySource = configurableEnvironment.getPropertySources().get(key);
                if (propertySource != null) {
                    Object source = propertySource.getSource();
                    if (source instanceof Map) {
                        ((Map<String, Object>) source).forEach((k, v) -> {
                            externalProperties.put(k, (String) v);
                        });
                    }
                }
            }
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
        return externalProperties;
    }

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public Boolean getIncludeSpringEnv() {
        return includeSpringEnv;
    }

    public void setIncludeSpringEnv(Boolean includeSpringEnv) {
        this.includeSpringEnv = includeSpringEnv;
    }

}
ConfigCenterConfig

ConfigCenterConfig里面的参数信息,由于里面基本上是参数信息,这里只放入参数信息:

代码语言:javascript复制
//配置中心配置
public class ConfigCenterConfig extends AbstractConfig {
    //原子类 inited
    private AtomicBoolean inited = new AtomicBoolean(false);
    //协议
    private String protocol;
    //地址
    private String address;
    //端口
    private Integer port;
    //集群 配置中心集群
    private String cluster;
    //命名空间 配置中心命名空间 dubbo
    private String namespace = CommonConstants.DUBBO;
    //配置中心 group dubbo
    private String group = CommonConstants.DUBBO;
    private String username;
    private String password;
    //超时时间
    private Long timeout = 3000L;
    //最高优先级 配置中心
    private Boolean highestPriority = true;
    private Boolean check = true;
    //dubbo.properties 配置文件
    private String configFile = CommonConstants.DEFAULT_DUBBO_PROPERTIES;
    //appConfig文件
    private String appConfigFile;
    //参数信息
    private Map<String, String> parameters;
    //扩展配置
    private Map<String, String> externalConfiguration;
    //app扩展配置
    private Map<String, String> appExternalConfiguration;
AbstractConfig

AbstractConfig:主要有几个方法,都是将参数添加到map中的,appendParameters、appendAttributes、appendAnnotation、addIntoConfigManager,refresh方法刷新配置信息等,这里以appendParamenters为例:

代码语言:javascript复制
 //对参数进行append操作
    //参数集合,用于URL.parameters
    public static void appendParameters(Map<String, String> parameters, Object config) {
        appendParameters(parameters, config, null);
    }

    //append参数信息
    @SuppressWarnings("unchecked")
    public static void appendParameters(Map<String, String> parameters, Object config, String prefix) {
        if (config == null) {
            return;
        }
        //拿到方法信息
        Method[] methods = config.getClass().getMethods();
        //对方法进行遍历
        for (Method method : methods) {
            try {
                //拿到方法的名称
                String name = method.getName();
                //判断其是不是Getter方法,如果是get方法则为true
                if (MethodUtils.isGetter(method)) {
                    //获取方法的注解参数
                    /**
                     * class ExampleConfig {
                     *      // Dubbo will try to get "dubbo.example.alias_for_item=xxx" from    .properties, if you want to use the original property
                     *      // "dubbo.example.item=xxx", you need to set useKeyAsProperty=false.
                     *      @Parameter(key = "alias_for_item")
                     *      public getItem();
                     *  }
                     * }
                     */
                    Parameter parameter = method.getAnnotation(Parameter.class);
                    if (method.getReturnType() == Object.class || parameter != null && parameter.excluded()) {
                        continue;
                    }
                    //拿到参数的key
                    String key;
                    if (parameter != null && parameter.key().length() > 0) {
                        key = parameter.key();
                    } else {
                        key = calculatePropertyFromGetter(name);
                    }
                    //拿到最原始的参数信息,获取属性值
                    Object value = method.invoke(config);
                    String str = String.valueOf(value).trim();
                    if (value != null && str.length() > 0) {
                        //转义
                        if (parameter != null && parameter.escaped()) {
                            str = URL.encode(str);
                        }
                        if (parameter != null && parameter.append()) {
                            //做拼接操作
                            // default. 里获取,适用于 ServiceConfig =》ProviderConfig 、ReferenceConfig =》ConsumerConfig
                            String pre = parameters.get(key);
                            if (pre != null && pre.length() > 0) {
                                str = pre   ","   str;
                            }
                        }
                        //添加前缀信息
                        // 通过 `parameters` 属性配置,例如 `AbstractMethodConfig.parameters` 。
                        if (prefix != null && prefix.length() > 0) {
                            key = prefix   "."   key;
                        }
                        //放入key和value信息
                        parameters.put(key, str);
                    } else if (parameter != null && parameter.required()) {
                        throw new IllegalStateException(config.getClass().getSimpleName()   "."   key   " == null");
                    }
                    //如果不是getter方法,看是不是参数getter方法
                } else if (isParametersGetter(method)) {
                    //放到map中
                    Map<String, String> map = (Map<String, String>) method.invoke(config, new Object[0]);
                    parameters.putAll(convert(map, prefix));
                }
            } catch (Exception e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
        }
    }

appendParameter方法进行的操作:

1.首先对传入的config进行判断,如果为空,则直接返回

2.通过config通过反射拿到方法method()数组

3.对method()数组进行遍历,或者方法的名称

4.如果方法的名称是getter方法,则获取getter方法上的注解参数信息,从而拿到它的key,从而拿到value值

5.通过配置获取value值,而value值是通过调用method.invoke()反射的底层方法获取的。而获取之后,如果参数是匹配escaped()方法的话,则会调用URL.encode方法对value值进行encode。如果是参数是追加,则做拼接操作。

6.最终将key和value值放入到parameter中。

2.ServiceBean

先看类图信息:

从类图,我们可以看到:

serviceBean信息 继承 服务配置 并实现InitializingBean和DisposableBean,以及各种aware。因此可以看到其必定存在相关的BeanName、ApplicationContextEventPublisher、ApplicationContext、Disposable、InitialingzingBena相关的方法:可以看到,这里我们就不展开说明了,主要关注实现InitialingziingBean之后的AfterPropertiesSet方法。

代码语言:javascript复制
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
        ApplicationContextAware, BeanNameAware, ApplicationEventPublisherAware {

//复写afterPropertiesSet方法,设置path
@Override
public void afterPropertiesSet() throws Exception {
    if (StringUtils.isEmpty(getPath())) {
        if (StringUtils.isNotEmpty(beanName)
                && StringUtils.isNotEmpty(getInterface())
                && beanName.startsWith(getInterface())) {
            //重点关注
            setPath(beanName);
        }
    }
}

/**
 * @since 2.6.5
 */
//进行exported
@Override
public void exported() {
    super.exported();
    // Publish ServiceBeanExportedEvent
    publishExportEvent();
  }
            
/**
 * @since 2.6.5
 */
private void publishExportEvent() {
    ServiceBeanExportedEvent exportEvent = new ServiceBeanExportedEvent(this);
   applicationEventPublisher.publishEvent(exportEvent);
  }  
 
//可以看到都会指向SerivceBean和Serviceconfig            
public ServiceBeanExportedEvent(ServiceBean serviceBean) {
     super(serviceBean);
  }

public ServiceConfigExportedEvent(ServiceConfig source) {
     super(source);
  }            
}
ServiceBean
ServiceConfig
代码语言:javascript复制
//服务配置信息 继承 服务配置基础信息 重要:export()方法
public class ServiceConfig<T> extends ServiceConfigBase<T> {
     public void setPath(String path) {
        this.path = path;
    }
}

没有看到我们想要的信息,但是我们却可以在看到SerivceCofing中存在的信息:暴露和延迟:

ServiceConfig#export方法
代码语言:javascript复制
public synchronized void export() {
    if (!shouldExport()) {
        return;
    }

    if (bootstrap == null) {
        bootstrap = DubboBootstrap.getInstance();
        bootstrap.init();
    }
    // 检测 provider 是否为空,为空则新建一个,并通过系统变量为其初始化
    checkAndUpdateSubConfigs();

    //init serviceMetadata
    //初始化服务元数据
    serviceMetadata.setVersion(version);
    serviceMetadata.setGroup(group);
    serviceMetadata.setDefaultGroup(group);
    serviceMetadata.setServiceType(getInterfaceClass());
    serviceMetadata.setServiceInterfaceName(getInterface());
    serviceMetadata.setTarget(getRef());

    //是否应该延迟
    if (shouldDelay()) {
        //延迟定时任务
        DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
    } else {
        //做暴露操作
        doExport();
    }

    exported();
}

1.取消暴露服务,则直接返回

2.进行bootstrap处理,如果bootstrap为空,则首先进行实例化,然后进行初始化

3.检测 provider 是否为空,为空则新建一个,并通过系统变量为其初始化

4.初始化服务提供者的元数据信息

5.进行服务暴露前先判断是否进行延迟操作,如果进行延迟,执行定时任务操作

6.如果不延迟,则直接进行暴露操作

ServiceConfig#checkAndUpdateSubConfigs方法

进行检查和更新:

代码语言:javascript复制
// 检测 provider 是否为空,为空则新建一个,并通过系统变量为其初始化
private void checkAndUpdateSubConfigs() {
    // Use default configs defined explicitly with global scope
    completeCompoundConfigs();
    checkDefault();
    checkProtocol();
    // init some null configuration.
    List<ConfigInitializer> configInitializers = ExtensionLoader.getExtensionLoader(ConfigInitializer.class)
            .getActivateExtension(URL.valueOf("configInitializer://"), (String[]) null);
    configInitializers.forEach(e -> e.initServiceConfig(this));

    // if protocol is not injvm checkRegistry
    if (!isOnlyInJvm()) {
        checkRegistry();
    }
    this.refresh();

    if (StringUtils.isEmpty(interfaceName)) {
        throw new IllegalStateException("<dubbo:service interface="" /> interface not allow null!");
    }
    //ref 非GenericService
    if (ref instanceof GenericService) {
        interfaceClass = GenericService.class;
        if (StringUtils.isEmpty(generic)) {
            generic = Boolean.TRUE.toString();
        }
    } else {
        try {
            interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                    .getContextClassLoader());
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        //对interfaceClass,以及<dubbo:method>标签中的必要字段进行检查
        checkInterfaceAndMethods(interfaceClass, getMethods());
        //对ref进行合法性校验
        checkRef();
        //设置generic=false
        generic = Boolean.FALSE.toString();
    }
    //local和stub在功能上应该是一样的,用于配置本地存根
    //目前local已经废弃,stub已经替代stub
    if (local != null) {
        if ("true".equals(local)) {
            local = interfaceName   "Local";
        }
        Class<?> localClass;
        try {
            //获取本地存根类
            localClass = ClassUtils.forNameWithThreadContextClassLoader(local);
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        if (!interfaceClass.isAssignableFrom(localClass)) {
            throw new IllegalStateException("The local implementation class "   localClass.getName()   " not implement interface "   interfaceName);
        }
    }
    if (stub != null) {
        if ("true".equals(stub)) {
            stub = interfaceName   "Stub";
        }
        Class<?> stubClass;
        try {
            stubClass = ClassUtils.forNameWithThreadContextClassLoader(stub);
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        if (!interfaceClass.isAssignableFrom(stubClass)) {
            throw new IllegalStateException("The stub implementation class "   stubClass.getName()   " not implement interface "   interfaceName);
        }
    }
    checkStubAndLocal(interfaceClass);
    ConfigValidationUtils.checkMock(interfaceClass, this);
    ConfigValidationUtils.validateServiceConfig(this);
    postProcessConfig();
}

进行延迟操作:

ServiceConfig#shouldDelay方法
代码语言:javascript复制
//进行延迟操作
public boolean shouldDelay() {
    Integer delay = getDelay();
    return delay != null && delay > 0;
}

//获取延迟操作
@Override
public Integer getDelay() {
    return (delay == null && provider != null) ? provider.getDelay() : delay;
}

进行服务暴露:

ServiceConfig#doExport方法
代码语言:javascript复制
 //进行服务暴露
protected synchronized void doExport() {
    //如果取消暴露,则抛异常,已经取消暴露
    if (unexported) {
        throw new IllegalStateException("The service "   interfaceClass.getName()   " has already    unexported!");
    }
    //如果已经暴露,则直接返回
    if (exported) {
        return;
    }
    exported = true;
    //如果路径为空,则给接口名称
    if (StringUtils.isEmpty(path)) {
        path = interfaceName;
    }
    //重要 做url暴露操作
    doExportUrls();
}

1.首先判断是否取消服务暴露,如果是,则抛异常

2.如果已经暴露,直接返回

3.如果路径为空,则给定接口信息

4.做url暴露

ServiceConfig#doExportUrls方法
代码语言:javascript复制
//暴露url操作
//格式:protocol://username:password@host:port/path?key=value&key=value

//比如:dubbo://127.0.0.1:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&default.delay=-1&default.retries=0&default.service.filter=demoFilter&delay=-1&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=19031&side=provider&timestamp=1519651641799
@SuppressWarnings({"unchecked", "rawtypes"})
private void doExportUrls() {
    //拿到ServiceRespository
    ServiceRepository repository = ApplicationModel.getServiceRepository();
    //拿到接口Class,注册服务信息
    ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
    //注册服务提供者的相关信息:服务的名称。引用信息实现类,服务描述信息、配置信息、服务的元数据信息
    repository.registerProvider(
            getUniqueServiceName(),
            ref,
            serviceDescriptor,
            this,
            serviceMetadata
    );
    //加载注册中心url:注册服务列表
    List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
    //对协议配置配置遍历,并在每个协议下暴露服务  重要
    for (ProtocolConfig protocolConfig : protocols) {
        String pathKey = URL.buildKey(getContextPath(protocolConfig)
                .map(p -> p   "/"   path)
                .orElse(path), group, version);
        // In case user specified path, register service one more time to map it to path.
        repository.registerService(pathKey, interfaceClass);
        // TODO, uncomment this line once service key is unified
        serviceMetadata.setServiceKey(pathKey);
        //组装url  非常重要
        doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }
}

在组装url之前,会先进行注册服务提供者的相关信息,同时加载注册中心的url列表信息,对每个协议配置遍历,并在每个协议下暴露服务。由于注册服务信息是一个列表。

ServiceConfig#doExportUrlsFor1Protocol方法:

代码语言:javascript复制
//组装url,url是dubbo配置的载体,通过url可让Dubbo的各种配置在各个模块之间传递。
//url之于dubbo,犹如水之于鱼,非常重要
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
    /**========================组装url ===================**/
    //拿到协议信息,如果协议名称为空,则设置协议名称默认为dubbo
    String name = protocolConfig.getName();
    if (StringUtils.isEmpty(name)) {
        name = DUBBO;
    }
    //添加 side信息
    Map<String, String> map = new HashMap<String, String>();
    map.put(SIDE_KEY, PROVIDER_SIDE);

    //通过反射将对象的字段信息添加到map中
    ServiceConfig.appendRuntimeParameters(map);
    AbstractConfig.appendParameters(map, getMetrics());
    AbstractConfig.appendParameters(map, getApplication());
    AbstractConfig.appendParameters(map, getModule());
    // remove 'default.' prefix for configs from ProviderConfig
    // appendParameters(map, provider, Constants.DEFAULT_KEY);
    AbstractConfig.appendParameters(map, provider);
    AbstractConfig.appendParameters(map, protocolConfig);
    AbstractConfig.appendParameters(map, this);
    MetadataReportConfig metadataReportConfig = getMetadataReportConfig();
    //外部化配置的时候用到
    // 添加元数据信息
    //dubbo.metadata-report.address=zookeeper://127.0.0.1:2181
    if (metadataReportConfig != null && metadataReportConfig.isValid()) {
        map.putIfAbsent(METADATA_KEY, REMOTE_METADATA_STORAGE_TYPE);
    }
    //如果获取的方法为methodConfig的集合不为空,
    // methodConfig中存储了<dubbo:method>标签的配置信息,则对其进行遍历
    //服务端方法级别:
    /**
     * <dubbo:service interface="...">
     *   <dubbo:method name="..." loadbalance="roundrobin"/>
     * </dubbo:service>
     */
    /**
     * <dubbo:reference>
     *    <dubbo:method name="findFoo" retries="2" />
     * </dubbo:reference>
     */
    if (CollectionUtils.isNotEmpty(getMethods())) {
        for (MethodConfig method : getMethods()) {
            AbstractConfig.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();
            //检测type属性是否为空,或者空串
            if (CollectionUtils.isNotEmpty(arguments)) {
                for (ArgumentConfig argument : arguments) {
                    // convert argument type
                    if (argument.getType() != null && argument.getType().length() > 0) {
                        Method[] methods = interfaceClass.getMethods();
                        // visit all methods
                        if (methods.length > 0) {
                            for (int i = 0; i < methods.length; i  ) {
                                String methodName = methods[i].getName();
                                // target the method, and get its signature
                                //目标方法,获取它的信息
                                if (methodName.equals(method.getName())) {
                                    Class<?>[] argtypes = methods[i].getParameterTypes();
                                    // one callback in the method中的参数名称是否一致,不一致,则抛出异常
                                    if (argument.getIndex() != -1) {
                                        if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {
                                            AbstractConfig.appendParameters(map, argument, method.getName()   "."   argument.getIndex());
                                        } else {
                                            throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :"   argument.getIndex()   ", type:"   argument.getType());
                                        }
                                    } else {
                                        // multiple callbacks in the method
                                        for (int j = 0; j < argtypes.length; j  ) {
                                            Class<?> argclazz = argtypes[j];
                                            //从参数类表中查找类型名称为argument.type的参数
                                            if (argclazz.getName().equals(argument.getType())) {
                                                AbstractConfig.appendParameters(map, argument, method.getName()   "."   j);
                                                if (argument.getIndex() != -1 && argument.getIndex() != j) {
                                                    throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :"   argument.getIndex()   ", type:"   argument.getType());
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        //用户没有配置type属性,但配置了index属性,且index!=-1
                        //添加ArgumentConfig字段信息到map中
                    } else if (argument.getIndex() != -1) {
                        AbstractConfig.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
    }

    //检查generic是否为true,并且根据检测结果向map中添加不同的信息
    if (ProtocolUtils.isGeneric(generic)) {
        map.put(GENERIC_KEY, generic);
        map.put(METHODS_KEY, ANY_VALUE);
     //否则,获取版本号信息、如果版本信息不为空,同时长度>0,则放入版本信息
     //同时为接口生成包装类,包装类中包含了接口的详细信息
    } else {
        String revision = Version.getVersion(interfaceClass, version);
        if (revision != null && revision.length() > 0) {
            map.put(REVISION_KEY, revision);
        }

        String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
        //添加方法到map中,如果包含多个方法名,则用逗号隔开,比如method=init,destory
        if (methods.length == 0) {
            logger.warn("No method found in service interface "   interfaceClass.getName());
            map.put(METHODS_KEY, ANY_VALUE);
        } else {
            map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
        }
    }

    /**
     * Here the token value configured by the provider is used to assign the value to ServiceConfig#token
     */
    if(ConfigUtils.isEmpty(token) && provider != null) {
        token = provider.getToken();
    }
    //添加token信息
    if (!ConfigUtils.isEmpty(token)) {
        if (ConfigUtils.isDefault(token)) {
            //随机生成token信息
            map.put(TOKEN_KEY, UUID.randomUUID().toString());
        } else {
            //添加token信息
            map.put(TOKEN_KEY, token);
        }
    }
    //init serviceMetadata attachments
    //初始化服务元数据标签
    serviceMetadata.getAttachments().putAll(map);

    // export service
    //host、port信息
    String host = findConfigedHosts(protocolConfig, registryURLs, map);
    Integer port = findConfigedPorts(protocolConfig, name, map);
    /**
     * 组装url   重要
     * URL url = new URL(name, host, port, (contextPath == null ||
     * contextPath.length() == 0 ? "" : contextPath   "/")   path, map);
     *  map中包含的信息:版本、时间戳、方法名以及各种配置对象的字段信息
     */
    URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p   "/"   path).orElse(path), map);

    // You can customize Configurator to append extra parameters
    //你可以自定义配置追加额外参数信息

    /**================ 组装完url,进行服务暴露,分为暴露到本地(jvm)和暴露到远程 ====================**/
    if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
            .hasExtension(url.getProtocol())) {
        //加载配置工厂,并生产配置实例,然后通过实例配置url
        url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
    }

    String scope = url.getParameter(SCOPE_KEY);
    // don't export when none is configured
    //如果scope==none,则什么都不做
    if (!SCOPE_NONE.equalsIgnoreCase(scope)) {

        // export to local if the config is not remote (export to remote only when config is remote)
        //暴露到本地
        if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
            //重要
            exportLocal(url);
        }
        // export to remote if the config is not local (export to local only when config is local)
        //暴露到远程
        if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
            if (CollectionUtils.isNotEmpty(registryURLs)) {
                for (URL registryURL : registryURLs) {
                    //if protocol is only injvm ,not register
                    if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
                        continue;
                    }
                    url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
                    //加载monitor链接
                    URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
                    if (monitorUrl != null) {
                        //将监控url作为参数加入到url中
                        url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
                    }
                    if (logger.isInfoEnabled()) {
                        if (url.getParameter(REGISTER_KEY, true)) {
                            logger.info("Register dubbo service "   interfaceClass.getName()   " url "   url   " to registry "   registryURL);
                        } else {
                            logger.info("Export dubbo service "   interfaceClass.getName()   " to url "   url);
                        }
                    }

                    // For providers, this is used to enable custom proxy to generate invoker
                    //为服务提供者ref生成Invoker
                    String proxy = url.getParameter(PROXY_KEY);
                    if (StringUtils.isNotEmpty(proxy)) {
                        registryURL = registryURL.addParameter(PROXY_KEY, proxy);
                    }

                    Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
                    //DelegateProviderMetaDataInvoker用于持有Invoker和ServiceConfig
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
                    //暴露服务,并生成Exporter
                    Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
                    exporters.add(exporter);
                }
                //不存在注册找那个性,仅暴露服务
            } else {
                if (logger.isInfoEnabled()) {
                    logger.info("Export dubbo service "   interfaceClass.getName()   " to url "   url);
                }
                //重要 invoker
                Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
                DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
                exporters.add(exporter);
            }
            /**
             * @since 2.7.0
             * ServiceData Store
             */
            WritableMetadataService metadataService = WritableMetadataService.getExtension(url.getParameter(METADATA_KEY, DEFAULT_METADATA_STORAGE_TYPE));
            if (metadataService != null) {
                metadataService.publishServiceDefinition(url);
            }
        }
    }
    this.urls.add(url);
}

首先它会进行url的组装,然后再进行服务暴露:而服务的暴露又分为本地暴露(jvm)和远程暴露。url在dubbo中是非常重要的。

一、组装过程:

1.首先添加协议信息,如果协议为空,则默认为dubbo协议

2.添加side信息信息

3.将原来的一些相关的服务提供者信息的map添加到map。通过反射拿到相关字段信息添加到map中。

4.外部化配置的时候,添加元数据信息

5.如果获取的方法为methodConfig的集合不为空,methodConifg中存储了dubbo:method标签的配置信息,对其进行遍历

6.获取 ArgumentConfig 列表,遍历ArgumentConifg如果type不为空,则通过反射获取interfaceClass的方法列表,通过比较方法名获取目标方法;通过反射获取目标方法的参数类型数组

7.检查genetic是否为true,并根据不同的情况添加不同的信息,如果不存在,则添加默认的

8.添加token信息,默认采用uuid生成,如果给定,则采用给定的

9.初始化服务元数据标签信息

appendParameters 这个方法出现的次数比较多,该方法用于将对象字段信息添加到 map 中。实现上则是通过反射获取目标对象的getter 方法,并调用该方法获取属性值。然后再通过 getter 方法名解析出属性名,比如从方法名 getName 中可解析出属性 name。如果用户传入了属性名前缀,此时需要将属性名加入前缀内容。最后将 <属性名,属性值> 键值对存入到 map 中就行了。

二、暴露服务

分为两种:本地服务暴露(jvm)和远程服务暴露,通过scope可以看到分为三种情况:

scope=none 不暴露服务

scope!=remote 暴露服务到本地,exportLocal(url)

scope!=local 暴露服务到远程,暴露到远程,会加载监控monitor链接,将其添加到url中,为服务提供类ref生产invoker,DelagateProviderMetadataInvoker用于持有Invoker和ServiceConfig,暴露服务,并生成Exporter。

因此我们必然需要对Invoker进行研究,也即

代码语言:javascript复制
/**
 * ProxyFactory. (API/SPI, Singleton, ThreadSafe)
 */
@SPI("javassist")
public interface ProxyFactory {
  @Adaptive({PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}

后面对invoker进行学习!

0 人点赞