dubbo 使用说明


dubbo 使用说明

                 

             

                                       

自动配置类

           

DubboAutoConfiguration

@ConditionalOnProperty(
    prefix = "dubbo",
    name = {"enabled"},
    matchIfMissing = true
)
@Configuration
@AutoConfigureAfter({DubboRelaxedBindingAutoConfiguration.class})
@EnableConfigurationProperties({DubboConfigurationProperties.class})  //dubbo配置类
@EnableDubboConfig      //开启dubbo配置
public class DubboAutoConfiguration {
    public DubboAutoConfiguration() {
    }

    @ConditionalOnProperty(
        prefix = "dubbo.scan.",
        name = {"base-packages"}
    )   //存在dubbo.scan.base-packages属性
    @ConditionalOnBean(
        name = {"dubbo-service-class-base-packages"}
    )   //并且存在名为dubbo-service-class-base-packages的bean时,
    @Bean
    public ServiceAnnotationPostProcessor serviceAnnotationBeanProcessor(@Qualifier("dubbo-service-class-base-packages") Set<String> packagesToScan) {
        return new ServiceAnnotationPostProcessor(packagesToScan);
    }   //创建ServiceAnnotationPostProcessor的bean
}

            

EnableDubboConfig:是否开启dubbo配置bean多值绑定,默认开启

/**
 * As a convenient and multiple {@link EnableConfigurationBeanBinding}
 * in default behavior , is equal to single bean bindings with below convention prefixes of properties:
 * <ul>
 * <li>{@link ApplicationConfig} binding to property : "dubbo.application"</li>
 * <li>{@link ModuleConfig} binding to property :  "dubbo.module"</li>
 * <li>{@link RegistryConfig} binding to property :  "dubbo.registry"</li>
 * <li>{@link ProtocolConfig} binding to property :  "dubbo.protocol"</li>
 * <li>{@link MonitorConfig} binding to property :  "dubbo.monitor"</li>
 * <li>{@link ProviderConfig} binding to property :  "dubbo.provider"</li>
 * <li>{@link ConsumerConfig} binding to property :  "dubbo.consumer"</li>
 * </ul>
 * <p>

 * In contrast, on multiple bean bindings that requires to set {@link #multiple()} to be <code>true</code> :
   //开启多值绑定需设置multiple为true,默认为true
 * <ul>
 * <li>{@link ApplicationConfig} binding to property : "dubbo.applications"</li>
 * <li>{@link ModuleConfig} binding to property :  "dubbo.modules"</li>
 * <li>{@link RegistryConfig} binding to property :  "dubbo.registries"</li>
 * <li>{@link ProtocolConfig} binding to property :  "dubbo.protocols"</li>
 * <li>{@link MonitorConfig} binding to property :  "dubbo.monitors"</li>
 * <li>{@link ProviderConfig} binding to property :  "dubbo.providers"</li>
 * <li>{@link ConsumerConfig} binding to property :  "dubbo.consumers"</li>
 * </ul>
 *
 * @see EnableConfigurationBeanBinding
 * @see DubboConfigConfiguration
 * @see DubboConfigConfigurationRegistrar
 * @since 2.5.8
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.class) //导入DubboConfigConfigurationRegistrar类
public @interface EnableDubboConfig {

    /**
     * It indicates whether binding to multiple Spring Beans.
     *
     * @return the default value is <code>true</code>
     * @revised 2.5.9
     */
    boolean multiple() default true;

}

          

DubboConfigConfigurationRegistrar:注册bean信息

public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
                //注册bean的定义信息

        // initialize dubbo beans
        DubboSpringInitializer.initialize(registry);  //dubbo bean初始化

        // Config beans creating from props have move to ConfigManager
        //multuiple配置bean的创建移到configManager中
//        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
//                importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
//
//        boolean multiple = attributes.getBoolean("multiple");
//
//        // Single Config Bindings
//        registerBeans(registry, DubboConfigConfiguration.Single.class);
//
//        if (multiple) { // Since 2.6.6 https://github.com/apache/dubbo/issues/3193
//            registerBeans(registry, DubboConfigConfiguration.Multiple.class);
//        }

    }
}

                 

DubboSpringInitializer:dubbo初始化

public class DubboSpringInitializer {

    public static void initialize(BeanDefinitionRegistry registry) {     //初始化操作

        // Spring ApplicationContext may not ready at this moment (e.g. load from xml), so use registry as key
        if (contextMap.putIfAbsent(registry, new DubboSpringInitContext()) != null) {
            return;
        }

        // prepare context and do customize
        DubboSpringInitContext context = contextMap.get(registry);

        // find beanFactory
        ConfigurableListableBeanFactory beanFactory = findBeanFactory(registry);

        // init dubbo context
        initContext(context, registry, beanFactory);  //初始化dubbo上下文
    }

    private static void initContext(DubboSpringInitContext context, BeanDefinitionRegistry registry,
                                    ConfigurableListableBeanFactory beanFactory) {
        context.setRegistry(registry);
        context.setBeanFactory(beanFactory);

        // customize context, you can change the bind module model via DubboSpringInitCustomizer SPI
        customize(context);

        // init ModuleModel
        ModuleModel moduleModel = context.getModuleModel();
        if (moduleModel == null) {
            ApplicationModel applicationModel;
            if (findContextForApplication(ApplicationModel.defaultModel()) == null) {
                // first spring context use default application instance
                applicationModel = ApplicationModel.defaultModel();
                logger.info("Use default application: " + safeGetModelDesc(applicationModel));
            } else {
                // create an new application instance for later spring context
                applicationModel = FrameworkModel.defaultModel().newApplication();
                logger.info("Create new application: " + safeGetModelDesc(applicationModel));
            }

            // init ModuleModel
            moduleModel = applicationModel.getDefaultModule();
            context.setModuleModel(moduleModel);
            logger.info("Use default module model of target application: " + safeGetModelDesc(moduleModel));
        } else {
            logger.info("Use module model from customizer: " + safeGetModelDesc(moduleModel));
        }
        logger.info("Bind " + safeGetModelDesc(moduleModel) + " to spring container: " + ObjectUtils.identityToString(registry));

        // set module attributes
        if (context.getModuleAttributes().size() > 0) {
            context.getModuleModel().getAttributes().putAll(context.getModuleAttributes());
        }

        // bind dubbo initialization context to spring context
        registerContextBeans(beanFactory, context);

        // mark context as bound
        context.markAsBound();

        // register common beans
        DubboBeanUtils.registerCommonBeans(registry);  //注册常用bean
    }

