Dubbo的源码分析(四)

.  Dubbo服务的注册流程

       服务发布步骤

       我们前面做的项目,如果要发布一个项目要做两件事,第一件,配置了注解@DubboService(loadbalance = "random",cluster = "failover",retries = 2);第二件是配置了注解扫描@DubboComponentScan;由这我们可能会想,这个注解扫描是怎么进行扫描和解析的,扫描哪些类;注解是怎么把服务注册到注册中心的;前面我们讲过缓存,当中心挂了之后服务依然能从缓存的URL路径中找到要调用的服务路径信息,那么这里面URL驱动是怎么完成的,他们的URL路径又是怎么组装的等等这些问题;下面我就提出的这些问题来进行探究;

      dubbo源码

      有了上面的疑问,我们就有了一个看代码的基本思路,我个人觉得看代码和我们建框架样,不能一来就去扣细节不放,我们在了解一个新的事务时一定是先看主线,了解了主线后再慢慢扩展到分支和细节;说了这么多接下来我们就进入源码分析;dubbo服务发布有两种形式,第一种是xml 形式 <dubbo:service>形式,另一种是注解形式;我们现在都喜欢用注解了,所以我主要分析我看的注解形式的源码

     注解解析流程

     我们看下@DubboComponentScan(basePackages = "com.ghy.spring.dubbo.springbootserver.service")里面定义的是什么东西,点击进去发现会导入一个DubboComponentScanRegistrar类;他是动态注册Bean的类,通过这个Bean的注册类会把Bean注册到IOC容器类,如果感觉兴趣的可以自己去看下IOC容器的源码,后面我有空也会写spring源码的解析过程

 

 

 

  点击进去看下,首先得到了一个getPackagesToScan;这个是我们在源数据中定义的@DubboComponentScan(basePackages = "com.ghy.spring.dubbo.springbootserver.service")中的basePackages;看到这里大家应该就明白   basePackages为什么可以配置多个了;

 

 

 接着向下跟进registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);看它注册了一个什么Bean;通过下图我们发现它注册的是ServiceAnnotationBeanPostProcessor这个Bean,并且在构造这个Bean时将packagesToScan传递进去了;看到这里我们应该就能猜到服务的注册流程应该和ServiceAnnotationBeanPostProcessor有关系; 在IOC容器注册完成后,会有方法调用ServiceAnnotationBeanPostProcessor做些事情进行解析里面的类的注解方法。它把这个Bean传过去个会调用BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);注册这个Bean的beanDefinition;

    

 

Dubbo中除了@DubboComponentScan会加载Bean外还有一个类会加载Ben,那就是DubboAutoConfiguration类;为了避免重复加载,他加了一个@ConditionalOnBean注解,通过观察我们发现这个Bean的加载方法和我们前面讲的有一个共同点那就是都有一个ServiceAnnotationBeanPostProcessor类;我们可以点进去看下

进来我们首先发现了@Deprecated标签,说明这是一个过期的;在里面我们可以在他的构造方法中发现传入了packagesToScan,bean装载完成之后,会触发下面这个方法.我们点super进去看下

 

 进去后发现他实现了BeanDefinitionRegistryPostProcessor接口重写了postProcessBeanDefinitionRegistry方法;

 

这里面我们关注注册基础Bean的registerBeans(registry, DubboBootstrapApplicationListener.class);和注册serverBean的registerServiceBeans(resolvedPackagesToScan, registry);这两个方法就行

 其中基础Bean应该是把DubboBootstrapApplicationListener注册进去了,我们点击进去看下;进去之后会发现里面有一个监听,里面会监听两个东西,一个是事件

ContextRefreshedEvent,这个是在Bean上下文装载完成后进行触发;另一个是ContextClosedEvent

 

看完上面我们回退到postProcessBeanDefinitionRegistry方法中,我们发现当我们传入的resolvedPackagesToScan不为空时为调用registerServiceBeans(resolvedPackagesToScan, registry);

