Dubbo源码一(服务导出)

服务概念

  1. DemoService接口表示一个服务,此时的服务表示服务定义

  2. DemoServiceImpl表示DemoService服务的具体实现,此时的服务表示服务的具体实现

  3. DemoService+group+version表示一个服务,此时的服务增加了分组和版本概念

  4. http://192.168.1.112:80/com.luban.DemoService表示一个服务,此时的服务增加了机器IP和Port,表示远程机器可以访问这个URL来使用com.luban.DemoService这个服务

  5. http://192.168.1.112:80/com.luban.DemoService?timeout=3000&version=1.0.1&application=dubbo-demo-provider-application表示一个服务,此时的服务是拥有参数的,比如超时时间、版本号、所属应用

服务导出要做的事情

1、更新服务参数,把服务参数确定好

public void onApplicationEvent(ContextRefreshedEvent event) {
    // 当前服务没有被导出并且没有卸载,才导出服务
    if (!isExported() && !isUnexported()) {
        if (logger.isInfoEnabled()) {
            logger.info("The service ready on spring started. service: " + getInterface());
        }
        // 服务导出(服务注册)
        export();
    }
}


@Override
public void export() {
    super.export();
    // Publish ServiceBeanExportedEvent
    publishExportEvent();
}

1、更新服务参数(前期准备工作)

在执行ServiceConfig.export()时,此时ServiceConfig对象就代表一个服务,我们已经知道了这个服务的名字(就是接口的名字),并且此时这个服务可能已经有一些参数了,就是**@Service注解上所定义的参数**。

但是在Dubbo中,除开可以在@Service注解中给服务配置参数,还有很多地方也可以给服务配置参数,比如:

  1. dubbo.properties文件,你可以建立这个文件,dubbo会去读取这个文件的内容作为服务的参数,Dubob的源码中叫做PropertiesConfiguration
  2. 配置中心,dubbo在2.7版本后就支持了分布式配置中心,你可以在Dubbo-Admin中去操作配置中心,分布式配置中心就相当于一个远程的dubbo.properties文件,你可以在Dubbo-Admin中去修改这个dubbo.properties文件,当然配置中心支持按应用进行配置,也可以按全局进行配置两种,在Dubbo的源码中AppExternalConfiguration表示应用配置,ExternalConfiguration表示全局配置。
  3. 系统环境变量,你可以在启动应用程序时,通过-D的方式来指定参数,在Dubbo的源码中叫SystemConfiguration
  4. 再加上通过@Service注解所配置的参数,在Dubbo的源码中叫AbstractConfig

服务的参数可以从这四个位置来,这四个位置上如果配了同一个参数的话,优先级从高到低如下:

SystemConfiguration** -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> **PropertiesConfiguration


可以看出,-D方式配置的参数优先级最高,配置中心次之,注解随后,dubbo.properties最后。

比如如果服务本身没有配置timeout参数,但是如果服务所属的应用的配置了timeout,那么这个应用下的服务都会继承这个timeout配置。

所以在确定服务参数时,需要先从上级获取参数,获取之后,如果服务本身配置了相同的参数,那么则进行覆盖。

那么我们来看看代码

public synchronized void export() {
    // 把服务参数更新到最新
    checkAndUpdateSubConfigs();

    // 检查服务是否需要导出
    if (!shouldExport()) {
        return;
    }

    // 检查是否需要延迟发布
    if (shouldDelay()) {
        DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
    } else {
        // 导出服务
        doExport();
    }
}
/**
 * 1. ServiceConfig中的某些属性如果是空的,那么就从ProviderConfig、ModuleConfig、ApplicationConfig中获取
 * 2. 从配置中心获取配置,包括应用配置和全局配置
 * 3. 从配置中心获取Provider配置
 * 4. 从配置中心获取Protocol配置
 * 5. 如果ApplicationConfig为空,则构造一个ApplicationConfig
 * 6. 从配置中心获取Registry配置
 * 7. 更新ServiceConfig中的属性为优先级最高的配置
 * 8. 更新MetadataReportConfig中的属性为优先级最高的配置
 * 9. 检查当前服务是不是一个泛化服务
 * 10.检查Stub和Local
 * 11.检查Mock
 */
public void checkAndUpdateSubConfigs() {
    // Use default configs defined explicitly on global configs
    // ServiceConfig中的某些属性如果是空的,那么就从ProviderConfig、ModuleConfig、ApplicationConfig中获取
    completeCompoundConfigs();

    // Config Center should always being started first.
    // 从配置中心获取配置,包括应用配置和全局配置
    // 把获取到的配置放入到Environment中的externalConfigurationMap和appExternalConfigurationMap中
    // 并刷新所有的Config属性
    startConfigCenter();

    // 如果没有ProviderConfig对象,则创建一个
    checkDefault();

    // 如果没有单独的配置protocols,那么就从provider获取配置的协议,添加到的ServiceConfig中去
    // 假如程序员在配置文件中配了一个dubbo协议,配置中心的全局配置或应用配置中也配置了一个协议,那么就会被添加到ServiceConfig中
    checkProtocol();


    checkApplication();


    // if protocol is not injvm checkRegistry
    // 如果protocol不是只有injvm协议,表示服务调用不是只在本机jvm里面调用,那就需要用到注册中心
    // 如果protocol是injvm,表示本地调用
    if (!isOnlyInJvm()) {
        checkRegistry();
    }

    // 刷新ServiceConfig
    this.refresh();

    // 如果配了metadataReportConfig,那么就刷新配置
    checkMetadataReport();

    if (StringUtils.isEmpty(interfaceName)) {
        throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
    }

    // 当前服务对应的实现类是一个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);
        }
        // 刷新MethodConfig,并判断MethodConfig中对应的方法在接口中是否存在
        checkInterfaceAndMethods(interfaceClass, methods);
        // 实现类是不是该接口类型
        checkRef();
        generic = Boolean.FALSE.toString();
    }
    // local和stub一样,不建议使用了
    if (local != null) {
        // 如果本地存根为true,则存根类为interfaceName + "Local"
        if (Boolean.TRUE.toString().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) {
        // 如果本地存根为true,则存根类为interfaceName + "Stub"
        if (Boolean.TRUE.toString().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);
        }
    }
    // 检查local和stub
    checkStubAndLocal(interfaceClass);
    // 检查mock
    checkMock(interfaceClass);
}

1.1、覆盖已经存在的配置

注意这里走的是ServiceBean类!!!

这里什么意思呢,就是在容器内部,我们创建了一些配置,但是有一些配置 我们没有配置,那么就覆盖一下