   ...

}

             

DubboBeanUtils:dubbo注册工具类

public interface DubboBeanUtils {

    static final Log log = LogFactory.getLog(DubboBeanUtils.class);

    /**
     * Register the common beans
     *
     * @param registry {@link BeanDefinitionRegistry}
     * @see ReferenceAnnotationBeanPostProcessor
     * @see DubboConfigDefaultPropertyValueBeanPostProcessor
     * @see DubboConfigAliasPostProcessor
     * @see DubboBootstrapApplicationListener
     */
    static void registerCommonBeans(BeanDefinitionRegistry registry) {

        registerInfrastructureBean(registry, ServicePackagesHolder.BEAN_NAME, ServicePackagesHolder.class);

        registerInfrastructureBean(registry, ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);

        // Since 2.5.7 Register @Reference Annotation Bean Processor as an infrastructure Bean
        registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
            ReferenceAnnotationBeanPostProcessor.class);
        //注册ReferenceAnnotationBeanPostProcessor实例,处理@DubboReference注解

        // TODO Whether DubboConfigAliasPostProcessor can be removed ?
        // Since 2.7.4 [Feature] https://github.com/apache/dubbo/issues/5093
        registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME,
            DubboConfigAliasPostProcessor.class);

        // Since 2.7.4 Register DubboBootstrapApplicationListener as an infrastructure Bean
//        registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME,
//            DubboBootstrapApplicationListener.class);

        // register ApplicationListeners
        registerInfrastructureBean(registry, DubboDeployApplicationListener.class.getName(), DubboDeployApplicationListener.class);
        registerInfrastructureBean(registry, DubboConfigApplicationListener.class.getName(), DubboConfigApplicationListener.class);

        // Since 2.7.6 Register DubboConfigDefaultPropertyValueBeanPostProcessor as an infrastructure Bean
        registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,
            DubboConfigDefaultPropertyValueBeanPostProcessor.class);

        // Dubbo config initializer
        registerInfrastructureBean(registry, DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class);

        // register infra bean if not exists later
        registerInfrastructureBean(registry, DubboInfraBeanRegisterPostProcessor.BEAN_NAME, DubboInfraBeanRegisterPostProcessor.class);
    }

    ...

}

                   

                           

                                       

相关注解

           

@EnableDubbo

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig        //使用注解@EnableDubboConfig
@DubboComponentScan       //使用注解@DubboComponentScan
public @interface EnableDubbo {

    /**
     * Base packages to scan for annotated @Service classes.
     * <p>
     * Use {@link #scanBasePackageClasses()} for a type-safe alternative to String-based
     * package names.
     *
     * @return the base packages to scan
     * @see DubboComponentScan#basePackages()
     */
    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};  //等同于注解DubboComponentScan的basePackages属性

    /**
     * Type-safe alternative to {@link #scanBasePackages()} for specifying the packages to
     * scan for annotated @Service classes. The package of each class specified will be
     * scanned.
     *
     * @return classes from the base packages to scan
     * @see DubboComponentScan#basePackageClasses
     */
    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};  //等同于注解DubboComponentScan的basePackageClasses属性


    /**
     * It indicates whether {@link AbstractConfig} binding to multiple Spring Beans.
     *
     * @return the default value is <code>true</code>
     * @see EnableDubboConfig#multiple()
     */
    @AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple")
    boolean multipleConfig() default true;    //等同于注解EnableDubboConfig的multiple的属性

}

                

DubboComponentScan:扫描指定包或者类,如果类上有注解@DubboService,创建bean

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)   //引入DubboComponentScanRegistrar类
public @interface DubboComponentScan {

    /**
     * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
     * declarations e.g.: {@code @DubboComponentScan("org.my.pkg")} instead of
     * {@code @DubboComponentScan(basePackages="org.my.pkg")}.
     *
     * @return the base packages to scan
     */
    String[] value() default {};

    /**
     * Base packages to scan for annotated @Service classes. {@link #value()} is an
     * alias for (and mutually exclusive with) this attribute.
     * <p>
     * Use {@link #basePackageClasses()} for a type-safe alternative to String-based
     * package names.
     *
     * @return the base packages to scan
     */
    String[] basePackages() default {};

    /**
     * Type-safe alternative to {@link #basePackages()} for specifying the packages to
     * scan for annotated @Service classes. The package of each class specified will be
     * scanned.
     *
     * @return classes from the base packages to scan
     */
    Class<?>[] basePackageClasses() default {};

}

            

DubboComponentScanRegistrar