点击进去

   private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
         //扫描的
        DubboClassPathBeanDefinitionScanner scanner =
                new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
         //生成一个BEAN名称的
        BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);

        scanner.setBeanNameGenerator(beanNameGenerator);
         //为了兼容老的版本,实际上就是把需要扫描的注解类型,设置到scanner
        // refactor @since 2.7.7
        serviceAnnotationTypes.forEach(annotationType -> {
            scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
        });

        for (String packageToScan : packagesToScan) {

            // Registers @Service Bean first
            scanner.scan(packageToScan);

            // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
            Set<BeanDefinitionHolder> beanDefinitionHolders =
                    findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

            if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {

                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
//这是扫描注解的过程,可以跟进看下 registerServiceBean(beanDefinitionHolder, registry, scanner); }
if (logger.isInfoEnabled()) { logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " + beanDefinitionHolders + " } were scanned under package[" + packageToScan + "]"); } } else { if (logger.isWarnEnabled()) { logger.warn("No Spring Bean annotating Dubbo's @Service was found under package[" + packageToScan + "]"); } } } }

 

private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,

                                     DubboClassPathBeanDefinitionScanner scanner) {

        

        Class<?> beanClass = resolveClass(beanDefinitionHolder);

 

        Annotation service = findServiceAnnotation(beanClass);

 

        /**

         * The {@link AnnotationAttributes} of @Service annotation

         */

        AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);

 

        Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);

 

        String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

 //首先我们可以看到他注册的是一个serviceBeanDefinition,传递的参数是serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName

 //至于是怎么注册的我们跟进buildServiceBeanDefinition看他是怎么组装的

        AbstractBeanDefinition serviceBeanDefinition =

                buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);

 

        // ServiceBean Bean name

        String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);

 

        if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
           
           registry.registerBeanDefinition(beanName, serviceBeanDefinition);

 

            if (logger.isInfoEnabled()) {

                logger.info("The BeanDefinition[" + serviceBeanDefinition +

                        "] of ServiceBean has been registered with name : " + beanName);

            }

 

        } else {

 

            if (logger.isWarnEnabled()) {

                logger.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition +

                        "] of ServiceBean[ bean name : " + beanName +

                        "] was be found , Did @DubboComponentScan scan to same package in many times?");

            }

 

        }

 

    }

通过上面我们可以知道他注入的bean源头是AbstractBeanDefinition serviceBeanDefinition =buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);

点击buildServiceBeanDefinition进入;进来第一眼我们就可以看到他把很多构造的Bean的信息放到ServiceBean.class中,这一步相当于将ServiceBean注册到IOC容器中去了

 

接着registerServiceBean向下看里面都是构造的构造和赋值的过程构造完成后他最终注册的Bean是registry.registerBeanDefinition(beanName, serviceBeanDefinition);

 

 ServiceBean的初始化阶段

    最终通过上述代码,将一个 dubbo中提供的ServiceBean注入到Spring IOC容器;IOC容器的注入本质上就是对Bean的初始化,我们注入一个Bean就会对他进行

初始化,初始化完成后我们就可以对他进行调用,我们进入到serverBean中看下就可以发现这个bean被注入后就是实例化了

 

 前面动作我们是完成了注解的扫描、配置的加载以及注册一个serverBean,serverBean注册完成后就进入到上图位置;当进入到初始化阶段会做两间事

public ServiceBean() {
super();
this.service = null;
}

ServiceBean初始化完成之后,会调用下面的方法.

@Override
public void afterPropertiesSet() throws Exception {
if (StringUtils.isEmpty(getPath())) {
if (StringUtils.isNotEmpty(beanName)
&& StringUtils.isNotEmpty(getInterface())
&& beanName.startsWith(getInterface())) {
setPath(beanName);
}
}
}

上面的程序中我们要关心的点是ServiceBean是干嘛的,我们看下面代码发现在Dubbo中有两个BEAN比较核心,一个是serviceBean另一个是ReferenceBean

 其实service是服务的发布,reference是动态代理引入;点击ServiceBean类中的super()进入下图

 

 

 

 我们现在走到这一步感觉不知道怎么向下走了,这时我们换一个思维,我们想springBean在BEAN加载完成后是不是要触发监听,这个思维我们在开发中可以引用;这时我们应该想到前面我说过的