private void completeCompoundConfigs() {
    // 如果配置了provider,那么则从provider中获取信息赋值其他属性,在这些属性为空的情况下
    if (provider != null) {
        if (application == null) {
            setApplication(provider.getApplication());
        }
        if (module == null) {
            setModule(provider.getModule());
        }
        if (registries == null) {
            setRegistries(provider.getRegistries());
        }
        if (monitor == null) {
            setMonitor(provider.getMonitor());
        }
        if (protocols == null) {
            setProtocols(provider.getProtocols());
        }
        if (configCenter == null) {
            setConfigCenter(provider.getConfigCenter());
        }
    }
    // 如果配置了module,那么则从module中获取信息赋值其他属性,在这些属性为空的情况下
    if (module != null) {
        if (registries == null) {
            setRegistries(module.getRegistries());
        }
        if (monitor == null) {
            setMonitor(module.getMonitor());
        }
    }
    // 如果配置了application,那么则从application中获取信息赋值其他属性,在这些属性为空的情况下
    if (application != null) {
        if (registries == null) {
            setRegistries(application.getRegistries());
        }
        if (monitor == null) {
            setMonitor(application.getMonitor());
        }
    }
}

这里很简单,如果当前容器provider配置存在,那么你的application配置不存在,那么就把provider配置给application配置

当然这里调用的ServiceBean

1.2、注册中心获取配置,并更新配置

void startConfigCenter() {
    // 获取注册中心
    if (configCenter == null) {
        ConfigManager.getInstance().getConfigCenter().ifPresent(cc -> this.configCenter = cc);
    }

    // 如果配置了ConfigCenter
    if (this.configCenter != null) {

        // 从其他位置获取配置中心的相关属性信息,比如配置中心地址
        // TODO there may have duplicate refresh
        this.configCenter.refresh();

        // 属性更新后,从远程配置中心获取数据(应用配置,全局配置)
        prepareEnvironment();
    }

    // 从配置中心取到配置数据后,刷新所有的XxConfig中的属性
    ConfigManager.getInstance().refreshAll();
}

这里需要注意 获取注册中心

1.2.1、获取配置中心

这里我们在dubbo配置中配置中心了配置中心

那么就会在Spring内部有一个ConfigCenterConfig这个对象

// 获取注册中心
if (configCenter == null) {
    ConfigManager.getInstance().getConfigCenter().ifPresent(cc -> this.configCenter = cc);
}

然后利用这里赋值给ServiceBean的configCenter

1.2.2、远程配置获取并存储

private void prepareEnvironment() {
    if (configCenter.isValid()) {
        if (!configCenter.checkOrUpdateInited()) {
            return;
        }

        // 动态配置中心,管理台上的配置中心
        DynamicConfiguration dynamicConfiguration = getDynamicConfiguration(configCenter.toUrl());

        // 如果是zookeeper,获取的就是/dubbo/config/dubbo/dubbo.properties节点中的内容
        String configContent = dynamicConfiguration.getProperties(configCenter.getConfigFile(), configCenter.getGroup());

        String appGroup = application != null ? application.getName() : null;
        String appConfigContent = null;
        if (StringUtils.isNotEmpty(appGroup)) {
            // 获取的就是/dubbo/config/dubbo-demo-consumer-application/dubbo.properties节点中的内容
            // 这里有bug
            appConfigContent = dynamicConfiguration.getProperties
            (StringUtils.isNotEmpty(configCenter.getAppConfigFile()) ? configCenter.getAppConfigFile() : configCenter.getConfigFile(),
             appGroup
            );
        }
        try {
            // 设置ServiceBean 待会刷新配置 咱们的优先级
            // 如果setConfigCenterFirst == true
            // 获取配置顺序如下
            // SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
            
            // 如果setConfigCenterFirst == false
            // 获取配置顺序如下
            // SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration
            
            // 这意味着如果为true,那么咱们自己在@Service配置的就在第四个获取
            // 如果为false,那么咱们在@Service配置的就在第二个获取
            Environment.getInstance().setConfigCenterFirst(configCenter.isHighestPriority());
            // 设置到全局map
            Environment.getInstance().updateExternalConfigurationMap(parseProperties(configContent));
            // 设置到服务级别Map
            Environment.getInstance().updateAppExternalConfigurationMap(parseProperties(appConfigContent));
        } catch (IOException e) {
            throw new IllegalStateException("Failed to parse configurations from Config Center.", e);
        }
    }
}

这里流程是这样的

1、获取配置中心 DynamicConfiguration(zookeeper)

2、利用注册中心 从/dubbo/config/dubbo/dubbo.properties节点中的内容,并获取/dubbo/config/dubbo-demo-consumer-application/dubbo.properties节点内容

3、设置一下配置顺序,这个配置顺序 是关于我们@Service配置获取的顺序,一个第二 第一个第四

4、把/dubbo/config/dubbo/dubbo.properties内容设置到externalConfigurationMap里

5、把/dubbo/config/dubbo-demo-consumer-application/dubbo.properties内容设置到appExternalConfigurationMap里面

1.2.3、更新配置(不包含ServiceBean)

这里更新配置是不包含ServiceBean的

public void refreshAll() {
    // refresh all configs here,
    getApplication().ifPresent(ApplicationConfig::refresh);
    getMonitor().ifPresent(MonitorConfig::refresh);
    getModule().ifPresent(ModuleConfig::refresh);

    getProtocols().values().forEach(ProtocolConfig::refresh);
    getRegistries().values().forEach(RegistryConfig::refresh);
    getProviders().values().forEach(ProviderConfig::refresh);
    getConsumers().values().forEach(ConsumerConfig::refresh);
}

1.2.4、刷新ServiceConfig(ServiceBean)

// 刷新XxConfig
// 一个XxConfig对象的属性可能是有值的,也可能是没有值的,这时需要从其他位置获取属性值,来进行属性的覆盖
// 覆盖的优先级,从大到小为系统变量->配置中心应用配置->配置中心全局配置->注解或xml中定义->dubbo.properties文件

// 以ServiceConfig为例,ServiceConfig中包括很多属性,比如timeout
// 但是在定义一个Service时,如果在注解上没有配置timeout,那么就会其他地方获取timeout的配置
// 比如可以从系统变量->配置中心应用配置->配置中心全局配置->注解或xml中定义->dubbo.properties文件
// refresh是刷新,将当前ServiceConfig上的set方法所对应的属性更新为优先级最高的值
public void refresh() {
    try {
        // 注意这里
        CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());

        // 表示XxConfig对象本身- AbstractConfig
        Configuration config = new ConfigConfigurationAdapter(this);

        if (Environment.getInstance().isConfigCenterFirst()) {
            // The sequence would be: SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
            compositeConfiguration.addConfiguration(4, config);
        } else {
            // The sequence would be: SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration
            compositeConfiguration.addConfiguration(2, config);
        }

        // loop methods, get override value and set the new value back to method
        //
        Method[] methods = getClass().getMethods();
        for (Method method : methods) {
            // 是不是setXX()方法
            if (MethodUtils.isSetter(method)) {
                // 获取xx配置项的value
                String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
                // isTypeMatch() is called to avoid duplicate and incorrect update, for example, we have two 'setGeneric' methods in ReferenceConfig.
                if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
                    method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
                }
                // 是不是setParameters()方法
            } else if (isParametersSetter(method)) {
                // 获取parameter配置项的value
                String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
                if (StringUtils.isNotEmpty(value)) {
                    Map<String, String> map = invokeGetParameters(getClass(), this);
                    map = map == null ? new HashMap<>() : map;
                    map.putAll(convert(StringUtils.parseParameters(value), ""));
                    invokeSetParameters(getClass(), this, map);
                }
            }
        }
    } catch (Exception e) {
        logger.error("Failed to override ", e);
    }
}