public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
                //注册类定义信息

        // initialize dubbo beans
        DubboSpringInitializer.initialize(registry);

        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);

        registerServiceAnnotationPostProcessor(packagesToScan, registry);
                //注册ServiceAnnotationPostProcessor处理器,处理@DubboService注解
    }

    /**
     * Registers {@link ServiceAnnotationPostProcessor}
     *
     * @param packagesToScan packages to scan without resolving placeholders
     * @param registry       {@link BeanDefinitionRegistry}
     * @since 2.5.8
     */
    private void registerServiceAnnotationPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
                //注册ServiceAnnotationPostProcessor处理器
        BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationPostProcessor.class);
        builder.addConstructorArgValue(packagesToScan);
        builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);

    }

    private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
        // get from @DubboComponentScan 
        Set<String> packagesToScan = getPackagesToScan0(metadata, DubboComponentScan.class, "basePackages", "basePackageClasses");

        // get from @EnableDubbo, compatible with spring 3.x
        if (packagesToScan.isEmpty()) {
            packagesToScan = getPackagesToScan0(metadata, EnableDubbo.class, "scanBasePackages", "scanBasePackageClasses");
        }

        if (packagesToScan.isEmpty()) {
            return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName()));
        }
        return packagesToScan;
    }

    private Set<String> getPackagesToScan0(AnnotationMetadata metadata, Class annotationClass, String basePackagesName, String basePackageClassesName) {

        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                metadata.getAnnotationAttributes(annotationClass.getName()));
        if (attributes == null) {
            return Collections.emptySet();
        }

        Set<String> packagesToScan = new LinkedHashSet<>();
        // basePackages
        String[] basePackages = attributes.getStringArray(basePackagesName);
        packagesToScan.addAll(Arrays.asList(basePackages));
        // basePackageClasses
        Class<?>[] basePackageClasses = attributes.getClassArray(basePackageClassesName);
        for (Class<?> basePackageClass : basePackageClasses) {
            packagesToScan.add(ClassUtils.getPackageName(basePackageClass));
        }
        // value
        if (attributes.containsKey("value")) {
            String[] value = attributes.getStringArray("value");
            packagesToScan.addAll(Arrays.asList(value));
        }
        return packagesToScan;
    }

}

               

               

@DubboService:服务提供端服务暴露,一般标注在接口实现类上

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Inherited
public @interface DubboService {

    /**
     * Interface class, default value is void.class
     */
    Class<?> interfaceClass() default void.class;

    /**
     * Interface class name, default value is empty string
     */
    String interfaceName() default "";

    String group() default "";    //服务分组
    String version() default "";  //服务版本

    /**
     * Service path, default value is empty string
     */
    String path() default "";

    boolean export() default true;       //是否暴露服务,默认true
    boolean dynamic() default true;      //服务是否动态注册,默认true
    boolean register() default true;     //是否向注册中心注册服务,默认true
    boolean deprecated() default false;  //服务是否禁用,默认false

    int delay() default -1;        //服务延时注册时间,默认为0
    int weight() default -1;       //服务权重,默认为0
    int executes() default -1;     //服务最大并发执行数,默认为0(不限制)
    int timeout() default -1;      //服务调用超时时间,默认为0

    /**
     * Service token, default value is false
     */
    String token() default "";

    /**
     * Access log for the service, default value is ""
     */
    String accesslog() default "";

    /**
     * Service doc, default value is ""
     */
    String document() default "";


    /**
     * @see DubboService#stub()
     * @deprecated
     */
    String local() default "";

    /**
     * Service stub name, use interface name + Local if not set
     */
    String stub() default "";

    /**
     * Cluster strategy, legal values include: failover, failfast, failsafe, failback, forking
     * you can use {@link org.apache.dubbo.common.constants.ClusterRules#FAIL_FAST} ……
     */
    String cluster() default ClusterRules.EMPTY;      //容错策略

    /**
     * Service invocation retry times
     *
     * @see org.apache.dubbo.common.constants.CommonConstants#DEFAULT_RETRIES
     */
    int retries() default -1;                         //重试次数

    /**
     * Service mock name, use interface name + Mock if not set
     */
    String mock() default "";                         //服务降级接口

    /**
     * Load balance strategy, legal values include: random, roundrobin, leastactive
     *
     * you can use {@link org.apache.dubbo.common.constants.LoadbalanceRules#RANDOM} ……
     */
    String loadbalance() default ClusterRules.EMPTY;  //负载均衡策略

    /**
     * How the proxy is generated, legal values include: jdk, javassist
     */
    String proxy() default "";

    /**
     * Maximum connections service provider can accept, default value is 0 - connection is shared
     */
    int connections() default -1;

    /**
     * The callback instance limit peer connection
     * <p>
     * see org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CALLBACK_INSTANCES
     */
    int callbacks() default -1;

    /**
     * Callback method name when connected, default value is empty string
     */
    String onconnect() default "";

    /**
     * Callback method name when disconnected, default value is empty string
     */
    String ondisconnect() default "";

    /**
     * Service owner, default value is empty string
     */
    String owner() default "";

    /**
     * Service layer, default value is empty string
     */
    String layer() default "";

    /**
     * Whether to enable async invocation, default value is false
     */
    boolean async() default false;     //是否开启异步调用,默认false

    /**
     * Maximum active requests allowed, default value is 0
     */
    int actives() default -1;          //最大活跃请求数

    /**
     * Whether the async request has already been sent, the default value is false
     */
    boolean sent() default false;

    /**
     * Whether to use JSR303 validation, legal values are: true, false
     */
    String validation() default "";

    /**
     * Specify cache implementation for service invocation, legal values include: lru, threadlocal, jcache
     */
    String cache() default "";

    /**
     * Filters for service invocation
     *
     * @see Filter
     */
    String[] filter() default {};      //过滤器

    /**
     * Listeners for service exporting and unexporting
     *
     * @see ExporterListener
     */
    String[] listener() default {};

    /**
     * Customized parameter key-value pair, for example: {key1, value1, key2, value2}
     */
    String[] parameters() default {};  //自定义参数,如:{key1, value1, key2, value2}

    /**
     * Application spring bean name
     * @deprecated This attribute was deprecated, use bind application/module of spring ApplicationContext
     */
    @Deprecated
    String application() default "";   //已禁用

    /**
     * Module spring bean name
     */
    String module() default "";

    /**
     * Provider spring bean name
     */
    String provider() default "";

    /**
     * Protocol spring bean names
     */
    String[] protocol() default {};

    /**
     * Monitor spring bean name
     */
    String monitor() default "";

    /**
     * Registry spring bean name
     */
    String[] registry() default {};

    /**
     * Service tag name
     */
    String tag() default "";

    /**
     * methods support
     *
     * @return
     */
    Method[] methods() default {};

