Dubbo服务导出

书接上回dubbo整合spring后会往Spring里面添加ServiceBean.class的beanDefinition。
当ServiceBean被Spring实例化后,会回调其Bean的生命周期方法onApplicationEvent()

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

思考:服务导出应该有那几步?

1、加载配置 这么多配置优先级怎么确定?
2、启动tomcat、nettry 开放远程服务端口
3、将本地的服务链接写入注册中心供,服务调用放发现服务
4、监听配置中心的变更。例如修改了timeout、心跳检测时间、限流等等

![image.png](https://img-blog.csdnimg.cn/img_convert/36dbcbad1d91e9fee269f481672762a2.png#clientId=udeeb0ff6-e299-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=748&id=uefa75fa7&margin=[object Object]&name=image.png&originHeight=1496&originWidth=2672&originalType=binary&ratio=1&rotation=0&showTitle=false&size=471388&status=done&style=none&taskId=u979f752d-4ecb-40dd-9343-ab6445aa01c&title=&width=1336)

配置加载与融合

ServiceConfig#checkAndUpdateSubConfigs() 该方法会检查并更新最新Dubbo服务的配置

public synchronized void export() {
        checkAndUpdateSubConfigs();

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

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

1、将公共的配置赋值给ServiceBean(ServiceConfiig)的属性
2、启动配置中心并更新本地配置
3、将多个地方获取的配置通过this.refresh();刷新到ServiceBean中

 public void checkAndUpdateSubConfigs() {
        // Use default configs defined explicitly on global configs
        // ServiceConfig中的某些属性如果是空的,那么就从ProviderConfig、ModuleConfig、ApplicationConfig中获取
        // 补全ServiceConfig中的属性
        completeCompoundConfigs();

        // Config Center should always being started first.
        // 从配置中心获取配置,包括应用配置和全局配置
        // 把获取到的配置放入到Environment中的externalConfigurationMap和appExternalConfigurationMap中
        // 并刷新所有的XxConfig的属性(除开ServiceConfig),刷新的意思就是将配置中心的配置覆盖调用XxConfig中的属性
        startConfigCenter();

        checkDefault();

        checkProtocol();

        checkApplication();

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

        // 刷新ServiceConfig
        this.refresh();

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

        if (StringUtils.isEmpty(interfaceName)) {
            throw new IllegalStateException(" 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);
    }

dubbo服务公共配置

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

启动配置中心

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中的属性,除开ServiceConfig
        ConfigManager.getInstance().refreshAll();
    }


多个配置融合刷新ServiceBean(ServiceConfig)

    // 刷新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);  // 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);
        }
    }

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

_/**

  • List holding all the configuration
    */
    _private List configList = new LinkedList();

本质就是通过linkedList维护了各个配置的顺序。

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

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

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

最后获取配置会调用到 CompositeConfiguration#getInternalProperty
遍历前面按顺序排好优先级的list。

    @Override
    public Object getInternalProperty(String key) {
        Configuration firstMatchingConfiguration = null;

        //
        for (Configuration config : configList) {
            try {
                if (config.containsKey(key)) {
                    firstMatchingConfiguration = config;
                    break;
                }
            } catch (Exception e) {
                logger.error("Error when trying to get value for key " + key + " from " + config + ", will continue to try the next one.");
            }
        }
        if (firstMatchingConfiguration != null) {
            return firstMatchingConfiguration.getProperty(key);
        } else {
            return null;
        }
    }
注册中心

注册中心的url会以registry开头。
url参数中registry=zookeeper,可以猜测后面肯定用到了Dubbo-Spi机制有个Zooeeper的注册类

   /**
     *
     * Load the registry and conversion it to {@link URL}, the priority order is: system property > dubbo registry config
     *
     * @param provider whether it is the provider side
     * @return
     */
    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®istry=zookeeper×tamp=1584886077813
                        // 该url表示:使用registry协议调用org.apache.dubbo.registry.RegistryService服务
                        // 参数为application=dubbo-demo-annotation-provider&dubbo=2.0.2&pid=269936®istry=zookeeper×tamp=1584886077813

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

编码ServerBean的配置为URL

Dubbo中的服务都是以URL的方式来表示

 private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
        // protocolConfig表示某个协议,registryURLs表示所有的注册中心

        // 如果配置的某个协议,没有配置name,那么默认为dubbo
        String name = protocolConfig.getName();
        if (StringUtils.isEmpty(name)) {
            name = DUBBO;
        }

        // 这个map表示服务url的参数
        Map<String, String> map = new HashMap<String, String>();
        map.put(SIDE_KEY, PROVIDER_SIDE);

        appendRuntimeParameters(map);

        // 监控中心参数
        appendParameters(map, metrics);
        // 应用相关参数
        appendParameters(map, application);
        // 模块相关参数
        appendParameters(map, module);
        // remove 'default.' prefix for configs from ProviderConfig
        // appendParameters(map, provider, Constants.DEFAULT_KEY);

        // 提供者相关参数
        appendParameters(map, provider);

        // 协议相关参数
        appendParameters(map, protocolConfig);

        // 服务本身相关参数
        appendParameters(map, this);
     
        ...............
 }