这里只需要注意第一行代码

剩下的都是把Method给拆解然后 获取参数

这里我们主要注意这个方法

获取配置

CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());

这里俩个参数为:

public CompositeConfiguration getConfiguration(String prefix, String id) {
    CompositeConfiguration compositeConfiguration = new CompositeConfiguration();
    // Config center has the highest priority

    // JVM环境变量
    compositeConfiguration.addConfiguration(this.getSystemConfig(prefix, id));
    // 操作系统环境变量
    compositeConfiguration.addConfiguration(this.getEnvironmentConfig(prefix, id));

    // 配置中心APP配置
    compositeConfiguration.addConfiguration(this.getAppExternalConfig(prefix, id));

    // 配置中心Gloab配置
    compositeConfiguration.addConfiguration(this.getExternalConfig(prefix, id));

    // dubbo.properties中的配置
    compositeConfiguration.addConfiguration(this.getPropertiesConfig(prefix, id));
    return compositeConfiguration;
}

这里相当于 把所有服务咱们服务的参数都给拿出来

2、导出服务(前期准备工作)

咱们现在要干哪些事(假设只有一个要导出的协议)

1、组装要注册的URL

2、注册到注册中心

3、启动Netty

4、监听配置中心,如果有改动那么需要重新导出

public synchronized void export() {
    // 把服务参数更新到最新
    checkAndUpdateSubConfigs();

    // 检查服务是否需要导出
    if (!shouldExport()) {
        return;
    }

    // 检查是否需要延迟发布
    if (shouldDelay()) {
        DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
    } else {
        // 导出服务
        doExport();
    }
}

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

2.1、获取注册URL

private void doExportUrls() {
    // 得到url,注册服务也是一个服务,所以也会有对应的url,通过调用该url完成服务注册
    List<URL> registryURLs = loadRegistries(true);   //

    // 遍历每个协议
    // 一个协议一个服务
    for (ProtocolConfig protocolConfig : protocols) {
        // path表示服务名
        // contextPath表示应用名(可配置)
        // pathKey = group/contextpath/path:version
        // 例子:myGroup/user/org.apache.dubbo.demo.DemoService:1.0.1

        String pathKey = URL.buildKey(getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);

        // ProviderModel中存在服务提供者访问路径,实现类,接口,以及接口中的各个方法对应的ProviderMethodModel
        // ProviderMethodModel表示某一个方法,方法名,所属的服务的,
        ProviderModel providerModel = new ProviderModel(pathKey, ref, interfaceClass);

        // ApplicationModel表示应用中有哪些服务提供者和引用了哪些服务
        ApplicationModel.initProviderModel(pathKey, providerModel);

        // 每种协议导出一个单独的服务,注册到各个注册中心
        doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }
}

这里其实就相于把注册中心转换成url

protected List<URL> loadRegistries(boolean provider) {
    // check && override if necessary
    List<URL> registryList = new ArrayList<URL>();
    if (CollectionUtils.isNotEmpty(registries)) {
        for (RegistryConfig config : registries) {
            String address = config.getAddress();
            // 如果注册中心没有配地址,则地址为0.0.0.0
            if (StringUtils.isEmpty(address)) {
                address = ANYHOST_VALUE;
            }
            // 如果注册中心的地址不是"N/A"
            if (!RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
                Map<String, String> map = new HashMap<String, String>();
                // 把application中的参数放入map中,注意,map中的key是没有prefix的
                appendParameters(map, application);
                // 把config中的参数放入map中,注意,map中的key是没有prefix的
                // config是RegistryConfig,表示注册中心
                appendParameters(map, config);
                // 此处path值固定为RegistryService.class.getName(),因为现在是在加载注册中心
                map.put(PATH_KEY, RegistryService.class.getName());
                // 把dubbo的版本信息和pid放入map中
                appendRuntimeParameters(map);

                // 如果map中如果没有protocol,那么默认为dubbo
                if (!map.containsKey(PROTOCOL_KEY)) {
                    map.put(PROTOCOL_KEY, DUBBO_PROTOCOL);
                }

                // 构造注册中心url,地址+参数
                List<URL> urls = UrlUtils.parseURLs(address, map);

                for (URL url : urls) {
                    url = URLBuilder.from(url)
                            .addParameter(REGISTRY_KEY, url.getProtocol())
                            .setProtocol(REGISTRY_PROTOCOL)
                            .build();
                    // 到此为止,url的内容大概为:
                    // registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-annotation-provider
                    // &dubbo=2.0.2&pid=269936&registry=zookeeper&timestamp=1584886077813
                    // 该url表示:使用registry协议调用org.apache.dubbo.registry.RegistryService服务
                    // 参数为application=dubbo-demo-annotation-provider&dubbo=2.0.2&pid=269936&registry=zookeeper&timestamp=1584886077813

                    // 这里是服务提供者和服务消费者区别的逻辑
                    // 如果是服务提供者,获取register的值,如果为false,表示该服务不注册到注册中心
                    // 如果是服务消费者,获取subscribe的值,如果为false,表示该引入的服务不订阅注册中心中的数据
                    if ((provider && url.getParameter(REGISTER_KEY, true))
                            || (!provider && url.getParameter(SUBSCRIBE_KEY, true))) {
                        registryList.add(url);
                    }
                }
            }
        }
    }
    return registryList;
}

如果有多个注册中心,那么就会生成多个URL

我们看一下这个url

registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?
application=dubbo-demo-provider1-application
&dubbo=2.0.2
&logger=log4j
&pid=31242
&registry=zookeeper
&release=2.7.0
&timestamp=1695022673057

变成这种url

2.2、遍历协议

为什么要遍历协议,因为一个协议就要注册一次到注册中心