    /**
     * the scope for referring/exporting a service, if it's local, it means searching in current JVM only.
     * @see org.apache.dubbo.rpc.Constants#SCOPE_LOCAL
     * @see org.apache.dubbo.rpc.Constants#SCOPE_REMOTE
     */
    String scope() default "";            //服务暴露、调用类型,可选值:local、remote

    /**
     * Weather the service is export asynchronously
     */
    boolean exportAsync() default false;  //是否异步暴露,默认false
}

                  

@DubboReference:消费端服务调用

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface DubboReference {

    /**
     * Interface class, default value is void.class
     */
    Class<?> interfaceClass() default void.class;

    /**
     * Interface class name, default value is empty string
     */
    String interfaceName() default "";

    String group() default "";    //服务分组
    String version() default "";  //服务版本

    /**
     * Service target URL for direct invocation, if this is specified, then registry center takes no effect.
     */
    String url() default "";      //直连服务提供端(如果配置了,则不使用注册中心)

    /**
     * Client transport type, default value is "netty"
     */
    String client() default "";   //客户端传输方式,默认为netty

    /**
     * Whether to enable generic invocation, default value is false
     * @deprecated Do not need specify generic value, judge by injection type and interface class
     */
    @Deprecated
    boolean generic() default false;   //已禁用

    /**
     * When enable, prefer to call local service in the same JVM if it's present, default value is true
     * @deprecated using scope="local" or scope="remote" instead
     */
    @Deprecated
    boolean injvm() default true;      //已禁用

    /**
     * Check if service provider is available during boot up, default value is true
     */
    boolean check() default true;

    /**
     * Whether eager initialize the reference bean when all properties are set, default value is true ( null as true)
     * @see ReferenceConfigBase#shouldInit()
     */
    boolean init() default true;

    /**
     * Whether to make connection when the client is created, the default value is false
     */
    boolean lazy() default false;

    /**
     * Export an stub service for event dispatch, default value is false.
     * <p>
     * see org.apache.dubbo.rpc.Constants#STUB_EVENT_METHODS_KEY
     */
    boolean stubevent() default false;

    /**
     * Whether to reconnect if connection is lost, if not specify, reconnect is enabled by default, and the interval
     * for retry connecting is 2000 ms
     * <p>
     * see org.apache.dubbo.remoting.Constants#DEFAULT_RECONNECT_PERIOD
     */
    String reconnect() default "";

    /**
     * Whether to stick to the same node in the cluster, the default value is false
     * <p>
     * see Constants#DEFAULT_CLUSTER_STICKY
     */
    boolean sticky() default false;

    /**
     * How the proxy is generated, legal values include: jdk, javassist
     */
    String proxy() default "";

    /**
     * Service stub name, use interface name + Local if not set
     */
    String stub() default "";

    /**
     * Cluster strategy, legal values include: failover, failfast, failsafe, failback, forking
     * you can use {@link org.apache.dubbo.common.constants.ClusterRules#FAIL_FAST} ……
     */
    String cluster() default ClusterRules.EMPTY;

    /**
     * Maximum connections service provider can accept, default value is 0 - connection is shared
     */
    int connections() default -1;

    /**
     * The callback instance limit peer connection
     * <p>
     * see org.apache.dubbo.rpc.Constants#DEFAULT_CALLBACK_INSTANCES
     */
    int callbacks() default -1;

    /**
     * Callback method name when connected, default value is empty string
     */
    String onconnect() default "";

    /**
     * Callback method name when disconnected, default value is empty string
     */
    String ondisconnect() default "";

    /**
     * Service owner, default value is empty string
     */
    String owner() default "";

    /**
     * Service layer, default value is empty string
     */
    String layer() default "";

    /**
     * Service invocation retry times
     * <p>
     * see Constants#DEFAULT_RETRIES
     */
    int retries() default -1;

    /**
     * Load balance strategy, legal values include: random, roundrobin, leastactive
     * you can use {@link org.apache.dubbo.common.constants.LoadbalanceRules#RANDOM} ……
     */
    String loadbalance() default LoadbalanceRules.EMPTY;

    /**
     * Whether to enable async invocation, default value is false
     */
    boolean async() default false;

    /**
     * Maximum active requests allowed, default value is 0
     */
    int actives() default -1;

    /**
     * Whether the async request has already been sent, the default value is false
     */
    boolean sent() default false;

    /**
     * Service mock name, use interface name + Mock if not set
     */
    String mock() default "";

    /**
     * Whether to use JSR303 validation, legal values are: true, false
     */
    String validation() default "";

    /**
     * Timeout value for service invocation, default value is 0
     */
    int timeout() default -1;

    /**
     * Specify cache implementation for service invocation, legal values include: lru, threadlocal, jcache
     */
    String cache() default "";

    /**
     * Filters for service invocation
     * <p>
     * see Filter
     */
    String[] filter() default {};

    /**
     * Listeners for service exporting and unexporting
     * <p>
     * see ExporterListener
     */
    String[] listener() default {};

    /**
     * Customized parameter key-value pair, for example: {key1, value1, key2, value2} or {"key1=value1", "key2=value2"}
     */
    String[] parameters() default {};

    /**
     * Application name
     * @deprecated This attribute was deprecated, use bind application/module of spring ApplicationContext
     */
    @Deprecated
    String application() default "";

    /**
     * Module associated name
     */
    String module() default "";

    /**
     * Consumer associated name
     */
    String consumer() default "";

    /**
     * Monitor associated name
     */
    String monitor() default "";

    /**
     * Registry associated name
     */
    String[] registry() default {};

    /**
     * The communication protocol of Dubbo Service
     *
     * @return the default value is ""
     * @since 2.6.6
     */
    String protocol() default "";

    /**
     * Service tag name
     */
    String tag() default "";

    /**
     * Service merger
     */
    String merger() default "";

    /**
     * methods support
     */
    Method[] methods() default {};

    /**
     * The id
     * NOTE: The id attribute is ignored when using @DubboReference on @Bean method
     * @return default value is empty
     * @since 2.7.3
     */
    String id() default "";

    /**
     * @return The service names that the Dubbo interface subscribed
     * @see RegistryConstants#SUBSCRIBED_SERVICE_NAMES_KEY
     * @since 2.7.8
     * @deprecated using {@link DubboReference#providedBy()}
     */
    @Deprecated
    String[] services() default {};

    /**
     * declares which app or service this interface belongs to
     * @see RegistryConstants#PROVIDED_BY
     */
    String[] providedBy() default {};

    /**
     * the scope for referring/exporting a service, if it's local, it means searching in current JVM only.
     * @see org.apache.dubbo.rpc.Constants#SCOPE_LOCAL
     * @see org.apache.dubbo.rpc.Constants#SCOPE_REMOTE
     */
    String scope() default "";

    /**
     * Weather the reference is refer asynchronously
     */
    boolean referAsync() default false;
}

             

                                

                                       

