跳至主要內容

服务导出(服务注册)

Yaien Blog原创大约 6 分钟微服务RPCdubbo

服务导出(服务注册)

本文主要记录学习Dubbo 整合 Spring 的源码笔记。记录服务导出(注册源码)的学习笔记。

服务导出(服务注册)的核心方法是ServiceBean对象中监听器里的export();调用时机是在Spring容器启动完成之后发布的完成事件,
ServiceBean通过监听该事件然后去进行服务导出(服务注册)。

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

大体流程

主要做三件事

  1. 读取配置

    读取配置其实就是要获取dubbo服务在启动时的一些配置信息,例如@Service注解、config文件等。
    其实这些在服务注册前就已经有一些配置解析好保存到ServiceBean中了,而读取配置真正意义上来说是读取当前服务
    最新(优先级最高的)、最全的配置。

  2. 服务注册(导出)

    获取注册中心地址,生成服务提供者服务地址,注将服务地址注册到注册中心

  3. 启动Netty/Tomcat

    如果是dubbo协议,启动的是Netty,Rest启动的是Tomcat

  4. 服务提供者监听配置更新

    服务提供者会监听配置中心配置的变更,修改本地配置

checkAndUpdateSubConfigs()

  • 补全配置,ServiceConfig中的某些属性如果是空的,那么就从ProviderConfig、ModuleConfig、ApplicationConfig中获取并补全
  • 连接配置中心
    • 获取配置中心数据,包括全局和应用配置,放到externalConfigurationMap和appExternalConfigurationMap中
    • 刷新所有的xxxConfig中的属性(除了ServiceCOnfig),即覆盖掉对象里面的属性值
  • 进行一系列的检查
  • 判断protocol是不是只有injvm协议,不是即服务调用不只是在本机jvm里调用,需要用到注册中心,然后会校验是否有配置了注册中心的地址
  • 刷新ServiceConfig,刷新某个服务的配置
    • 创建一个Configuration列表,用compositeConfiguration进行封装。用来存各个位置的配置
      • (JVM环境变量、操作系统环境变量、配置中心APP配置、配置中心Global配置、dubbo中的properties文件配置)
    • 校验configCenterFirst配置,是否配置中心配置优先,默认true,根据这个来处理配置优先级
      • true :SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig ->
        PropertiesConfiguration
      • false :SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration ->
        PropertiesConfiguration
      • 按照以上顺序存到Configuration列表中
    • 覆盖ServiceBean中的配置
      • 便利ServiceBean中的方法
      • 是set方法
        • 截取方法名,获取属性名
        • 通过属性名从compositeConfiguration按优先级获取,获取到覆盖ServiceBean中的属性值并返回
      • 是setParameters方法
        • 获取parameter配置项的value
        • 覆盖ServiceBean中的属性值并返回

刷新ServiceConfig配置的源码

    public void refresh() {
        try {
            CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());

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

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

export

校验是否执行服务导出,通过属性可进行配置,默认true

delay

校验是否延迟导出,通过delay属性可进行配置,默认0不延迟(毫秒)

doExport()

调用doExport方法进行服务导出

loadRegistries()

loadRegistries方法主要做的事情就是获取配置用户配置的注册中心,最终会返回一个URL列表,一个URL就代表一个注册中心地址,URL的内容如下:

registry://1.12.242.126:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-provider1-application&dubbo=2.0.2&logger=log4j&pid=93947&registry=zookeeper&release=2.7.0&timeout=3000&timestamp=1684854503882

此时URL并没有标识是使用zookeeper或者nacos,而是先试用registry进行标识

doExportUrlsFor1Protocol()

这个方法主要做的事情就是为每一个协议,都导出一个服务。例如提供者提供了一个UserService服务,但是我配置了两个协议,那此时就会生成两个UserService服务地址,并且都会注册到所有的注册中心上。

具体实现流程

  • 获取协议名称,没有默认就是dubbo
  • 构建一个map,用来存服务url的参数,并往map中填值
  • 构建Token,Token是为了防止服务被消费者直接调用(伪造http请求)
    • token生成规则:如果没有配则没有,配置true或者default自动通过uuid生成,否者使用配置的字符做token
  • 构建服务URL
  • 生成一个当前服务接口的代理对象,使用代理对象生成一个Invoker,Invoker表示服务提供者的代理,可以使用Invoker的invoke方法执行服务
  • 封装DelegateProviderMetaDataInvoker,包括了Invoker和服务的配置
  • 导出服务&服务注册
    • 从invoker种获取注册中心URL(registerUrl)和服务提供者URL(providerUrl)
    • 在providerUrl的基础上生成overrideSubscribeUrl,这个是老版本的动态配置监听url,表示了需要监听的服务以及监听的类型(configurators,
      这是老版本上的动态配置)s
    • 创建OverrideListener监听器,用来监听overrideSubscribeUrl配置变更(老版本)
    • 添加两个监听器(新版本的监听器)
      • providerConfigurationListener:表示应用级别的动态配置监听器,属于RegistyProtocol的一个属性
      • serviceConfigurationListener:表示服务级别的动态配置监听器,每暴露一个服务时就会生成一个
    • 通过两个监听器,重写providerUrl
    • 使用重写过后的providerUrl,调用doLocalExport()进行服务导出
    • 获取注册中心(通过SPI扩展点机制)
    • 简化服务URL并注册到注册中心

doLocalExport()

此方法就是真正去执行服务导出的逻辑,里面会调用protocol.export(invokerDelegate)去导出服务

具体实现

  • 获取服务URL
  • 通过URL生成一个key
  • 构造一个Exporter进行服务导出
  • 存到exporterMap中
  • 调用openServer启动Netty/Tomcat

服务提供者监听配置更新

服务提供者主要监听动态配置,在服务运行是时可以动态修改服务配置,会监听三个节点,一个是老版本的节点以及两个新节点

补充

当dubbo的某一个服务导出完了之后,会发布一个Spring的时间,如果想知道某个服务是否已经导出完毕,可以监听ServiceBeanExportedEvent实现。

上次编辑于:
贡献者: yanggl