private void doExportUrls() {
    // 得到url,注册服务也是一个服务,所以也会有对应的url,通过调用该url完成服务注册
    List<URL> registryURLs = loadRegistries(true);   //

    // 遍历每个协议
    // 一个协议一个服务
    for (ProtocolConfig protocolConfig : protocols) {
        // path表示服务名
        // contextPath表示应用名(可配置)
        // pathKey = group/contextpath/path:version
        // 例子:myGroup/user/org.apache.dubbo.demo.DemoService:1.0.1

        String pathKey = URL.buildKey(getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);

        // ProviderModel中存在服务提供者访问路径,实现类,接口,以及接口中的各个方法对应的ProviderMethodModel
        // ProviderMethodModel表示某一个方法,方法名,所属的服务的,
        ProviderModel providerModel = new ProviderModel(pathKey, ref, interfaceClass);

        // ApplicationModel表示应用中有哪些服务提供者和引用了哪些服务
        ApplicationModel.initProviderModel(pathKey, providerModel);

        // 每种协议导出一个单独的服务,注册到各个注册中心
        doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }
}

这里协议只有一个我们配置的dubbo

2.3、组装URL

这里没有啥好说的就是拼接参数 组装

最后生成这么个东西

// dubbo://192.168.3.145:20881/org.apache.dubbo.demo.DemoService
// ?anyhost=true
// &application=dubbo-demo-provider1-application
// &bean.name=ServiceBean:org.apache.dubbo.demo.DemoService:1.1.1:g1
// &bind.ip=192.168.3.145
// &bind.port=20881&deprecated=false
// &dubbo=2.0.2
// &dynamic=true
// &generic=false
// &group=g1
// &interface=org.apache.dubbo.demo.DemoService
// &logger=log4j
// &methods=sayHello
// &pid=31467
// &release=2.7.0
// &revision=1.1.1
// &sayHello.loadbalance=random
// &sayHello.return=true
// &side=provider
// &timeout=3000
// &timestamp=1695023256184
// &version=1.1.1

2.4、生成代理对象

这里我把代码给优化了

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
	拼接参数...
	代码优化...

	// export service
    // 通过该host和port访问该服务
    String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
    Integer port = this.findConfigedPorts(protocolConfig, name, map);
    // 服务url
    URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
    // url:http://192.168.40.17:80/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-annotation-provider&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService&bind.ip=192.168.40.17&bind.port=80&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=285072&release=&side=provider&timestamp=1585206500409

    // 可以通过ConfiguratorFactory,在服务导出时候进行统一配置
    if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
            .hasExtension(url.getProtocol())) {
        url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
    }

    String scope = url.getParameter(SCOPE_KEY); // scope可能为null,remote, local,none
    // don't export when none is configured
    if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
        // 如果scope为none,则不会进行任何的服务导出,既不会远程,也不会本地

        // export to local if the config is not remote (export to remote only when config is remote)
        if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
            // 如果scope不是remote,则会进行本地导出,会把当前url的protocol改为injvm,然后进行导出
            exportLocal(url);
        }
        // export to remote if the config is not local (export to local only when config is local)
        if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
            // 如果scope不是local,则会进行远程导出

            if (CollectionUtils.isNotEmpty(registryURLs)) {
                // 如果有注册中心,则将服务注册到注册中心
                for (URL registryURL : registryURLs) {

                    //if protocol is only injvm ,not register
                    // 如果是injvm,则不需要进行注册中心注册
                    if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
                        continue;
                    }

                    // 该服务是否是动态,对应zookeeper就是是否是临时节点,对应dubbo中的功能就是静态服务
                    url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));

                    // 基于注册中心地址的到监控中心地址,为什么是基于注册中心地址?
                    URL monitorUrl = loadMonitor(registryURL);

                    // 把监控中心地址添加到服务url中
                    if (monitorUrl != null) {
                        url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
                    }

                    // 服务的register参数,如果为true,则表示要注册到注册中心
                    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表示服务提供者的代理,可以使用Invoker的invoke方法执行服务
                    // 对应的url为 registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-annotation-provider&dubbo=2.0.2&export=http%3A%2F%2F192.168.40.17%3A80%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-annotation-provider%26bean.name%3DServiceBean%3Aorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.40.17%26bind.port%3D80%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D19472%26release%3D%26side%3Dprovider%26timestamp%3D1585207994860&pid=19472&registry=zookeeper&timestamp=1585207994828
                    // 这个Invoker中包括了服务的实现者、服务接口类、服务的注册地址
                    // 在Dubbo的源码中需要屏蔽具体的服务类型
                    Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));

                    // DelegateProviderMetaDataInvoker也表示服务提供者,包括了Invoker和服务的配置
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                    // 使用特定的协议来对服务进行导出,这里的协议为RegistryProtocol,导出成功后得到一个Exporter
                    // 1. 先使用RegistryProtocol进行服务注册
                    // 2. 注册完了之后,使用DubboProtocol进行导出
                    Exporter<?> exporter = protocol.export(wrapperInvoker);
                    exporters.add(exporter);
                }
            } else {
                // 没有配置注册中心时,也会导出服务

                if (logger.isInfoEnabled()) {
                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                }

                // 下面的代码和存在注册中心时一样,代码虽然一样,但是生成invoker的url是不一样的
                // 当存在注册中心时,是先使用Registy协议注册服务,然后在使用Http协议导出服务
                // 而没有注册中心时,是直接使用Http协议导出服务
                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
             */
            // 根据服务url,讲服务的元信息存入元数据中心
            MetadataReportService metadataReportService = null;
            if ((metadataReportService = getMetadataReportService()) != null) {
                metadataReportService.publishProvider(url);
            }
        }
    }
    this.urls.add(url);
}

这里生成代理对象,就一句话

// 生成一个当前服务接口的代理对象
// 使用代理生成一个Invoker,Invoker表示服务提供者的代理,可以使用Invoker的invoke方法执行服务
// 对应的url为 registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-annotation-provider&dubbo=2.0.2&export=http%3A%2F%2F192.168.40.17%3A80%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-annotation-provider%26bean.name%3DServiceBean%3Aorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.40.17%26bind.port%3D80%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D19472%26release%3D%26side%3Dprovider%26timestamp%3D1585207994860&pid=19472&registry=zookeeper&timestamp=1585207994828
// 这个Invoker中包括了服务的实现者、服务接口类、服务的注册地址
// 在Dubbo的源码中需要屏蔽具体的服务类型

// ref == 我们实例对象(我们测试的DemoServiceImpl)
// interfaceClass == 我们实例对象继承的接口(我们测试的DemoService)
// url == 根据咱们服务生成的url
// 但是这里注意 传递进去的是注册中心url是 registery:// 这个协议开头的
// 这里相当于把url当成一个参数(export) 放到了registery://...(注册中心url)后面