配置类

           

DubboConfiguratinProperties

@ConfigurationProperties("dubbo")
public class DubboConfigurationProperties {
    @NestedConfigurationProperty
    private DubboConfigurationProperties.Config config = new DubboConfigurationProperties.Config();
    @NestedConfigurationProperty
    private DubboConfigurationProperties.Scan scan = new DubboConfigurationProperties.Scan();
    @NestedConfigurationProperty
    private ApplicationConfig application = new ApplicationConfig();
    @NestedConfigurationProperty
    private ModuleConfig module = new ModuleConfig();
    @NestedConfigurationProperty
    private RegistryConfig registry = new RegistryConfig();
    @NestedConfigurationProperty
    private ProtocolConfig protocol = new ProtocolConfig();
    @NestedConfigurationProperty
    private MonitorConfig monitor = new MonitorConfig();
    @NestedConfigurationProperty
    private ProviderConfig provider = new ProviderConfig();
    @NestedConfigurationProperty
    private ConsumerConfig consumer = new ConsumerConfig();
    @NestedConfigurationProperty
    private ConfigCenterBean configCenter = new ConfigCenterBean();
    @NestedConfigurationProperty
    private MetadataReportConfig metadataReport = new MetadataReportConfig();
    @NestedConfigurationProperty
    private MetricsConfig metrics = new MetricsConfig();
    private Map<String, ModuleConfig> modules = new LinkedHashMap();
    private Map<String, RegistryConfig> registries = new LinkedHashMap();
    private Map<String, ProtocolConfig> protocols = new LinkedHashMap();
    private Map<String, MonitorConfig> monitors = new LinkedHashMap();
    private Map<String, ProviderConfig> providers = new LinkedHashMap();
    private Map<String, ConsumerConfig> consumers = new LinkedHashMap();
    private Map<String, ConfigCenterBean> configCenters = new LinkedHashMap();
    private Map<String, MetadataReportConfig> metadataReports = new LinkedHashMap();
    private Map<String, MetricsConfig> metricses = new LinkedHashMap();

    public DubboConfigurationProperties() {
    }


******
内部类:Scan

    static class Scan {
        private Set<String> basePackages = new LinkedHashSet();

        Scan() {
        }

        public Set<String> getBasePackages() {
            return this.basePackages;
        }

        public void setBasePackages(Set<String> basePackages) {
            this.basePackages = basePackages;
        }
    }


******
内部类:Config

    static class Config {
        private ConfigMode mode;
        private boolean multiple;
        private boolean override;

        Config() {
            this.mode = ConfigMode.STRICT;
            this.multiple = true;
            this.override = true;
        }

          

AbstractConfig

public abstract class AbstractConfig implements Serializable {

    protected static final Logger logger = LoggerFactory.getLogger(AbstractConfig.class);
    private static final long serialVersionUID = 4267533505537413570L;

    /**
     * tag name cache, speed up get tag name frequently
     */
    private static final Map<Class, String> tagNameCache = new ConcurrentHashMap<>();

    /**
     * attributed getter method cache for equals(), hashCode() and toString()
     */
    private static final Map<Class, List<Method>> attributedMethodCache = new ConcurrentHashMap<>();

    /**
     * The suffix container
     */
    private static final String[] SUFFIXES = new String[]{"Config", "Bean", "ConfigBase"};

    /**
     * The config id
     */
    private String id;

    protected final AtomicBoolean refreshed = new AtomicBoolean(false);

    /**
     * Is default config or not
     */
    protected Boolean isDefault;

    /**
     * The scope model of this config instance.
     * <p>
     * <b>NOTE:</b> the model maybe changed during config processing,
     * the extension spi instance needs to be reinitialized after changing the model!
     */
    protected ScopeModel scopeModel;

    public AbstractConfig() {
        this(ApplicationModel.defaultModel());
    }

    public AbstractConfig(ScopeModel scopeModel) {
        this.setScopeModel(scopeModel);
    }

            

ScopeModel

public abstract class ScopeModel implements ExtensionAccessor {
    protected static final Logger LOGGER = LoggerFactory.getLogger(ScopeModel.class);

    /**
     * The internal id is used to represent the hierarchy of the model tree, such as:
     * <ol>
     *     <li>1</li>
     *     FrameworkModel (index=1)
     *     <li>1.2</li>
     *     FrameworkModel (index=1) -> ApplicationModel (index=2)
     *     <li>1.2.0</li>
     *     FrameworkModel (index=1) -> ApplicationModel (index=2) -> ModuleModel (index=0, internal module)
     *     <li>1.2.1</li>
     *     FrameworkModel (index=1) -> ApplicationModel (index=2) -> ModuleModel (index=1, first user module)
     * </ol>
     */
    private String internalId;

    /**
     * Public Model Name, can be set from user
     */
    private String modelName;

    private String desc;

    private Set<ClassLoader> classLoaders;

    private final ScopeModel parent;
    private final ExtensionScope scope;

    private ExtensionDirector extensionDirector;

    private ScopeBeanFactory beanFactory;
    private List<ScopeModelDestroyListener> destroyListeners;