DubboBootstrapApplicationListener,这个监听会帮我们启动服务

 

 点进去看一下,在看之前我们想一下这个start会帮我们做什么,我们见名知意,可能会想,这个逻辑会不会是完成我们开篇写的一些猜想,完成URL拼接,服务注册及启动INetty server及元数据初始化啥的呢,

我想说的是,对的,在这里面他会完成这些事情,至于怎么完成的,我们慢慢来聊

    

/**

     * Start the bootstrap

     */

    public DubboBootstrap start() {

        if (started.compareAndSet(false, true)) {

            ready.set(false);

//初始化,可以跟进去看一看

            initialize();

            if (logger.isInfoEnabled()) {

                logger.info(NAME + " is starting...");

            }
             //发布DUBBO服务

            // 1. export Dubbo Services

            exportServices();

 

            // Not only provider register

            if (!isOnlyRegisterProvider() || hasExportedServices()) {

        //暴露本地元数据服务

                // 2. export MetadataService

                exportMetadataService();

//注册服务实例;这里面的注册绝对是将dubbo的服务注册到一个专用的

//服务中心

                //3. Register the local ServiceInstance if required

                registerServiceInstance();

            }

            //处理消费者的操作

            referServices();

            if (asyncExportingFutures.size() > 0) {

                new Thread(() -> {

                    try {

                        this.awaitFinish();

                    } catch (Exception e) {

                        logger.warn(NAME + " exportAsync occurred an exception.");

                    }

                    ready.set(true);

                    if (logger.isInfoEnabled()) {

                        logger.info(NAME + " is ready.");

                    }

                }).start();

            } else {

                ready.set(true);

                if (logger.isInfoEnabled()) {

                    logger.info(NAME + " is ready.");

                }

            }

            if (logger.isInfoEnabled()) {

                logger.info(NAME + " has started.");

            }

        }

        return this;

    }

 

  下面这段代码我们先了解下有个概念就可以

 

private void initialize() {

        if (!initialized.compareAndSet(false, true)) {

            return;

        }

         //维护我们一些信息,配置管理和模型信息

        ApplicationModel.initFrameworkExts();

 

        startConfigCenter();

         //服务注册信息

        useRegistryAsConfigCenterIfNecessary();

 

        loadRemoteConfigs();

 

        checkGlobalConfigs();

         //初始化元数据

        initMetadataService();

           //初始化监听

        initEventListener();

 

        if (logger.isInfoEnabled()) {

            logger.info(NAME + " has been initialized!");

        }

    }

 

  服务的发布

      我们回退一步进入exportServices();

//在这里拼接URL如果是dubbo协议会启动netty server 

    private void exportServices() {

//configManager这里面会管理我们发布服务的列表

        configManager.getServices().forEach(sc -> {

            // TODO, compatible with ServiceConfig.export()

            ServiceConfig serviceConfig = (ServiceConfig) sc;

            serviceConfig.setBootstrap(this);

           

            if (exportAsync) {

//异步发布

                ExecutorService executor = executorRepository.getServiceExporterExecutor();

                Future<?> future = executor.submit(() -> {

//跟进去

                    sc.export();

                    exportedServices.add(sc);

                });

                asyncExportingFutures.add(future);

            } else {

//同步发布

                sc.export();

                exportedServices.add(sc);

            }

        });

    }

//调用的是ServiceConfig中的方法

 

public synchronized void export() {

   //是否应该发布

    if (!shouldExport()) {

        return;

    }

      //bootstrap是否初始化了,没有就初始化

    if (bootstrap == null) {

        bootstrap = DubboBootstrap.getInstance();

        bootstrap.init();

    }

    //检查配置信息

    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();

}

进入doExport,这里面没啥我们直接进入doExportUrls();

 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;
        }
        doExportUrls();
    }

doExportUrls()这里面是进行服务的发布,发布URL