// url == dubbo://192.168.3.145:20881/org.apache.dubbo.demo.DemoService
// ?anyhost=true
// &application=dubbo-demo-provider1-application
// &bean.name=ServiceBean:org.apache.dubbo.demo.DemoService:1.1.1:g1
// &bind.ip=192.168.3.145
// &bind.port=20881&deprecated=false
// &dubbo=2.0.2
// &dynamic=true
// &generic=false
// &group=g1
// &interface=org.apache.dubbo.demo.DemoService
// &logger=log4j
// &methods=sayHello
// &pid=31467
// &release=2.7.0
// &revision=1.1.1
// &sayHello.loadbalance=random
// &sayHello.return=true
// &side=provider
// &timeout=3000
// &timestamp=1695023256184
// &version=1.1.1

Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));

// DelegateProviderMetaDataInvoker也表示服务提供者,包括了Invoker和服务的配置
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

这里需要看一下我们PROXY_FACTORY.getInvoker()这个方法的参数

ref == 咱们的实例对象

interfaceCLass == 咱们的实例对象继承的接口

registerURL == 咱们的注册中心,但是后面有一个参数export 放着我们服务生成的url

2.4.1、JavassistProxyFactory处理创建AbstractProxyInvoker

这个PROXY_FACTORY默认我们没有处理,所以Dubbo的SPI会创建JavassistProxyFactory这个对象处理

public class JavassistProxyFactory extends AbstractProxyFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {

        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        // 如果现在被代理的对象proxy本身就是一个已经被代理过的对象,那么则取代理类的Wrapper,否则取type
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);

        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {

                // 执行proxy的method方法
                // 为什么要用wrapper,因为方便,这里不是代理接口,是直接基于某个对象proxy,进行代理
                // 执行的proxy实例的方法
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

}

这里实际上嵌套了俩层

AbstractProxyInvoker

–调用doInvoke方法的时候

–Wrapper.invokeMethod

2.4.2、DelegateProviderMetaDataInvoker封装

当我们创建完毕后,这个时候我们会获取一个AbstractProxyInvoker这个Invoker,然后在利用DelegateProviderMetaDataInvoker把 AbstractProxyInvoker 和 ServiceBean封装到里面

Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));

// DelegateProviderMetaDataInvoker也表示服务提供者,包括了Invoker和服务的配置
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

DelegateProviderMetaDataInvoker

AbstractProxyInvoker

Wrapper(里面保存我们真正的服务实例)

3、导出服务(RegisterProtocol导出)

Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));

// DelegateProviderMetaDataInvoker也表示服务提供者,包括了Invoker和服务的配置
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

// 使用特定的协议来对服务进行导出,这里的协议为RegistryProtocol,导出成功后得到一个Exporter
// 1. 先使用RegistryProtocol进行服务注册
// 2. 注册完了之后,使用DubboProtocol进行导出
Exporter<?> exporter = protocol.export(wrapperInvoker);

这里注意,我们刚刚上面已经了解到DelegateProviderMetaDataInvoker里面封装咱们Invoker,那么这个Invoker的URL就是咱们的上面AbstractProxyInvoker保存的url,registery://开头,那么Dubbo的SPI就会根据这个registery来找到RegisteryProtocol这个协议类

3.1、导出之前经历的Wrapper类

在经历调用到RegisteryProcol会经过俩个Wrapper类

ProtocolListenerWrapper(先执行)

ProtocolFilterWrapper(后执行)

这俩个类暂时都不会有任何作用,都是放行的

3.1.1、ProtocolListenerWrapper处理RegisteryProtocol导出

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    if (REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        return protocol.export(invoker);
    }
    return new ListenerExporterWrapper<T>(protocol.export(invoker),
            // 得到ExporterListener接口中能用的扩展点,根据url和EXPORTER_LISTENER_KEY进行筛选
            Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                    .getActivateExtension(invoker.getUrl(), EXPORTER_LISTENER_KEY)));
}

3.1.2、ProtocolFilterWrapper处理RegisteryProtocol导出

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    if (REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        return protocol.export(invoker);
    }
    return protocol.export(buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER));
}

3.2、RegisterProtocol导出

@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    // originInvoker就是DelegateProviderMetaDataInvoker,表示一个服务提供者的Invoker

    // 将registry://xxx?xx=xx&registry=zookeeper 转为 zookeeper://xxx?xx=xx
    URL registryUrl = getRegistryUrl(originInvoker); // zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-provider-application&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.40.17%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-provider-application%26bean.name%3DServiceBean%3Aorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.40.17%26bind.port%3D20880%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26logger%3Dlog4j%26methods%3DsayHello%26pid%3D27656%26release%3D2.7.0%26side%3Dprovider%26timeout%3D3000%26timestamp%3D1590735956489&logger=log4j&pid=27656&release=2.7.0&timestamp=1590735956479
    // url to export locally 服务提供者url
    URL providerUrl = getProviderUrl(originInvoker); // dubbo://192.168.40.17:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-provider-application&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService&bind.ip=192.168.40.17&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&logger=log4j&methods=sayHello&pid=27656&release=2.7.0&side=provider&timeout=3000&timestamp=1590735956489

    // Subscribe the override data
    // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call
    //  the same service. Because the subscribed is cached key with the name of the service, it causes the
    //  subscription information to cover.

    // overrideSubscribeUrl是老版本的动态配置监听url,表示了需要监听的服务以及监听的类型(configurators, 这是老版本上的动态配置)
    // 在服务提供者url的基础上,生成一个overrideSubscribeUrl,协议为provider://,增加参数category=configurators&check=false
    final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);

    // 一个overrideSubscribeUrl对应一个OverrideListener,用来监听变化事件,监听到overrideSubscribeUrl的变化后,
    // OverrideListener就会根据变化进行相应处理,具体处理逻辑看OverrideListener的实现
    final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
    overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);


    // 在这个方法里会利用providerConfigurationListener和serviceConfigurationListener去重写providerUrl
    // providerConfigurationListener表示应用级别的动态配置监听器,providerConfigurationListener是RegistyProtocol的一个属性
    // serviceConfigurationListener表示服务级别的动态配置监听器,serviceConfigurationListener是在每暴露一个服务时就会生成一个
    // 这两个监听器都是新版本中的监听器
    // 新版本监听的zk路径是:
    // 服务: /dubbo/config/dubbo/org.apache.dubbo.demo.DemoService.configurators节点的内容
    // 应用: /dubbo/config/dubbo/dubbo-demo-provider-application.configurators节点的内容
    // 注意,要喝配置中心的路径区分开来,配置中心的路径是:
    // 应用:/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService/dubbo.properties节点的内容
    // 全局:/dubbo/config/dubbo/dubbo.properties节点的内容
    providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);

    // export invoker
    // 根据动态配置重写了providerUrl之后,就会调用DubboProtocol或HttpProtocol去进行导出服务了
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

    // url to registry
    // 得到注册中心-ZookeeperRegistry
    final Registry registry = getRegistry(originInvoker);

    // 得到存入到注册中心去的providerUrl,会对服务提供者url中的参数进行简化
    final URL registeredProviderUrl = getRegisteredProviderUrl(providerUrl, registryUrl);

    // 将当前服务提供者Invoker,以及该服务对应的注册中心地址,以及简化后的服务url存入ProviderConsumerRegTable
    ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker,
    registryUrl, registeredProviderUrl);


    //to judge if we need to delay publish
    //是否需要注册到注册中心
    boolean register = providerUrl.getParameter(REGISTER_KEY, true);
    if (register) {
    // 注册服务,把简化后的服务提供者url注册到registryUrl中去
    register(registryUrl, registeredProviderUrl);
    providerInvokerWrapper.setReg(true);
}

    // 针对老版本的动态配置,需要把overrideSubscribeListener绑定到overrideSubscribeUrl上去进行监听
    // 兼容老版本的配置修改,利用overrideSubscribeListener去监听旧版本的动态配置变化
    // 监听overrideSubscribeUrl   provider://192.168.40.17:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-annotation-provider&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService&bind.ip=192.168.40.17&bind.port=20880&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=416332&release=&side=provider&timestamp=1585318241955
    // 那么新版本的providerConfigurationListener和serviceConfigurationListener是在什么时候进行订阅的呢?在这两个类构造的时候
    // Deprecated! Subscribe to override rules in 2.6.x or before.
    // 老版本监听的zk路径是:/dubbo/org.apache.dubbo.demo.DemoService/configurators/override://0.0.0.0/org.apache.dubbo.demo.DemoService?category=configurators&compatible_config=true&dynamic=false&enabled=true&timeout=6000
    // 监听的是路径的内容,不是节点的内容
    registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);


    exporter.setRegisterUrl(registeredProviderUrl);
    exporter.setSubscribeUrl(overrideSubscribeUrl);
    //Ensure that a new exporter instance is returned every time export
    return new DestroyableExporter<>(exporter);
}