    private Map<String, Object> attributes;
    private final AtomicBoolean destroyed = new AtomicBoolean(false);

    public ScopeModel(ScopeModel parent, ExtensionScope scope) {
        this.parent = parent;
        this.scope = scope;
    }

                

               

ApplicationConfig

public class ApplicationConfig extends AbstractConfig {
    private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationConfig.class);

    private static final long serialVersionUID = 5508512956753757169L;

    private String name;          //应用名称(必填)
    private String version;       //应用版本
    private String owner;         //应用负责人,用于服务治理,可填写负责人公司邮箱前缀
    private String organization;  //组织名称(BU或部门),用于注册中心区分服务来源
    private String architecture;  //用于服务分层对应的架构,如:ntl、china
    private String environment;   //应用环境,如:develop、test、product,作为只用于开发测试功能的限制条件

    private String compiler;      //生成自适应代码的编译器,可选值:jdk、javassist(默认)
    private String logger;        //日志输出方式,可选值:slf4j(默认),jcl,log4j,log4j2,jdk

    /**
     * Registry centers
     */
    private List<RegistryConfig> registries;
    private String registryIds;

    /**
     * Monitor center
     */
    private MonitorConfig monitor;

    /**
     * Directory for saving thread dump
     */
    private String dumpDirectory;

    /**
     * Whether to enable qos or not
     */
    private Boolean qosEnable;

    /**
     * The qos host to listen
     */
    private String qosHost;

    /**
     * The qos port to listen
     */
    private Integer qosPort;

    /**
     * Should we accept foreign ip or not?
     */
    private Boolean qosAcceptForeignIp;

    /**
     * Customized parameters
     */
    private Map<String, String> parameters;

    /**
     * Config the shutdown.wait
     */
    private String shutwait;

    private String hostname;

    /**
     * Metadata type, local or remote, if choose remote, you need to further specify metadata center.
     */
    private String metadataType;

    private Boolean registerConsumer;

    private String repository;

    private Boolean enableFileCache;

    /**
     * The preferred protocol(name) of this application
     * convenient for places where it's hard to determine which is the preferred protocol
     */
    private String protocol;

    /**
     * The protocol used for peer-to-peer metadata transmission
     */
    private String metadataServiceProtocol;

    /**
     * Metadata Service, used in Service Discovery
     */
    private Integer metadataServicePort;

    /**
     * used to set extensions of probe in qos
     */
    private String livenessProbe;

    private String readinessProbe;

    private String startupProbe;

    private String registerMode;     //provider注册类型,可选值:all(默认)、interface(接口粒度)、instance(应用粒度)

    private Boolean enableEmptyProtection;