启动服务端

 private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> 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);

           // 当前服务连接哪个监控中心
           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
           // 服务使用的动态代理机制,如果为空则使用javassit
           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®istry=zookeeper×tamp=1585207994828
           // 这个Invoker中包括了服务的实现者、服务接口类、服务的注册地址(针对当前服务的,参数export指定了当前服务)
           // 此invoker表示一个可执行的服务,调用invoker的invoke()方法即可执行服务,同时此invoker也可用来导出
           Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
           // invoker.invoke(Invocation)

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

           // 使用特定的协议来对服务进行导出,这里的协议为RegistryProtocol,导出成功后得到一个Exporter
           // 1. 先使用RegistryProtocol进行服务注册
           // 2. 注册完了之后,使用DubboProtocol进行导出
           // 到此为止做了哪些事情? ServiceBean.export()-->刷新ServiceBean的参数-->得到注册中心URL和协议URL-->遍历每个协议URL-->组成服务URL-->生成可执行服务Invoker-->导出服务
           Exporter<?> exporter = protocol.export(wrapperInvoker);
           exporters.add(exporter);
        ...............
 }

核心方法就是Exporter exporter = protocol.export(wrapperInvoker);
wrapperInvoker.getUrl() 返回的Url,getProtocol()方法返回的就是url的协议:registry

@Override
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        // 导出服务
        // registry://   ---> RegistryProtocol
        // zookeeper://  ---> ZookeeperRegistry
        // dubbo://      ---> DubboProtocol

        // registry://xxx?xx=xx®istry=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×tamp=1590735956479
        // 得到服务提供者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×tamp=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);
        ........
    }
    @SuppressWarnings("unchecked")
    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);
        });
    }

invokerDelegate.getUrl().getProtocol() 已经是dubbo

 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);  //请求--->invocation--->服务key--->exporterMap.get(key)--->exporter--->invoker--->invoker.invoke(invocation)-->执行服务

        // 特殊的一些序列化机制,比如kryo提供了注册机制来注册类,提高序列化和反序列化的速度
        optimizeSerialization(url);

        return exporter;
    }
  private void openServer(URL url) {
        // find server.
        String key = url.getAddress(); // 获得ip地址和port, 192.168.40.17:20880

        // NettyClient, NettyServer
        //client can export a service which's only for server to invoke
        boolean isServer = url.getParameter(IS_SERVER_KEY, true);
        if (isServer) {
            // 缓存Server对象
            ExchangeServer server = serverMap.get(key);

            // DCL,Double Check Lock
            if (server == null) {
                synchronized (this) {
                    server = serverMap.get(key);
                    if (server == null) {
                        // 创建Server,并进行缓存
                        serverMap.put(key, createServer(url));
                    }
                }
            } else {
                // server supports reset, use together with override
                // 服务重新导出时,就会走这里
                server.reset(url);
            }
        }
    }

    /**
     * Init and start netty server
     *
     * @throws Throwable
     */
    @Override
    protected void doOpen() throws Throwable {
        bootstrap = new ServerBootstrap();

        bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));
        workerGroup = new NioEventLoopGroup(getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
                new DefaultThreadFactory("NettyServerWorker", true));

        final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
        channels = nettyServerHandler.getChannels();

        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
                .childOption(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
                .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        // FIXME: should we use getTimeout()?
                        int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
                        // 这里就会拿到DubboCodec,接收到数据之后就会进行解码
                        NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
                        ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug
                                .addLast("decoder", adapter.getDecoder())
                                .addLast("encoder", adapter.getEncoder())
                                .addLast("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
                                .addLast("handler", nettyServerHandler);
                    }
                });
        // bind
        ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
        channelFuture.syncUninterruptibly();
        channel = channelFuture.channel();

    }

注册中心暴露服务

1、会将服务的url简化一下。因为很多配置项不需要暴露出去。只是在本地启动服务的时候需要
2、将简化好的服务写入注册中心既可

@Override
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        ........
         // 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);
        }
       ..........
    }
   public void register(URL registryUrl, URL registeredProviderUrl) {
        Registry registry = registryFactory.getRegistry(registryUrl);
        // 调用ZookeeperRegistry的register方法
        registry.register(registeredProviderUrl);
    }
    @Override
    public void doRegister(URL url) {
        try {
            zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
        } catch (Throwable e) {
            throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

监听配置中心配置

    @Override
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        .................. 
        // 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);
        
        // 针对老版本的动态配置,需要把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×tamp=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);
        ..................
       }

如果配置变更了会重新的导出,但是不会重新的启动nettry服务。端口和ip是允许修改的

public <T> void reExport(final Invoker<T> originInvoker, URL newInvokerUrl) {

        // 根据newInvokerUrl进行导出
        // update local exporter
        ExporterChangeableWrapper exporter = doChangeLocalExport(originInvoker, newInvokerUrl);

        // 获取准确的ProviderUrl
        // update registry
        URL registryUrl = getRegistryUrl(originInvoker);
        // 对于一个服务提供者url,在注册到注册中心时,会先进行简化
        final URL registeredProviderUrl = getRegisteredProviderUrl(newInvokerUrl, registryUrl);

        //decide if we need to re-publish
        // 根据getServiceKey获取ProviderInvokerWrapper
        ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.getProviderWrapper(registeredProviderUrl, originInvoker);
        // 生成一个新的ProviderInvokerWrapper
        ProviderInvokerWrapper<T> newProviderInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registeredProviderUrl);

        /**
         * Only if the new url going to Registry is different with the previous one should we do unregister and register.
         * 如果新的服务提供者url简化后的url和这个服务之前的服务提供者url简化后的url不相等,则需要把新的简化后的服务提供者url注册到注册中心去
         */
        if (providerInvokerWrapper.isReg() && !registeredProviderUrl.equals(providerInvokerWrapper.getProviderUrl())) {
            unregister(registryUrl, providerInvokerWrapper.getProviderUrl());
            register(registryUrl, registeredProviderUrl);
            newProviderInvokerWrapper.setReg(true);
        }

        exporter.setRegisterUrl(registeredProviderUrl);
    }

你可能感兴趣的:(dubbo,java,rpc,开发语言)