这里我们逐步解析

1、获取注册中心URL,获取真实服务URL

2、创建监听器

3.2.1、获取注册中心URL,获取真实服务URL

// 将registry://xxx?xx=xx&registry=zookeeper 转为 zookeeper://xxx?xx=xx
URL registryUrl = getRegistryUrl(originInvoker); // zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-provider-application&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.40.17%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-provider-application%26bean.name%3DServiceBean%3Aorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.40.17%26bind.port%3D20880%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26logger%3Dlog4j%26methods%3DsayHello%26pid%3D27656%26release%3D2.7.0%26side%3Dprovider%26timeout%3D3000%26timestamp%3D1590735956489&logger=log4j&pid=27656&release=2.7.0&timestamp=1590735956479
//  服务提供者url,就是放在原本url当参数(export)那段 服务URL
URL providerUrl = getProviderUrl(originInvoker);

3.2.2、创建监听器

// overrideSubscribeUrl是老版本的动态配置监听url,表示了需要监听的服务以及监听的类型(configurators, 这是老版本上的动态配置)
// 在服务提供者url的基础上,生成一个overrideSubscribeUrl,协议为provider://,增加参数category=configurators&check=false
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);

// 一个overrideSubscribeUrl对应一个OverrideListener,用来监听变化事件,监听到overrideSubscribeUrl的变化后,
// OverrideListener就会根据变化进行相应处理,具体处理逻辑看OverrideListener的实现
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);