// 主要流程,根据开发者配置的协议列表,遍历协议列表逐项进行发布。

 private void doExportUrls() {

        ServiceRepository repository = ApplicationModel.getServiceRepository();

        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);

//针对具体协议去发布

            doExportUrlsFor1Protocol(protocolConfig, registryURLs);

        }

    }
  * 生成url
  * 根据url中配置的协议类型,调用指定协议进行服务的发布
       * 启动服务
       * 注册服务

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
     //如果协议名称为空则用默认DUBBO协议
	 String name = protocolConfig.getName();
        if (StringUtils.isEmpty(name)) {
            name = DUBBO;
        }
        //用来存储所有的配置信息
          * dubbo:service
               dubbo:method
                  dubbo:argument
        Map<String, String> map = new HashMap<String, String>();
        map.put(SIDE_KEY, PROVIDER_SIDE);

        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();
        if (metadataReportConfig != null && metadataReportConfig.isValid()) {
            map.putIfAbsent(METADATA_KEY, REMOTE_METADATA_STORAGE_TYPE);
        }
		//注解解析
        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();
                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];
                                                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());
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        } 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
        }
         //如果是泛化的话,再添加一个参数
        if (ProtocolUtils.isGeneric(generic)) {
            map.put(GENERIC_KEY, generic);
            map.put(METHODS_KEY, ANY_VALUE);
        } else {
            String revision = Version.getVersion(interfaceClass, version);
            if (revision != null && revision.length() > 0) {
                map.put(REVISION_KEY, revision);
            }

            String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
            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();
        }

        if (!ConfigUtils.isEmpty(token)) {
            if (ConfigUtils.isDefault(token)) {
                map.put(TOKEN_KEY, UUID.randomUUID().toString());
            } else {
                map.put(TOKEN_KEY, token);
            }
        }
        //init serviceMetadata attachments
        serviceMetadata.getAttachments().putAll(map);


         
        // export service
        String host = findConfigedHosts(protocolConfig, registryURLs, map);//自己DUBUGGER看下拼接的路径
        Integer port = findConfigedPorts(protocolConfig, name, 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
		
		//扩展点;如果我们有要扩展的配置的话可以在这个扩展点进行扩展
        if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .hasExtension(url.getProtocol())) {
				//比对 urlf替换
            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
        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));
                        URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
                        if (monitorUrl != null) {
                            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
                        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 wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                        Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
                        exporters.add(exporter);
                    }
                } else {
                    if (logger.isInfoEnabled()) {
                        logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                    }
                    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);
    }

  

dubbo中的扩展点

  上面聊到了扩展点,下面我就dubbo的扩展点说点东西,概念的东西官网写的很详细我就不多说了http://dubbo.apache.org/zh-cn/docs/dev/impls/protocol.html  ;http://dubbo.apache.org/zh-cn/docs/source_code_guide/dubbo-spi.html

 对于源码的一些讲解官网上也有

 

 

  dubbo中提供了三种扩展点,分别是:

  1. 自适应扩展点            ExtesionLoader.getExtensionLoader(protocol.class).getExtension("name")
  2. 指定名称扩展点         ExtesionLoader.getExtensionLoader(protocol.class).getAdaptiveExtension()
  3. 激活扩展点                ExtesionLoader.getExtensionLoader(protocol.class) .getAdaptiveExtension

前面有讲过负载均衡功能相信有看过的朋友应该还有印象,在讲扩展点源码前我们就负载均衡功能来实操下;首先继承AbstractLoadBalance;AbstractLoadBalance 抽象类是所有负载均衡策略实现类的父类,实现了LoadBalance接口 的方法,同时提供抽象方法交由子类实现

我扩展的是LoadBalance接口,所以文件命名也是有要求的,前面部分是LoadBalance类路径,后面是类名;文件中键值对也是有要求“=”号后面是我们扩展类的路径

=

 

 

 如果想要验证可以写个测试类自己验证下

 

 

 

posted @ 2021-02-11 17:29  童话述说我的结局  阅读(231)  评论(0编辑  收藏  举报