    public ApplicationConfig() {
    public ApplicationConfig(ApplicationModel applicationModel) {
    public ApplicationConfig(String name) {
    public ApplicationConfig(ApplicationModel applicationModel, String name) {

                  

ModuleConfig

public class ModuleConfig extends AbstractConfig {

    private static final long serialVersionUID = 5508512956753757169L;

    /**
     * Module name
     */
    private String name;

    /**
     * Module version
     */
    private String version;

    /**
     * Module owner
     */
    private String owner;

    /**
     * Module's organization
     */
    private String organization;

    /**
     * Registry centers
     */
    private List<RegistryConfig> registries;  //注册中心配置

    /**
     * Monitor center
     */
    private MonitorConfig monitor;           //监控中心配置

    /**
     * Whether start module in background.
     * If start in backgound, do not await finish on Spring ContextRefreshedEvent.
     *
     * @see org.apache.dubbo.config.spring.context.DubboDeployApplicationListener
     */
    private Boolean background;

    /**
     * Weather the reference is refer asynchronously
     */
    private Boolean referAsync;    //是否异步引用

    /**
     * Thread num for asynchronous refer pool size
     */
    private Integer referThreadNum;  //消费端引用线程池大小

    /**
     * Weather the service is export asynchronously
     */
    private Boolean exportAsync;     //provider是否异步暴露

    /**
     * Thread num for asynchronous export pool size
     */
    private Integer exportThreadNum; //provider暴露线程池大小


    public ModuleConfig() {
    public ModuleConfig(ModuleModel moduleModel) {
    public ModuleConfig(String name) {
    public ModuleConfig(ModuleModel moduleModel, String name) {

                

RegistryConfig:注册中心配置

public class RegistryConfig extends AbstractConfig {

    public static final String NO_AVAILABLE = "N/A";
    private static final long serialVersionUID = 5508512956753757169L;

    private String address;     //注册中心地址,多个值用逗号间隔,如:ip:port,ip:port(可不带port)
    private Integer port;       //注册中心端口,默认9090
    private String username;    //用户名
    private String password;    //密码

    private String protocol;    //注册中心地址协议,可选值:dubbo(默认)、zookeeper、redis等
    private String transporter; //网络传输方式,可选值:mina、netty(默认)

    private String server;
    private String client;

    /**
     * Affects how traffic distributes among registries, useful when subscribing multiple registries, available options:
     * 1. zone-aware, a certain type of traffic always goes to one Registry according to where the traffic is originated.
     */
    private String cluster;

    private String group;       //服务注册分组,默认dubbo
    private String version;     //服务版本
    private String zone;        //服务分区

    private Integer timeout;    //注册中心请求超时时间,单位为毫秒
    private Integer session;    //注册中心会话超时时间,单位为毫秒
    private Integer wait;       //停止时等待通知完成时间,单位为毫秒

    private String file;        //使用文件缓存注册中心地址列表及服务提供者列表

    private Boolean check;      //应用启动时检查注册中心是否可用,默认true
    private Boolean dynamic;    //服务是否可以动态注册,默认true
    private Boolean register;   //是否向注册中心注册服务,如果设为false,将只订阅,不注册,默认true
    private Boolean subscribe;  //是否向此注册中心订阅服务,如果设为false,将只注册,不订阅,默认true

    private Map<String, String> parameters;    //自定义参数

    /**
     * Simple the registry. both useful for provider and consumer
     *
     * @since 2.7.0
     */
    private Boolean simplified;
    /**
     * After simplify the registry, should add some parameter individually. just for provider.
     * <p>
     * such as: extra-keys = A,b,c,d
     *
     * @since 2.7.0
     */
    private String extraKeys;

    private Boolean useAsConfigCenter;    //注册中心是否用作配置中心
    private Boolean useAsMetadataCenter;  //注册中心是否用作元数据中心

    private String accepts;               //可接受的rpc协议列表,如:dubbo,rest
    private Boolean preferred;            //是否优先使用该注册中心

    /**
     * Affects traffic distribution among registries, useful when subscribe to multiple registries
     * Take effect only when no preferred registry is specified.
     */
    private Integer weight;        //注册中心权重,未设置prefered时有效

    private String registerMode;   //注册模式,可选值:all(默认)、interface(接口粒度)、instance(应用粒度)

    private Boolean enableEmptyProtection;


    public RegistryConfig() {
    public RegistryConfig(ApplicationModel applicationModel) {
    public RegistryConfig(String address) {
    public RegistryConfig(ApplicationModel applicationModel, String address) {
    public RegistryConfig(String address, String protocol) {
    public RegistryConfig(ApplicationModel applicationModel, String address, String protocol) {

            

MonitorConfig:监控中心配置

public class MonitorConfig extends AbstractConfig {

    private static final long serialVersionUID = -1184681514659198203L;

    /**
     * The protocol of the monitor, if the value is registry, it will search the monitor address from the registry center,
     * otherwise, it will directly connect to the monitor center
     */
    private String protocol;
    private String address;

    private String username;
    private String password;

    private String group;
    private String version;
    private String interval;

    /**
     * customized parameters
     */
    private Map<String, String> parameters;    //自定义的参数


    public MonitorConfig() {
    public MonitorConfig(ApplicationModel applicationModel) {
    public MonitorConfig(String address) {
    public MonitorConfig(ApplicationModel applicationModel, String address) {

           

ProviderConfig:provider配置

public class ProviderConfig extends AbstractServiceConfig {

    private static final long serialVersionUID = 6913423882496634749L;

    // ======== protocol default values, it'll take effect when protocol's attributes are not set ========

    /**
     * Service ip addresses (used when there are multiple network cards available)
     */
    private String host;

    /**
     * Service port
     */
    private Integer port;

    /**
     * Context path
     */
    private String contextpath;

    /**
     * Thread pool
     */
    private String threadpool;

    /**
     * Thread pool name
     */
    private String threadname;

    /**
     * Thread pool size (fixed size)
     */
    private Integer threads;

    /**
     * IO thread pool size (fixed size)
     */
    private Integer iothreads;

    /**
     * Thread pool keepAliveTime, default unit TimeUnit.MILLISECONDS
     */
    private Integer alive;

    /**
     * Thread pool queue length
     */
    private Integer queues;

    /**
     * Max acceptable connections
     */
    private Integer accepts;

    /**
     * Protocol codec
     */
    private String codec;

    /**
     * The serialization charset
     */
    private String charset;

    /**
     * Payload max length
     */
    private Integer payload;

    /**
     * The network io buffer size
     */
    private Integer buffer;

    /**
     * Transporter
     */
    private String transporter;

    /**
     * How information gets exchanged
     */
    private String exchanger;

    /**
     * Thread dispatching mode
     */
    private String dispatcher;

    /**
     * Networker
     */
    private String networker;

    /**
     * The server-side implementation model of the protocol
     */
    private String server;

    /**
     * The client-side implementation model of the protocol
     */
    private String client;

    /**
     * Supported telnet commands, separated with comma.
     */
    private String telnet;

    /**
     * Command line prompt
     */
    private String prompt;

    /**
     * Status check
     */
    private String status;

    /**
     * Wait time when stop
     */
    private Integer wait;

    /**
     * Thread num for asynchronous export pool size
     */
    private Integer exportThreadNum;

    /**
     * Whether export should run in background or not.
     *
     * @deprecated replace with {@link ModuleConfig#setBackground(Boolean)}
     * @see ModuleConfig#setBackground(Boolean)
     */
    private Boolean exportBackground;


    public ProviderConfig() {
    public ProviderConfig(ModuleModel moduleModel) {

          

ConsumerConfig:consumer配置

public class ConsumerConfig extends AbstractReferenceConfig {

    private static final long serialVersionUID = 2827274711143680600L;

    /**
     * Networking framework client uses: netty, mina, etc.
     */
    private String client;

    /**
     * Consumer thread pool type: cached, fixed, limit, eager
     */
    private String threadpool;

    /**
     * Consumer threadpool core thread size
     */
    private Integer corethreads;

    /**
     * Consumer threadpool thread size
     */
    private Integer threads;

    /**
     * Consumer threadpool queue size
     */
    private Integer queues;

    /**
     * By default, a TCP long-connection communication is shared between the consumer process and the provider process.
     * This property can be set to share multiple TCP long-connection communications. Note that only the dubbo protocol takes effect.
     */
    private Integer shareconnections;

    /**
     *  Url Merge Processor
     *  Used to customize the URL merge of consumer and provider
     */
    private String urlMergeProcessor;

    /**
     * Thread num for asynchronous refer pool size
     */
    private Integer referThreadNum;

    /**
     * Whether refer should run in background or not.
     *
     * @deprecated replace with {@link ModuleConfig#setBackground(Boolean)}
     * @see ModuleConfig#setBackground(Boolean)
     */
    private Boolean referBackground;


    public ConsumerConfig() {
    public ConsumerConfig(ModuleModel moduleModel) {

           

ConfigCenterBean:配置中心实例

public class ConfigCenterBean extends ConfigCenterConfig implements ApplicationContextAware, DisposableBean, EnvironmentAware {

    private transient ApplicationContext applicationContext;

    private Boolean includeSpringEnv = false;

    public ConfigCenterBean() {
    }

         

ConfigCneterConfig:配置中心配置

public class ConfigCenterConfig extends AbstractConfig {
    private final AtomicBoolean initialized = new AtomicBoolean(false);

    private String protocol;
    private String address;
    private Integer port;

    /**
     * The config center cluster, it's real meaning may very on different Config Center products.
     */
    private String cluster;

    /**
     * The namespace of the config center, generally it's used for multi-tenant,
     * but it's real meaning depends on the actual Config Center you use.
     * The default value is CommonConstants.DUBBO
     */
    private String namespace;

    /**
     * The group of the config center, generally it's used to identify an isolated space for a batch of config items,
     * but it's real meaning depends on the actual Config Center you use.
     * The default value is CommonConstants.DUBBO
     */
    private String group;
    private String username;
    private String password;

    /**
     * The default value is 3000L;
     */
    private Long timeout;

    /**
     * If the Config Center is given the highest priority, it will override all the other configurations
     * The default value is true
     * @deprecated no longer used
     */
    private Boolean highestPriority;

    /**
     * Decide the behaviour when initial connection try fails, 'true' means interrupt the whole process once fail.
     * The default value is true
     */
    private Boolean check;

    /**
     * Used to specify the key that your properties file mapping to, most of the time you do not need to change this parameter.
     * Notice that for Apollo, this parameter is meaningless, set the 'namespace' is enough.
     * The default value is CommonConstants.DEFAULT_DUBBO_PROPERTIES
     */
    private String configFile;

    /**
     * the properties file under 'configFile' is global shared while .properties under this one is limited only to this application
     */
    private String appConfigFile;

    /**
     * If the Config Center product you use have some special parameters that is not covered by this class, you can add it to here.
     * For example, with XML:
     *    <dubbo:config-center>
     *       <dubbo:parameter key="{your key}" value="{your value}" />
     *    </dubbo:config-center>
     */
    private Map<String, String> parameters;      //自定义参数
    private Map<String, String> externalConfiguration;
    private Map<String, String> appExternalConfiguration;


    public ConfigCenterConfig() {
    public ConfigCenterConfig(ApplicationModel applicationModel) {

               

MetadataReportConfig:元数据中心配置

public class MetadataReportConfig extends AbstractConfig {

    private static final long serialVersionUID = 55233L;

    private String protocol;

    private String address;      //元数据中心地址
    private Integer port;        //元数据中心端口
    private String username;     //元数据中心用户名
    private String password;     //元数据中心密码
    private Integer timeout;     //请求超时时间,单位毫秒

    /**
     * The group the metadata in . It is the same as registry
     */
    private String group;

    private Map<String, String> parameters;    //自定义参数
    private Integer retryTimes;                //重试次数,默认100次
    private Integer retryPeriod;               //重试周期,默认3000毫秒

    /**
     * By default the metadata store will store full metadata repeatedly every day .
     */
    private Boolean cycleReport;              //是否定时刷新元数据,默认true

    /**
     * Sync report, default async
     */
    private Boolean syncReport;               //是否同步存储,默认异步

    /**
     * cluster
     */
    private Boolean cluster;

    /**
     * registry id
     */
    private String registry;

    /**
     * File for saving metadata center dynamic list
     */
    private String file;

    /**
     * Decide the behaviour when initial connection try fails,
     * 'true' means interrupt the whole process once fail.
     * The default value is true
     */
    private Boolean check;


    public MetadataReportConfig() {
    public MetadataReportConfig(ApplicationModel applicationModel) {
    public MetadataReportConfig(String address) {
    public MetadataReportConfig(ApplicationModel applicationModel, String address) {

             

MetricsConfig:指标运维配置

public class MetricsConfig extends AbstractConfig {

    private static final long serialVersionUID = -9089919311611546383L;

    private String protocol;

    /**
     * @deprecated After metrics config is refactored.
     * This parameter should no longer use and will be deleted in the future.
     */
    @Deprecated
    private String port;

    /**
     * The prometheus metrics config
     */
    @Nested
    private PrometheusConfig prometheus;   //prometheus配置

    /**
     * The metrics aggregation config
     */
    @Nested
    private AggregationConfig aggregation;  //聚合配置


    public MetricsConfig() {
    public MetricsConfig(ApplicationModel applicationModel) {

              

PrometheusConfig:prometheus配置

public class PrometheusConfig {

    /**
     * Prometheus exporter configuration
     */
    @Nested
    private Exporter exporter;

    /**
     * Prometheus Pushgateway configuration
     */
    @Nested
    private Pushgateway pushgateway;


*******
内部类:Exporter

    public static class Exporter {

        /**
         * Enable prometheus exporter
         */
        private Boolean enabled;

        /**
         * Enable http service discovery for prometheus
         */
        private Boolean enableHttpServiceDiscovery;

        /**
         * Http service discovery url
         */
        private String httpServiceDiscoveryUrl;

        /**
         * When using pull method, which port to expose
         */
        private Integer metricsPort;

        /**
         * When using pull mode, which path to expose metrics
         */
        private String metricsPath;


*******
内部类:Pushgateway

    public static class Pushgateway {

        /**
         * Enable publishing via a Prometheus Pushgateway
         */
        private Boolean enabled;

        /**
         * Base URL for the Pushgateway
         */
        private String baseUrl;

        /**
         * Login user of the Prometheus Pushgateway
         */
        private String username;

        /**
         * Login password of the Prometheus Pushgateway
         */
        private String password;

        /**
         * Frequency with which to push metrics
         */
        private Integer pushInterval;

        /**
         * Job identifier for this application instance
         */
        private String job;

              

AggregationConfig:聚合配置

public class AggregationConfig {

    /**
     * Enable local aggregation or not
     */
    private Boolean enabled;

    /**
     * Bucket num for time window quantile
     */
    private Integer bucketNum;

    /**
     * Time window seconds for time window quantile
     */
    private Integer timeWindowSeconds;

                 

               

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值