1、把原本服务url(dubbo:// 开头) 重新生成了一个 以provider:// 开头的,并增加参数category=configurators&check=false

2、创建一个OverrideListener也就是监听器

3、把重新生成的服务url(provider://) + 监听器,存到这个OverrideListers这个Map里

3.2.3、应用监听器和服务监听器

// 此时的providerUrl == 是dubbo:// 开头的原本服务生成的URL
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);

核心

private URL overrideUrlWithConfig(URL providerUrl, OverrideListener listener) {
    // 在这个方法里会利用providerConfigurationListener和serviceConfigurationListener去重写providerUrl
    // providerConfigurationListener表示应用级别的动态配置监听器,providerConfigurationListener是RegistyProtocol的一个属性
    // serviceConfigurationListener表示服务级别的动态配置监听器,serviceConfigurationListener是在每暴露一个服务时就会生成一个
    // 这两个监听器都是新版本中的监听器
    // 新版本监听的zk路径是:
    // 服务: /dubbo/config/dubbo/org.apache.dubbo.demo.DemoService.configurators节点的内容
    // 应用: /dubbo/config/dubbo/dubbo-demo-provider-application.configurators节点的内容
    // 注意,要喝配置中心的路径区分开来,配置中心的路径是:
    // 应用:/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService/dubbo.properties节点的内容
    // 全局:/dubbo/config/dubbo/dubbo.properties节点的内容

    // 应用配置,providerConfigurationListener是在属性那里直接初始化好的,providerConfigurationListener会监听配置中心的应用配置信息变动
    providerUrl = providerConfigurationListener.overrideUrl(providerUrl);

    // 服务配置,new ServiceConfigurationListener的时候回初始化,ServiceConfigurationListener会监听配置中心的服务信息配置信息变动
    ServiceConfigurationListener serviceConfigurationListener = new ServiceConfigurationListener(providerUrl, listener);
    serviceConfigurationListeners.put(providerUrl.getServiceKey(), serviceConfigurationListener);
    return serviceConfigurationListener.overrideUrl(providerUrl);
}

这里分俩个监听器

providerConfigurationListener:创建RegisteryProtocol就创建了这个这个监听器(应用级别监听器)

ServiceConfigurationListener:导出的时候才创建(服务界别监听器)

3.2.3.1、ProviderConfigurationListener应用级别监听器处理

这个监听器在RegisterProtocol初始化的时候才创建出来

info 初始化

public ProviderConfigurationListener() {
    // // 订阅 应用名+".configurators"
    this.initWith(ApplicationModel.getApplication() + CONFIGURATORS_SUFFIX);
}

这里有init方法我们得看一下

// 在构造ProviderConfigurationListener和ServiceConfigurationListener都会调用到这个方法
// 完成Listener自身订阅到对应的应用和服务
// 订阅关系绑定完了之后,主动从动态配置中心获取一下对应的配置数据生成configurators,后面需要重写providerUrl
protected final void initWith(String key) {
	// 获取注册中心
    DynamicConfiguration dynamicConfiguration = DynamicConfiguration.getDynamicConfiguration();
    // 添加Listener,进行了订阅
    // 往Zookeeper添加监听器,这个this是ProviderConfigurationListener
    dynamicConfiguration.addListener(key, this);

    // 从配置中心ConfigCenter获取属于当前应用的动态配置数据,从zk中拿到原始数据(主动从配置中心获取数据)
    String rawConfig = dynamicConfiguration.getRule(key, DynamicConfiguration.DEFAULT_GROUP);
    // 如果存在应用配置信息则根据配置信息生成Configurator
    if (!StringUtils.isEmpty(rawConfig)) {
        genConfiguratorsFromRawRule(rawConfig);
    }


}

1、获取注册中心(Zookeeper)

2、把自己当成监听器注册到注册中心

3、从配置中心获取数据,并把数据生成Configurator

3.2.3.1.1、监听器注册

info 这里我们看一下怎么注册的

default void addListener(String key, ConfigurationListener listener) {
    addListener(key, DEFAULT_GROUP, listener);
}

key:dubbo-demo-provider1-application.configurators

DEFAULT_GROUP:dubbo

public void addListener(String key, String group, ConfigurationListener listener) {
    // getPathKey(group, key) = /dubbo/config/dubbo/dubbo-demo-provider1-application.configurators
    cacheListener.addListener(getPathKey(group, key), listener);
}

private Map<String, Set<ConfigurationListener>> keyListeners = new ConcurrentHashMap<>();
public void addListener(String key, ConfigurationListener configurationListener) {
    //这里创建了一个CopyOnWrite的把key和监听器都放到这个Map里
    Set<ConfigurationListener> listeners = this.keyListeners.computeIfAbsent(key, k -> new CopyOnWriteArraySet<>());
    listeners.add(configurationListener);
}

这里就是注册到一个Map里,key:Set<监听器>

info 当来修改事件的时候,就会调用

public void dataChanged(String path, Object value, EventType eventType) {
    if (eventType == null) {
        return;
    }

    if (eventType == EventType.INITIALIZED) {
        initializedLatch.countDown();
        return;
    }

    if (path == null || (value == null && eventType != EventType.NodeDeleted)) {
        return;
    }

    // TODO We only care the changes happened on a specific path level, for example
    //  /dubbo/config/dubbo/configurators, other config changes not in this level will be ignored,
    if (path.split("/").length >= MIN_PATH_DEPTH) {
        String key = pathToKey(path);
        ConfigChangeType changeType;
        switch (eventType) {
            case NodeCreated:
                changeType = ConfigChangeType.ADDED;
                break;
            case NodeDeleted:
                changeType = ConfigChangeType.DELETED;
                break;
            case NodeDataChanged:
                changeType = ConfigChangeType.MODIFIED;
                break;
            default:
                return;
        }

        ConfigChangeEvent configChangeEvent = new ConfigChangeEvent(key, (String) value, changeType);
        Set<ConfigurationListener> listeners = keyListeners.get(path);
        if (CollectionUtils.isNotEmpty(listeners)) {
            // 这里来处理
            listeners.forEach(listener -> listener.process(configChangeEvent));
        }
    }
}
public void process(ConfigChangeEvent event) {
    if (logger.isInfoEnabled()) {
        logger.info("Notification of overriding rule, change type is: " + event.getChangeType() +
                ", raw config content is:\n " + event.getValue());
    }

    //
    if (event.getChangeType().equals(ConfigChangeType.DELETED)) {
        configurators.clear();
    } else {
        if (!genConfiguratorsFromRawRule(event.getValue())) {
            return;
        }
    }

    notifyOverrides();
}

// 调用到我们ProviderConfigurationListener
@Override
protected void notifyOverrides() {
    overrideListeners.values().forEach(listener -> ((OverrideListener) listener).doOverrideIfNecessary());
}

画板

3.2.3.1.2、overrideUrl方法处理URL
private <T> URL overrideUrl(URL providerUrl) {
    // 通过configurators去修改/装配providerUrl
    return RegistryProtocol.getConfigedInvokerUrl(configurators, providerUrl);
}

private static URL getConfigedInvokerUrl(List<Configurator> configurators, URL url) {
    if (configurators != null && configurators.size() > 0) {
        for (Configurator configurator : configurators) {
            url = configurator.configure(url);
        }
    }
    return url;
}

这里就是相当于 监听器监听的配置 来修改url

3.2.3.2、Dubbo是如何利用Zookeeper来实现的监听(动态配置)

注意Dubbo是利用Zookeeper来Watch机制来监听的,不过它监听的是dubbo/config这个里面的所有的变化,如果我们改动了就会调用到Watch,这个Watch会根据path来判断该调用哪个监听器,我们在注册监听器的时候会设置一个path

3.2.3.3、ServiceConfigurationListener服务级别监听器处理
private URL overrideUrlWithConfig(URL providerUrl, OverrideListener listener) {
    // 在这个方法里会利用providerConfigurationListener和serviceConfigurationListener去重写providerUrl
    // providerConfigurationListener表示应用级别的动态配置监听器,providerConfigurationListener是RegistyProtocol的一个属性
    // serviceConfigurationListener表示服务级别的动态配置监听器,serviceConfigurationListener是在每暴露一个服务时就会生成一个
    // 这两个监听器都是新版本中的监听器
    // 新版本监听的zk路径是:
    // 服务: /dubbo/config/dubbo/org.apache.dubbo.demo.DemoService.configurators节点的内容
    // 应用: /dubbo/config/dubbo/dubbo-demo-provider-application.configurators节点的内容
    // 注意,要喝配置中心的路径区分开来,配置中心的路径是:
    // 应用:/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService/dubbo.properties节点的内容
    // 全局:/dubbo/config/dubbo/dubbo.properties节点的内容

    // 应用配置,providerConfigurationListener是在属性那里直接初始化好的,providerConfigurationListener会监听配置中心的应用配置信息变动
    providerUrl = providerConfigurationListener.overrideUrl(providerUrl);

    // 服务配置,new ServiceConfigurationListener的时候回初始化,ServiceConfigurationListener会监听配置中心的服务信息配置信息变动
    ServiceConfigurationListener serviceConfigurationListener = new ServiceConfigurationListener(providerUrl, listener);
    serviceConfigurationListeners.put(providerUrl.getServiceKey(), serviceConfigurationListener);
    return serviceConfigurationListener.overrideUrl(providerUrl);
}

这个类创建流程和ProviderConfigurationListener一样,不过他监听的是服务的

/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService:1.1.1:g1.configurators

是这个地址的

然后放到这个serviceConfigurationListeners这个Map里

然后overrideUrl()这个方法执行流程和ProviderConfigurationListener一样,他也是修改url

3.2.4、DubboProtocol服务导出

// export invoker
// 根据动态配置重写了providerUrl之后,就会调用DubboProtocol或HttpProtocol去进行导出服务了
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
    String key = getCacheKey(originInvoker);

    return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
        Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
        // protocol属性的值是哪来的,是在SPI中注入进来的,是一个代理类
        // 这里实际利用的就是DubboProtocol或HttpProtocol去export  NettyServer
        // 为什么需要ExporterChangeableWrapper?方便注销已经被导出的服务
        return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
    });
}

这里从bounds,这个Map里获取,没有就执行这个里面的lambda表达式

此时providerUrl是dubbo://开头的

这里把providerUrl 和 Invoker封装到这个invokerDelegate,然后进行服务导出

这里protocol.export()就会调用到DubboProtocol这个export方法里

不过在调用之前还会经历Wrapper类

3.2.4.1、DubboProtocol经历的Wrapper类

在经历调用到RegisteryProcol会经过俩个Wrapper类

ProtocolListenerWrapper(后执行)

ProtocolFilterWrapper(先执行)

3.2.4.1.1、ProtocolFilterWrapper处理
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    if (REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        return protocol.export(invoker);
    }
    return protocol.export(buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER));
}

这里会经历一个buildInvoker方法 来封装这个invoker

private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
    Invoker<T> last = invoker;
    // 根据url获取filter,根据url中的parameters取key为key的value所对应的filter,但是还会匹配group
    List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

    // ConsumerContextFilter--->FutureFilter--->MonitorFilter
    // ConsumerContextFilter用来设置RpcContext
    //

    if (!filters.isEmpty()) {
        for (int i = filters.size() - 1; i >= 0; i--) {
            final Filter filter = filters.get(i);
            final Invoker<T> next = last;
            last = new Invoker<T>() {

                @Override
                public Class<T> getInterface() {
                    return invoker.getInterface();
                }

                @Override
                public URL getUrl() {
                    return invoker.getUrl();
                }

                @Override
                public boolean isAvailable() {
                    return invoker.isAvailable();
                }

                @Override
                public Result invoke(Invocation invocation) throws RpcException {
                    Result asyncResult;
                    try {
                        // 得到一个异步结果
                        asyncResult = filter.invoke(next, invocation);
                    } catch (Exception e) {
                        // onError callback
                        if (filter instanceof ListenableFilter) {
                            Filter.Listener listener = ((ListenableFilter) filter).listener();
                            if (listener != null) {
                                listener.onError(e, invoker, invocation);
                            }
                        }
                        throw e;
                    }
                    return asyncResult;
                }

                @Override
                public void destroy() {
                    invoker.destroy();
                }

                @Override
                public String toString() {
                    return invoker.toString();
                }
            };
        }
    }

    return new CallbackRegistrationInvoker<>(last, filters);
}

这里会把8个filter都封装 到一个CallbackRegistrationInvoker

CallbackRegistrationInvoker

filter:8个

invoker:InvokerDelegate

InvokerDelegate

DelegateProviderMetaDataInvoker

AbstractProxyInvoker

Wrapper(里面保存我们真正的服务实例

3.2.4.1.2、ProtocolListenerWrapper处理
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    if (REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        return protocol.export(invoker);
    }
    return new ListenerExporterWrapper<T>(protocol.export(invoker),
            // 得到ExporterListener接口中能用的扩展点,根据url和EXPORTER_LISTENER_KEY进行筛选
            Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                    .getActivateExtension(invoker.getUrl(), EXPORTER_LISTENER_KEY)));
}

这里是后续处理,当DubboProtocol导出完毕后,就会把DubboProtocol返回的DubboExporter给封装

封装成一个ListenerExporterWrapper,并且会利用SPI找ExporterListener这个类,给一并封装进去

3.2.4.2、DubboProtocol导出逻辑
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    URL url = invoker.getUrl();

    // export service.
    String key = serviceKey(url);
    // 构造一个Exporter
    DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
    exporterMap.put(key, exporter);

    //export an stub service for dispatching event
    Boolean isStubSupportEvent = url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT);
    Boolean isCallbackservice = url.getParameter(IS_CALLBACK_SERVICE, false);
    if (isStubSupportEvent && !isCallbackservice) {
        String stubServiceMethods = url.getParameter(STUB_EVENT_METHODS_KEY);
        if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
            if (logger.isWarnEnabled()) {
                logger.warn(new IllegalStateException("consumer [" + url.getParameter(INTERFACE_KEY) +
                                                      "], has set stubproxy support event ,but no stub methods founded."));
            }

        } else {
            // 服务的stub方法
            stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
        }
    }

    // 开启NettyServer
    openServer(url);

    optimizeSerialization(url);

    return exporter;
}

1、获取url,此时的url是dubbo://

2、获取ServiceKey(组 + 服务名 + 版本 + 端口号)

g1/org.apache.dubbo.demo.DemoService:1.1.1:20881

3、把Invoker 和 ServiceKey 封装成DubboExporter

4、把这个DubboExporter 放入Map<ServiceKey,DubboExporter>

为什么要放入这个Map因为,客户端来请求的时候就可以带url来转换成ServiceKey,然后找到DubboExporter,就可以执行真正的方法Invoker了

5、// 开启NettyServer

到此导出结束

4、复盘逻辑

4.1、服务端Invoker架构

  1. ProtocolFilterWrapper$CallbackRegistrationInvoker:会去调用下层Invoker,下层Invoker执行完了之后,会遍历过滤器,查看是否有过滤器实现了ListenableFilter接口,如果有,则回调对应的onResponse方法,比如TimeoutFilter,当调用完下层Invoker之后,就会计算服务的执行时间
  2. ProtocolFilterWrapper$1:ProtocolFilterWrapper中的过滤器组成的Invoker,利用该Invoker,可以执行服务端的过滤器,执行完过滤器之后,调用下层Invoker
  3. RegistryProtocol$InvokerDelegate:服务的的委托类,里面包含了DelegateProviderMetaDataInvoker对象和服务对应的providerUrl,执行时直接调用下层Invoker
  4. DelegateProviderMetaDataInvoker:服务的的委托类,里面包含了AbstractProxyInvoker对象和ServiceConfig对象,执行时直接调用下层Invoker
  5. AbstractProxyInvoker:服务接口的代理类,绑定了对应的实现类,执行时会利用反射调用服务实现类实例的具体方法,并得到结果

4.2、Exporter架构

![](https://i-blog.csdnimg.cn/img_convert/8856711830e9d7a1ce06024b8fd10607.png)

一个服务导出成功后,会生成对应的Exporter:

  1. DestroyableExporter:Exporter的最外层包装类,这个类的主要作用是可以用来unexporter对应的服务
  2. ExporterChangeableWrapper:这个类主要负责在unexport对应服务之前,把服务URL从注册中心中移除,把该服务对应的动态配置监听器移除
  3. ListenerExporterWrapper:这个类主要负责在unexport对应服务之后,把服务导出监听器移除
  4. DubboExporter:这个类中保存了对应服务的Invoker对象,和当前服务的唯一标志,当NettyServer接收到请求后,会根据请求中的服务信息,找到服务对应的DubboExporter对象,然后从对象中得到Invoker对象

4.3、监听逻辑图![](https://i-blog.csdnimg.cn/img_convert/604b7a8570ef91254d6385970a36226d.png)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值