Dubbo服务端发布流程

1、背景

了解 消费方向服务提供方发起调用时,消费方如何知道提供者的地址

2、配置

注解配置

1、启动Dubbo框架注解

2、@DubboService 注解

 
dubbo:
  json-framework:
    prefer: jackson
  provider:
    delay: -1
  protocol:
    port: ${server.port}
    name: tri
    #port: 50052
    triple:
      enable-servlet: true
      prefer-serialization: fastjson2
      serialization: fastjson2
  application:
    name: ${spring.application.name}
    qos-enable: true
    manual-register: true
    check-serializable: false
    serialize-check-status: DISABLE
    #应用级注册
    register-mode: instance
  registry:
    use-as-config-center: false
    use-as-metadata-center: false
    address: nacos://${spring.cloud.nacos.discovery.server-addr}
    parameters:
      accessKey: ${spring.cloud.nacos.discovery.accessKey}
      secretKey: ${spring.cloud.nacos.discovery.secretKey}
      namespace: ${spring.cloud.nacos.discovery.namespace}
  shutdown:
    timeout: 60
    step-timeout: 3
    consumer-update-wait-time: 3
    internal-signal: true
    offline-request-window-timeout: 0
  #关闭dubbo默认检查提供者是否可用(解决必须先启动提供者后启动消费者)
  consumer:
    check: false
    timeout: 3000
    retries: 0

详见:dubbo-spring-boot-autoconfigure-compatible

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

注解处理器

后置处理器 ServiceAnnotationPostProcessor

注解列表 serviceAnnotationTypes

扫描服务列表 scanServiceBeans

代码
private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {

 
     //扫描@Dubbo路径下的所有Bean定义
    DubboClassPathBeanDefinitionScanner scanner =
            new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);

    BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
    scanner.setBeanNameGenerator(beanNameGenerator);
    for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
        scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
    }

    ScanExcludeFilter scanExcludeFilter = new ScanExcludeFilter();
    scanner.addExcludeFilter(scanExcludeFilter);

    for (String packageToScan : packagesToScan) {

        // Registers @Service Bean first
         扫描给定的包名,其实也就是扫描 cn.itbox.uap 路径
        // 扫描那些属于 serviceAnnotationTypes 注解类型的 Bean 定义
        scanner.scan(packageToScan);

        // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
        // 处理扫描到的 Bean 定义对象集合
        Set<BeanDefinitionHolder> beanDefinitionHolders =
                findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

        if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
            for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                //单个处理
                processScannedBeanDefinition(beanDefinitionHolder);
                servicePackagesHolder.addScannedClass(
                        beanDefinitionHolder.getBeanDefinition().getBeanClassName());
            }
        }
        servicePackagesHolder.addScannedPackage(packageToScan);
    }
}

Bean定义方法 processScannedBeanDefinition

private void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder) {

// 从 Bean 定义中找到对应的 Dubbo 接口服务类信息
// 即:beanClass = cn.itbox.xxxx.DubboService 类信息
    Class<?> beanClass = resolveClass(beanDefinitionHolder);

 // 从 beanClass 中找到含有 @DubboService 注解的对象
 // 准确来说,应该是查找 serviceAnnotationTypes 列表中有值的注解,取出找到的第一个
    Annotation service = findServiceAnnotation(beanClass);

    // The attributes of @Service annotation
    // 从刚刚找出的 service 注解对象中搜刮出注解属性值
    // @DubboService 中配置的 timeout = 8888 属性就会存在于
    Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);

    // 找到对应的服务接口名称
    // 即:serviceInterface = cn.itbox.xxxx.DubboService
    String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass);
   // annotatedServiceBeanName = DubboServiceImpl
    String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

    // ServiceBean Bean name
    // 生成 ServiceBean 的 beanName
    String beanName = generateServiceBeanName(serviceAnnotationAttributes, serviceInterface);

// 重新根据相关参数构建出一个新的 beanClass = ServiceBean.class 类型的 Bean 定义
    AbstractBeanDefinition serviceBeanDefinition =
            buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, annotatedServiceBeanName);

// 然后将构建出来的 Bean 定义注册到 Spring 的容器中,等着后续的实例化操作
    registerServiceBeanDefinition(beanName, serviceBeanDefinition, serviceInterface);
}

步骤

1、利用扫描器,把含有 @DubboService 注解的类扫描出来,得到一堆 Bean 定义集合。

2、循环扫描出来的 Bean 定义集合,在循环体中,从已扫描的 Bean 定义中,提取注解属性和

服务接口名,然后创建一个新的 ServiceBean 类型的 Bean 定义。

3、新 Bean 定义在实例化后被使用时,一定会触发调用 ServiceBean 类或父类中

的一些 public 方法。

验证

查找服务注解 findServiceAnnotation

获取服务注解 getServiceAnnotationAttributes

ServiceBean

ServiceConfig

3、导出

Dubbo服务导出过程

原理与步骤

在应用启动过程中,在启动类上加上 @EnableDubbo 时,该注解上有一个 @DubboComponentScan 注解,@DubboComponentScan 注解 Import 了一个 DubboComponentScanRegistrar,DubboComponentScanRegistrar 中会调用 DubboSpringInitializer.initialize(),该方法中会注册一个 DubboDeployApplicationListener,而 DubboDeployApplicationListener 会监听 Spring 容器启动完成事件 ContextRefreshedEvent,一旦接收到这个事件后,就会开始 Dubbo 的启动流程,就会执行 DefaultModuleDeployer 的 start() 进行服务导出与服务引入。

@EnableDubbo(scanBasePackages = {"cn.itbox.uap.sys.setting"})

 
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo
 
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan 

DubboComponentScanRegistrar 初始化

public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

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

        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);

        registerServiceAnnotationPostProcessor(packagesToScan, registry);
    }
   }

DubboSpringInitializer 在Bean 初始化完成之后触发 DubboSpringInitializer onContextRefreshedEvent 事件

DubboDeployApplicationListener

DubboDeployApplicationListener 触发 DefaultModuleDeployer.start方法

 
public class DubboDeployApplicationListener
        implements ApplicationListener<ApplicationContextEvent>, ApplicationContextAware, Ordered {

private void onContextRefreshedEvent(ContextRefreshedEvent event) {
    ModuleDeployer deployer = moduleModel.getDeployer();
    Assert.notNull(deployer, "Module deployer is null");
    Object singletonMutex = LockUtils.getSingletonMutex(applicationContext);
    // start module
    Future future = null;
    synchronized (singletonMutex) {
        future = deployer.start();
    }
  }
 }

DefaultModuleDeployer

public class DefaultModuleDeployer extends AbstractDeployer<ModuleModel> implements ModuleDeployer {
@Override
public Future start() throws IllegalStateException {
    // initialize,maybe deadlock applicationDeployer lock & moduleDeployer lock
    applicationDeployer.initialize();

    return startSync();
}



private synchronized Future startSync() throws IllegalStateException {
 
        onModuleStarting();
        initialize();
        // export services 
        // 调用导出服务方法
        exportServices();
   
    return startFuture;
}

private void exportServices() {
    for (ServiceConfigBase sc : configManager.getServices()) {
        exportServiceInternal(sc);
    }
}


private void exportServiceInternal(ServiceConfigBase sc) {
    ServiceConfig<?> serviceConfig = (ServiceConfig<?>) sc;
    if (!serviceConfig.isRefreshed()) {
        serviceConfig.refresh();
    }
    if (exportAsync || sc.shouldExportAsync()) {
        ExecutorService executor = executorRepository.getServiceExportExecutor();
        CompletableFuture<Void> future = CompletableFuture.runAsync(
                () -> {
                        if (!sc.isExported()) {
                            sc.export();
                            exportedServices.add(sc);
                        }
                    
                },
                executor);
        asyncExportingFutures.add(future);
    } else {
        if (!sc.isExported()) {
            sc.export(RegisterTypeEnum.AUTO_REGISTER_BY_DEPLOYER);
            exportedServices.add(sc);
        }
    }
}
}

ServiceConfig.export

@Override
public void export(RegisterTypeEnum registerType) {
    synchronized (this) {
        if (this.shouldExport()) {
            this.init();

            if (shouldDelay()) {
                // should register if delay export
                doDelayExport();
            } else if (Integer.valueOf(-1).equals(getDelay())
                    && Boolean.parseBoolean(ConfigurationUtils.getProperty(
                            getScopeModel(), CommonConstants.DubboProperty.DUBBO_MANUAL_REGISTER_KEY, "false"))) {
                // should not register by default
                doExport(RegisterTypeEnum.MANUAL_REGISTER);
            } else {
                doExport(registerType);
            }
        }
    }
}

private void doExportUrls(RegisterTypeEnum registerType) {
    ModuleServiceRepository repository = getScopeModel().getServiceRepository();
    ServiceDescriptor serviceDescriptor;
    final boolean serverService = ref instanceof ServerService;
    providerModel = new ProviderModel(
            serviceMetadata.getServiceKey(),
            ref,
            serviceDescriptor,
            getScopeModel(),
            serviceMetadata,
            interfaceClassLoader);

    // Compatible with dependencies on ServiceModel#getServiceConfig(), and will be removed in a future version
    providerModel.setConfig(this);

    providerModel.setDestroyRunner(getDestroyRunner());
    repository.registerProvider(providerModel);

    List<URL> registryURLs = !Boolean.FALSE.equals(isRegister())
            ? ConfigValidationUtils.loadRegistries(this, true)
            : Collections.emptyList();

    for (ProtocolConfig protocolConfig : protocols) {
        String pathKey = URL.buildKey(
                getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);
        // stub service will use generated service name
        if (!serverService) {
            // In case user specified path, register service one more time to map it to path.
            repository.registerService(pathKey, interfaceClass);
        }
        doExportUrlsFor1Protocol(protocolConfig, registryURLs, registerType);
    }

    providerModel.setServiceUrls(urls);
}

导出代码分析

@Override
public void export(RegisterTypeEnum registerType) {

    //已经导出的话,则不再处理导出逻辑
    if (this.exported) {
        return;
    }
    //synchronized 保证线程安全,即只允许导出有且仅有一次
    synchronized (this) {
        //再次检测
        if (this.exported) {
            return;
        }
        //Dubbo Config 属性重写
        if (!this.isRefreshed()) {
            this.refresh();
        }
        //默认为true  判断provider是否已经导出,已导出则false
        if (this.shouldExport()) {
            this.init();

            if (shouldDelay()) {
                // should register if delay export 是否延迟导出
                doDelayExport();
            } else if (Integer.valueOf(-1).equals(getDelay())
                    && Boolean.parseBoolean(ConfigurationUtils.getProperty(
                            getScopeModel(), CommonConstants.DubboProperty.DUBBO_MANUAL_REGISTER_KEY, "false"))) {
                // should not register by default
                doExport(RegisterTypeEnum.MANUAL_REGISTER);
            } else {
                //导出核心逻辑
                doExport(registerType);
            }
        }
    }
}

 // 单个接口服务的导出方法,比如 cn.itbox.xxxx.DubboService 服务的导出,就会进入到这里来
 org.apache.dubbo.config.ServiceConfig#doExport
 ↓
 // 因为存在多协议的缘故,所以这里就会将单个接口服务按照不同的协议进行导出
 org.apache.dubbo.config.ServiceConfig#doExportUrls
 ↓
 // 将单个接口服务按照单个协议进行导出
 // 其实是 doExportUrls 方法中循环调用了 doExportUrlsFor1Protocol 方法
 org.apache.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol
  ↓
 // 将单个接口服务按照单协议导出到多个注册中心上
org.apache.dubbo.config.ServiceConfig#exportUrl

  ↓
  private void exportUrl(URL url, List<URL> registryURLs, RegisterTypeEnum registerType) {
    String scope = url.getParameter(SCOPE_KEY);
    // don't export when none is configured
    if (!SCOPE_NONE.equalsIgnoreCase(scope)) {

        // export to local if the config is not remote (export to remote only when config is remote)
        if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
        // 本地导出
            exportLocal(url);
        }

        // export to remote if the config is not local (export to local only when config is local)
        if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
            // export to extra protocol is used in remote export
            String extProtocol = url.getParameter(EXT_PROTOCOL, "");
            List<String> protocols = new ArrayList<>();

            if (StringUtils.isNotBlank(extProtocol)) {
                // export original url
                url = URLBuilder.from(url)
                        .addParameter(IS_PU_SERVER_KEY, Boolean.TRUE.toString())
                        .build();
            }
            
            //远程导出
            url = exportRemote(url, registryURLs, registerType);
            if (!isGeneric(generic) && !getScopeModel().isInternal()) {
                MetadataUtils.publishServiceDefinition(url, providerModel.getServiceModel(), getApplicationModel());
            }

            if (StringUtils.isNotBlank(extProtocol)) {
                String[] extProtocols = extProtocol.split(",", -1);
                protocols.addAll(Arrays.asList(extProtocols));
            }
            // export extra protocols
            for (String protocol : protocols) {
                if (StringUtils.isNotBlank(protocol)) {
                    URL localUrl = URLBuilder.from(url)
                            .setProtocol(protocol)
                            .addParameter(IS_EXTRA, Boolean.TRUE.toString())
                            .removeParameter(EXT_PROTOCOL)
                            .build();
                    localUrl = exportRemote(localUrl, registryURLs, registerType);
                    if (!isGeneric(generic) && !getScopeModel().isInternal()) {
                        MetadataUtils.publishServiceDefinition(
                                localUrl, providerModel.getServiceModel(), getApplicationModel());
                    }
                    this.urls.add(localUrl);
                }
            }
        }
    }
    this.urls.add(url);
}
 

导出核心逻辑

导出的核心逻辑

通过url 的 scope属性来控制本地导出和远程导出

1、scope属性什么都不配置,本地+远程都导出

2、scope 配置为local 只进行本地导出,不会进行远程导出

3、scope配置为remote ,只进行远程导出,不会进行本地导出

本地导出

//本地导出
org.apache.dubbo.config.ServiceConfig#exportLocal

String LOCAL_PROTOCOL = "injvm";
String LOCALHOST_VALUE = "127.0.0.1";
/**
 * always export injvm
 */
private void exportLocal(URL url) {
    URL local = URLBuilder.from(url)
            .setProtocol(LOCAL_PROTOCOL)
            .setHost(LOCALHOST_VALUE)
            .setPort(0)
            .build();
    //tri://192.168.0.54:8080/cn.itbox.uap.sys.setting.application.service.FeaturesService?anyhost=true&application=uap-sys-setting-application&background=false&bind.ip=192.168.0.54&bind.port=8080&check.serializable=false&delay=-1&deprecated=false&dubbo=2.0.2&dynamic=true&executor-management-mode=isolation&file-cache=true&generic=false&interface=cn.itbox.uap.sys.setting.application.service.FeaturesService&methods=getFeaturesContent&pid=37080&prefer.serialization=hessian2,fastjson2&qos.enable=true&register-mode=instance&release=3.3.0-beta.4&serialize.check.status=DISABLE&side=provider&timestamp=1722177737367&triple.enable.push=false&triple.enable.servlet=true&triple.header.table.size=4096&triple.initial.buffer.size=16384&triple.initial.window.size=8388608&triple.max.body.size=8388608&triple.max.chunk.size=8388608&triple.max.concurrent.streams=2147483647&triple.max.frame.size=8388608&triple.max.header.list.size=32768&triple.max.header.size=8192&triple.max.initial.line.length=4096&triple.max.response.body.size=8388608&warmup=30000
    //injvm://127.0.0.1/cn.itbox.uap.sys.setting.application.service.FeaturesService?anyhost=true&application=uap-sys-setting-application&background=false&bind.ip=192.168.0.54&bind.port=8080&check.serializable=false&delay=-1&deprecated=false&dubbo=2.0.2&dynamic=true&executor-management-mode=isolation&exporter.listener=injvm&file-cache=true&generic=false&interface=cn.itbox.uap.sys.setting.application.service.FeaturesService&methods=getFeaturesContent&pid=37080&prefer.serialization=hessian2,fastjson2&qos.enable=true&register-mode=instance&release=3.3.0-beta.4&serialize.check.status=DISABLE&side=provider&timestamp=1722177737367&triple.enable.push=false&triple.enable.servlet=true&triple.header.table.size=4096&triple.initial.buffer.size=16384&triple.initial.window.size=8388608&triple.max.body.size=8388608&triple.max.chunk.size=8388608&triple.max.concurrent.streams=2147483647&triple.max.frame.size=8388608&triple.max.header.list.size=32768&triple.max.header.size=8192&triple.max.initial.line.length=4096&triple.max.response.body.size=8388608&warmup=30000        
    local = local.setScopeModel(getScopeModel()).setServiceModel(providerModel);
    local = local.addParameter(EXPORTER_LISTENER_KEY, LOCAL_PROTOCOL);
    doExportUrl(local, false, RegisterTypeEnum.AUTO_REGISTER);
    logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry url : " + local);
}

 //org.apache.dubbo.config.ServiceConfig#doExportUrl
  // url 对应的内存值为:injvm://127.0.0.1/cn.itbox.uap.sys.setting.application.service.FeaturesService?anyhost=true&application=uap-sys-setting-application&background=false&bind.ip=192.168.0.54&bind.port=8080&check.serializable=false&delay=-1&deprecated=false&dubbo=2.0.2&dynamic=true&executor-management-mode=isolation&exporter.listener=injvm&file-cache=true&generic=false&interface=cn.itbox.uap.sys.setting.application.service.FeaturesService&methods=getFeaturesContent&pid=37080&prefer.serialization=hessian2,fastjson2&qos.enable=true&register-mode=instance&release=3.3.0-beta.4&serialize.check.status=DISABLE&side=provider&timestamp=1722177737367&triple.enable.push=false&triple.enable.servlet=true&triple.header.table.size=4096&triple.initial.buffer.size=16384&triple.initial.window.size=8388608&triple.max.body.size=8388608&triple.max.chunk.size=8388608&triple.max.concurrent.streams=2147483647&triple.max.frame.size=8388608&triple.max.header.list.size=32768&triple.max.header.size=8192&triple.max.initial.line.length=4096&triple.max.response.body.size=8388608&warmup=30000   
private void doExportUrl(URL url, boolean withMetaData, RegisterTypeEnum registerType) {
    if (!url.getParameter(REGISTER_KEY, true)) {
        registerType = RegisterTypeEnum.MANUAL_REGISTER;
    }
    if (registerType == RegisterTypeEnum.NEVER_REGISTER
            || registerType == RegisterTypeEnum.MANUAL_REGISTER
            || registerType == RegisterTypeEnum.AUTO_REGISTER_BY_DEPLOYER) {
        url = url.addParameter(REGISTER_KEY, false);
    }

// proxyFactory 为代理自适应扩展点
// 获取经过 AbstractProxyInvoker 包装过的 Wrapper 动态代理类
// 即 invoker 是 AbstractProxyInvoker 类型的,
// 然后 AbstractProxyInvoker 中持有了 Wrapper 动态代理类
    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
    if (withMetaData) {
        invoker = new DelegateProviderMetaDataInvoker(invoker, this);
    }
    // protocolSPI 为协议自定义扩展点
    // 将 invoker 按照指定的协议进行导出
    Exporter<?> exporter = protocolSPI.export(invoker);
    exporters
            .computeIfAbsent(registerType, k -> new CopyOnWriteArrayList<>())
            .add(exporter);
}

doExportUrl 这个方法,只有两行简短的代码调用,一行是获取代理对象

(proxyFactory.getInvoker) ,一行是协议导出代理对此(protocolSPI.export)

 
///
 // org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol#export
 // InjvmProtocol 类中的 export 的导出核心逻辑,
 // 其实就是将入参的 invoker 对象封装到 exporterMap 中而已
 ///
 
 @Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    return new InjvmExporter<>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}


/**
 ///
 // org.apache.dubbo.rpc.protocol.injvm.InjvmExporter#InjvmExporter
 // injvm 协议对应的导出对象
 ///
 * InjvmExporter
 */
public class InjvmExporter<T> extends AbstractExporter<T> {
    private final String key;
    private final Map<String, Exporter<?>> exporterMap;
    InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
        super(invoker);
        this.key = key;
        this.exporterMap = exporterMap;
        // 存储到一个 exporterMap 集合中
        exporterMap.put(key, this);
    }
}

核心原理就是,将生成的Invoker 代理类缓存到了 InjvmProtocol 中的 exporterMap 成员变量中。

//JavassistProxyFactory#getInvoker 将入参对象包装为一个代理对象返回
@Override
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    try {
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        //生成Wrapper代理类
        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 {
                    //上层若对Invoker 调用的话
                    //最终会通过wrapper内部的if ....else 找到代理类进行强转调用
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    } catch (Throwable fromJavassist) {
        // try fall back to JDK proxy factory
        //降级为JDK 生成代理
        try {
            Invoker<T> invoker = jdkProxyFactory.getInvoker(proxy, type, url);
            logger.error(
                    PROXY_FAILED,
                    "",
                    "",
                    "Failed to generate invoker by Javassist failed. Fallback to use JDK proxy success. "
                            + "Interfaces: " + type,
                    fromJavassist);
            // log out error
            return invoker;
        } catch (Throwable fromJdk) {
            logger.error(
                    PROXY_FAILED,
                    "",
                    "",
                    "Failed to generate invoker by Javassist failed. Fallback to use JDK proxy is also failed. "
                            + "Interfaces: " + type + " Javassist Error.",
                    fromJavassist);
            logger.error(
                    PROXY_FAILED,
                    "",
                    "",
                    "Failed to generate invoker by Javassist failed. Fallback to use JDK proxy is also failed. "
                            + "Interfaces: " + type + " JDK Error.",
                    fromJdk);
            throw fromJavassist;
        }
    }
}

proxyFactory.getInvoker 方法执行流程

1、先执行ProxyFactory接口的自适应扩展代理类(ProxyFactory$Adaptive)中的getInvoker方法

2、执行包装类StubProxyFacroryWrapper的 getInvoker 方法

3、执行JavassistProxyFactory 实现类的getInvoker 方法

远程导出

 

//org.apache.dubbo.config.ServiceConfig#exportRemote
//远程导出逻辑
private URL exportRemote(URL url, List<URL> registryURLs, RegisterTypeEnum registerType) {
  //若注册中心地址集合不为空,则进行for 循环处理
    if (CollectionUtils.isNotEmpty(registryURLs) && registerType != RegisterTypeEnum.NEVER_REGISTER) {
    //开始循环每一个注册中心地址
        for (URL registryURL : registryURLs) {
        
        //String SERVICE_REGISTRY_PROTOCOL = "service-discovery-registry";
        //service-discovery-register为了将服务的接口相关信息存储在内存中
            if (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())) {
            //String SERVICE_NAME_MAPPING_KEY = "service-name-mapping";
                url = url.addParameterIfAbsent(SERVICE_NAME_MAPPING_KEY, "true");
            }

            // if protocol is only injvm ,not register
            //如果发现注册中心地址写着 injvm协议的话,则跳过不做任何导出处理
            //String LOCAL_PROTOCOL = "injvm";
            if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
                continue;
            }

            //dynamic=true 就会在注册中心创建临时节点
            //dynamic=false 就会在注册中心创建永久节点,若服务器宕机的话,需要人工手动删除
            //String DYNAMIC_KEY = "dynamic
            url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
            
            //监控中心地址,如果配置了的话,服务调用信息就会上报
            URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
            if (monitorUrl != null) {
                url = url.putAttribute(MONITOR_KEY, monitorUrl);
            }

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




            //有注册中心的逻辑,远程导出的核心逻辑
            //url: //tri://192.168.0.54:8080/cn.itbox.uap.sys.setting.application.service.FeaturesService?anyhost=true&application=uap-sys-setting-application&background=false&bind.ip=192.168.0.54&bind.port=8080&check.serializable=false&delay=-1&deprecated=false&dubbo=2.0.2&dynamic=true&executor-management-mode=isolation&file-cache=true&generic=false&interface=cn.itbox.uap.sys.setting.application.service.FeaturesService&methods=getFeaturesContent&pid=37080&prefer.serialization=hessian2,fastjson2&qos.enable=true&register-mode=instance&release=3.3.0-beta.4&serialize.check.status=DISABLE&side=provider&timestamp=1722177737367&triple.enable.push=false&triple.enable.servlet=true&triple.header.table.size=4096&triple.initial.buffer.size=16384&triple.initial.window.size=8388608&triple.max.body.size=8388608&triple.max.chunk.size=8388608&triple.max.concurrent.streams=2147483647&triple.max.frame.size=8388608&triple.max.header.list.size=32768&triple.max.header.size=8192&triple.max.initial.line.length=4096&triple.max.response.body.size=8388608&warmup=30000
            //service-discovery-registry://mse-a022adf2-nacos-ans.mse.aliyuncs.com:8848/org.apache.dubbo.registry.RegistryService?REGISTRY_CLUSTER=default:itbox-nacos&access-key=LTAI5tRF5VG1KR6zvFGP9rzq&accessKey=LTAI5tRF5VG1KR6zvFGP9rzq&application=uap-alarm-application&check=false&check.serializable=false&dubbo=2.0.2&executor-management-mode=isolation&file-cache=true&mse.project.name=j1h07oev3e@f616b8f0b37130c&namespace=itbox-nacos&opensergo.io/canary-huyanbing=huyanbing&pid=11665&qos.enable=true&register=true&register-mode=instance&registry=nacos&release=3.3.0-beta.4&secret-key=KsSjPyZJjFKdHEsjwNbyFiOEbn7v6q&secretKey=KsSjPyZJjFKdHEsjwNbyFiOEbn7v6q&serialize.check.status=DISABLE&timestamp=1724196058242
            doExportUrl(registryURL.putAttribute(EXPORT_KEY, url), true, registerType);
        }

    } else {
         //无注册中心 导出逻辑
        doExportUrl(url, true, registerType);
    }

    return url;
}

doExportUrl

 
org.apache.dubbo.config.ServiceConfig#doExportUrl
//通用方法,根据给定的入参url 进行服务导出
private void doExportUrl(URL url, boolean withMetaData, RegisterTypeEnum registerType) {
//如果 register=true  
    if (!url.getParameter(REGISTER_KEY, true)) {
    //默认为手动注册
        registerType = RegisterTypeEnum.MANUAL_REGISTER;
    }
    //如果为 registerType 为 绝不注册
    if (registerType == RegisterTypeEnum.NEVER_REGISTER
            || registerType == RegisterTypeEnum.MANUAL_REGISTER
            || registerType == RegisterTypeEnum.AUTO_REGISTER_BY_DEPLOYER) {
        url = url.addParameter(REGISTER_KEY, false);
    }

//injvm://127.0.0.1/cn.itbox.uap.sys.setting.application.service.FeaturesService?anyhost=true&application=uap-sys-setting-application&background=false&bind.ip=192.168.0.54&bind.port=8080&check.serializable=false&delay=-1&deprecated=false&dubbo=2.0.2&dynamic=true&executor-management-mode=isolation&exporter.listener=injvm&file-cache=true&generic=false&interface=cn.itbox.uap.sys.setting.application.service.FeaturesService&methods=getFeaturesContent&pid=37080&prefer.serialization=hessian2,fastjson2&qos.enable=true&register-mode=instance&release=3.3.0-beta.4&serialize.check.status=DISABLE&side=provider&timestamp=1722177737367&triple.enable.push=false&triple.enable.servlet=true&triple.header.table.size=4096&triple.initial.buffer.size=16384&triple.initial.window.size=8388608&triple.max.body.size=8388608&triple.max.chunk.size=8388608&triple.max.concurrent.streams=2147483647&triple.max.frame.size=8388608&triple.max.header.list.size=32768&triple.max.header.size=8192&triple.max.initial.line.length=4096&triple.max.response.body.size=8388608&warmup=30000        
   //
//proxyFactory 为代理自适应扩展点
//获取经过AbstractProxyInvoker包装过的Wrapper动态代理类
//即invokder 是AbstractProxyInvoker类型的
//然后AbstractProxyInvoker中持有了Wrapper动态代理类
    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
    if (withMetaData) {
        invoker = new DelegateProviderMetaDataInvoker(invoker, this);
    }
    Exporter<?> exporter = protocolSPI.export(invoker);
    exporters
            .computeIfAbsent(registerType, k -> new CopyOnWriteArrayList<>())
            .add(exporter);
}

判断是否有registryURL 导出的逻辑不一样,有registryURL ,则用registryURL 传入到doExportUrl 方法,无registryURL ,则用接口服务的地址传入到doExeportURL

使用 registryURL 传入到 doExportUrl 方法,会将接口服务的地址内容

以 key = export 属性形式,放在 registryURL 中。

本地导出和远程导出,都调用同一个 doExportUrl 方法,也就意味着导出的主流程代

码还是之前的两行代码,只不过远程导出时会走其他的实现类而已。

Protocol$Adaptive->ProtocolSerializationWrapper->ProtocolFilterWrapper->ProtocolListenerWrapper->QosProtocolWrapper->InterfaceCompatibleRegistryProtocol

InterfaceCompatibleRegistryProtocol

 


//org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol
//RegistryProtocol 注册协议
//兼容接口级注册协议的实现类,继承了RegistryProtocol  
//这里灭有export 方法,所以可以推测export方法在其父类中
public class InterfaceCompatibleRegistryProtocol extends RegistryProtocol {

    @Override
    protected URL getRegistryUrl(Invoker<?> originInvoker) {
        URL registryUrl = originInvoker.getUrl();
        if (REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {
            String protocol = registryUrl.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY);
            registryUrl = registryUrl.setProtocol(protocol).removeParameter(REGISTRY_KEY);
        }
        return registryUrl;
    }

    @Override
    protected URL getRegistryUrl(URL url) {
        return URLBuilder.from(url)
                .setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
                .removeParameter(REGISTRY_KEY)
                .build();
    }

    @Override
    public <T> ClusterInvoker<T> getInvoker(Cluster cluster, Registry registry, Class<T> type, URL url) {
        DynamicDirectory<T> directory = new RegistryDirectory<>(type, url);
        return doCreateInvoker(directory, cluster, registry, type);
    }

    @Override
    public <T> ClusterInvoker<T> getServiceDiscoveryInvoker(
            Cluster cluster, Registry registry, Class<T> type, URL url) {
        registry = getRegistry(super.getRegistryUrl(url));
        DynamicDirectory<T> directory = new ServiceDiscoveryRegistryDirectory<>(type, url);
        return doCreateInvoker(directory, cluster, registry, type);
    }

    @Override
    protected <T> ClusterInvoker<T> getMigrationInvoker(
            RegistryProtocol registryProtocol,
            Cluster cluster,
            Registry registry,
            Class<T> type,
            URL url,
            URL consumerUrl) {
        //        ClusterInvoker<T> invoker = getInvoker(cluster, registry, type, url);
        return new MigrationInvoker<>(registryProtocol, cluster, registry, type, url, consumerUrl);
    }
}

export

//org.apache.dubbo.registry.integration.RegistryProtocol#export
//远程导出核心逻辑,开启Netty 端口+向注册中心写数据
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {

//service-discovery-registry://mse-a022adf2-nacos-ans.mse.aliyuncs.com:8848/org.apache.dubbo.registry.RegistryService
//从 originInvoker 取出 "url" 的属性值,结果取出了  service-discovery-registry 值
//然后将 service-discovery-registry 替换协议 "protocol" 属性的值就变成了 registryUrl
    URL registryUrl = getRegistryUrl(originInvoker);
    //tri://192.168.0.46:8081/cn.itbox.uap.alarm.application.service.AlarmHookService
    //tri://192.168.0.46:8081/cn.itbox.uap.alarm.application.service.AlarmHookService?__micro.service.app.id__=j1h07oev3e@f616b8f0b37130c&__micro.service.env__=[{"desc":"sys-label","priority":100,"tag":"huyanbing","type":"tag"}]&anyhost=true&application=uap-alarm-application&background=false&bind.ip=192.168.0.46&bind.port=8081&check.serializable=false&delay=-1&deprecated=false&dubbo=2.0.2&dynamic=true&executor-management-mode=isolation&file-cache=true&generic=false&interface=cn.itbox.uap.alarm.application.service.AlarmHookService&methods=sendArmsMsg,sendMsg,sendMsgStr&mse.project.name=j1h07oev3e@f616b8f0b37130c&opensergo.io/canary-huyanbing=huyanbing&pid=48708&prefer.serialization=jackson&qos.enable=true&register-mode=instance&release=3.3.0-beta.4&remote.timestamp=1724283093382&serialization=jackson&serialize.check.status=DISABLE&service-name-mapping=true&side=provider&timeout=3000&timestamp=1724283093382&triple.enable.push=false&triple.enable.servlet=true&triple.header.table.size=4096&triple.initial.buffer.size=16384&triple.initial.window.size=8388608&triple.max.body.size=8388608&triple.max.chunk.size=8388608&triple.max.concurrent.streams=2147483647&triple.max.frame.size=8388608&triple.max.header.list.size=32768&triple.max.header.size=8192&triple.max.initial.line.length=4096&triple.max.response.body.size=8388608&warmup=30000
    // url to export locally
    
    URL providerUrl = getProviderUrl(originInvoker);

    // 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.
    //tri://192.168.0.46:8081/cn.itbox.uap.alarm.application.service.AlarmHookService?__micro.service.app.id__=j1h07oev3e@f616b8f0b37130c&__micro.service.env__=[{"desc":"sys-label","priority":100,"tag":"huyanbing","type":"tag"}]&anyhost=true&application=uap-alarm-application&background=false&bind.ip=192.168.0.46&bind.port=8081&check.serializable=false&delay=-1&deprecated=false&dubbo=2.0.2&dynamic=true&executor-management-mode=isolation&file-cache=true&generic=false&interface=cn.itbox.uap.alarm.application.service.AlarmHookService&methods=sendArmsMsg,sendMsg,sendMsgStr&mse.project.name=j1h07oev3e@f616b8f0b37130c&opensergo.io/canary-huyanbing=huyanbing&pid=48708&prefer.serialization=jackson&qos.enable=true&register-mode=instance&release=3.3.0-beta.4&remote.timestamp=1724283093382&serialization=jackson&serialize.check.status=DISABLE&service-name-mapping=true&side=provider&timeout=3000&timestamp=1724283093382&triple.enable.push=false&triple.enable.servlet=true&triple.header.table.size=4096&triple.initial.buffer.size=16384&triple.initial.window.size=8388608&triple.max.body.size=8388608&triple.max.chunk.size=8388608&triple.max.concurrent.streams=2147483647&triple.max.frame.size=8388608&triple.max.header.list.size=32768&triple.max.header.size=8192&triple.max.initial.line.length=4096&triple.max.response.body.size=8388608&warmup=30000
    //tri:// 替换为 provider://
    final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
    
    final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
    Map<URL, Set<NotifyListener>> overrideListeners =
            getProviderConfigurationListener(overrideSubscribeUrl).getOverrideListeners();
    overrideListeners
            .computeIfAbsent(overrideSubscribeUrl, k -> new ConcurrentHashSet<>())
            .add(overrideSubscribeListener);

    providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
    // export invoker
    // “本地导出”,此本地导出并不是之前看到的“本地导出”
// 这里是注册中心协议实现类的本地导出,是需要本地开启20880端口的netty服务
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

    // url to registry
     根据 registryUrl 获取对应的注册器,这里获取的是对象从外层到内层依次是:
// ListenerRegistryWrapper -> NacosServiceDiscovery,最终拿到了 nacos 注册器
    final Registry registry = getRegistry(registryUrl);
    final URL registeredProviderUrl = customizeURL(providerUrl, registryUrl);

    // decide if we need to delay publish (provider itself and registry should both need to register)
    boolean register = providerUrl.getParameter(REGISTER_KEY, true) && registryUrl.getParameter(REGISTER_KEY, true);
    if (register) {
    // 向 nacos 进行写数据,将 registeredProviderUrl 写到注册中心服务中去
        register(registry, registeredProviderUrl);
    }

    // register stated url on provider model
    registerStatedUrl(registryUrl, registeredProviderUrl, register);

    exporter.setRegisterUrl(registeredProviderUrl);
    exporter.setSubscribeUrl(overrideSubscribeUrl);
    exporter.setNotifyListener(overrideSubscribeListener);
    exporter.setRegistered(register);

    ApplicationModel applicationModel = getApplicationModel(providerUrl.getScopeModel());
    if (applicationModel
            .modelEnvironment()
            .getConfiguration()
            .convert(Boolean.class, ENABLE_26X_CONFIGURATION_LISTEN, true)) {
        if (!registry.isServiceDiscovery()) {
            // Deprecated! Subscribe to override rules in 2.6.x or before.
            registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
        }
    }

    notifyExport(exporter);
    // Ensure that a new exporter instance is returned every time export
    return new DestroyableExporter<>(exporter);
}

tri:// 替换为 provider://

步骤:

1、进入RegistryProtocol#doLocalExport 注册协议实现类的本地导出方法

2、接着调用 protocol.export(InvokerDelegate),originInvoker)方法,实则调用链是否这样:

Protocol$Adaptive->ProtocolSerializationWrapper

->ProtocolFilterWrapper->QosProtocolWrapper->ProtocolListenerWrapper->DubboProtocol 最终调用了DubboProtocol 最终调用了 DubboProtocol#export 或者 TripleProtocol#export 方法

3、调用DubboProtocol#openServer开启协议端口服务

4、本地若没有开启端口服务的话,则调用DUbboProtocol#createServer方法创建一个端口服务

Dubbo协议

org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#openServer
private void openServer(URL url) {
    checkDestroyed();
    // find server.
    String key = url.getAddress();
    // client can export a service which only for server to invoke
    boolean isServer = url.getParameter(IS_SERVER_KEY, true);

    if (isServer) {
        ProtocolServer server = serverMap.get(key);
        if (server == null) {
            synchronized (this) {
                server = serverMap.get(key);
                if (server == null) {
                    serverMap.put(key, createServer(url));
                    return;
                }
            }
        }

        // server supports reset, use together with override
        server.reset(url);
    }
}

org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#createServer
private ProtocolServer createServer(URL url) {
   
    ExchangeServer server;
    try {
        server = Exchangers.bind(url, requestHandler);
    } 
    DubboProtocolServer protocolServer = new DubboProtocolServer(server);
    loadServerProperties(protocolServer);
    return protocolServer;
}

Triple协议:

 
org.apache.dubbo.rpc.protocol.tri.TripleProtocol#export

@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    boolean bindPort = true;
    //是否dubbo.protocol.triple.enable-servlet=true
if (SERVLET_ENABLED) {
    int port = url.getParameter(BIND_PORT_KEY, url.getPort());
    Integer serverPort = ServletExchanger.getServerPort();
    if (serverPort == null) {
        if (NetUtils.isPortInUsed(port)) {
            bindPort = false;
        }
    } else if (serverPort == port) {
        bindPort = false;
    }
    ServletExchanger.bind(url);
}
if (bindPort) {
    PortUnificationExchanger.bind(url, new DefaultPuHandler());
}

if (isHttp3Enabled(url)) {
    Http3Exchanger.bind(url);
}

optimizeSerialization(url);
return exporter;
}

5、调用server=Exchangers.bind(url,requestHandler) 绑定端口

6、进入绑定方法HeaderExchanger#bind->Transporters#bind(URL,ChannelHandler…)->

NettyTransporter#bind

 
org.apache.dubbo.remoting.exchange.Exchangers#bind(org.apache.dubbo.common.URL, org.apache.dubbo.remoting.exchange.ExchangeHandler)
org.apache.dubbo.remoting.exchange.support.header.HeaderExchanger#bind

org.apache.dubbo.remoting.Transporters#bind(org.apache.dubbo.common.URL, org.apache.dubbo.remoting.ChannelHandler...){
   return getTransporter(url).bind(url, handler);
}

org.apache.dubbo.remoting.transport.netty4.NettyTransporter#bind
@Override
public RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException {
    return new NettyServer(url, handler);
}

7、最终到了 Netty4.NettyServer#NettyServer的构造方法

 
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
    // you can customize name and type of client thread pool by THREAD_NAME_KEY and THREAD_POOL_KEY in
    // CommonConstants.
    // the handler will be wrapped: MultiMessageHandler->HeartbeatHandler->handler
    super(url, ChannelHandlers.wrap(handler, url));

    // read config before destroy
    serverShutdownTimeoutMills = ConfigurationUtils.getServerShutdownTimeout(getUrl().getOrDefaultModuleModel());
}

8、进入父类AbstractServer#AbstractServer构造方法

public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
    super(url, handler);

    try {
    //调用子类的NettyServer#doOpen
        doOpen();
    } catch (Throwable t) {

    }
}

9、调用子类的NettyServer#doOpen打开端口实现方法,最终通过Netty 的 channel=bootstrap.bind(getBindAddress())方法进行端口服务的绑定

triple协议调用的是:

PortUnificationExchanger.bind(url, new DefaultPuHandler());->

org.apache.dubbo.remoting.exchange.PortUnificationExchanger#bind->

org.apache.dubbo.remoting.transport.netty4.NettyPortUnificationTransporter#bind->

org.apache.dubbo.remoting.transport.netty4.NettyPortUnificationServer#NettyPortUnificationServer->

org.apache.dubbo.remoting.api.pu.AbstractPortUnificationServer#AbstractPortUnificationServer->

org.apache.dubbo.remoting.transport.AbstractServer#AbstractServer->

org.apache.dubbo.remoting.transport.netty.NettyPortUnificationServer#doOpen

 
protected void doOpen() {
    NettyHelper.setNettyLoggerFactory();
    ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory(EVENT_LOOP_BOSS_POOL_NAME, true));
    ExecutorService worker =
            Executors.newCachedThreadPool(new NamedThreadFactory(EVENT_LOOP_WORKER_POOL_NAME, true));
    ChannelFactory channelFactory = new NioServerSocketChannelFactory(
            boss, worker, getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
    bootstrap = new ServerBootstrap(channelFactory);

    final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
    dubboChannels = nettyHandler.getChannels();
    // https://issues.jboss.org/browse/NETTY-365
    // https://issues.jboss.org/browse/NETTY-379
    // final Timer timer = new HashedWheelTimer(new NamedThreadFactory("NettyIdleTimer", true));
    bootstrap.setOption("child.tcpNoDelay", true);
    bootstrap.setOption("backlog", getUrl().getPositiveParameter(BACKLOG_KEY, Constants.DEFAULT_BACKLOG));
    bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
        @Override
        public ChannelPipeline getPipeline() {
            NettyCodecAdapter adapter =
                    new NettyCodecAdapter(getCodec(), getUrl(), NettyPortUnificationServer.this);
            ChannelPipeline pipeline = Channels.pipeline();
            /*int idleTimeout = getIdleTimeout();
            if (idleTimeout > 10000) {
                pipeline.addLast("timer", new IdleStateHandler(timer, idleTimeout / 1000, 0, 0));
            }*/
            pipeline.addLast("decoder", adapter.getDecoder());
            pipeline.addLast("encoder", adapter.getEncoder());
            pipeline.addLast("handler", nettyHandler);
            return pipeline;
        }
    });
    // bind
    String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
    int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
    if (getUrl().getParameter(ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
        bindIp = ANYHOST_VALUE;
    }
    InetSocketAddress bindAddress = new InetSocketAddress(bindIp, bindPort);
    channel = bootstrap.bind(bindAddress);
}

通过源码,从 doLocalExport,追到 DubboProtocol 的 export 和 openServer 方法,紧

接着找到了 NettyTransporter 的 bind 方法,看见了 NettyServer 服务类,然后继续追到了其

父类 AbstractServer 调用了子类的 doOpen 方法,最后,在 NettyServer 的 doOpen 实现类

中,可以看到 Dubbo 框架采用 Netty 网络通信框架,进行了端口服务的绑定。

4、注册

1、进入RegistryProtocol#register注册入口;

2、进入org.apache.dubbo.registry.ListenerRegistryWrapper#register 包装器注册方法;

3、进入到org.apache.dubbo.registry.nacos.ServiceDiscoveryRegistry的父类FailbackRegistry注册方法

org.apache.dubbo.registry.support.FailbackRegistry#register;

4、父类FailbackRegistry#register 回调 ServiceDiscoveryRegistry#doRegister

tri:// 开头的才是发布服务协议

5、子类 NacosServiceDiscovery#doRegister 会调用父类的 AbstractServiceDiscovery#register

添加元数据

 
org.apache.dubbo.registry.client.AbstractServiceDiscovery#register(org.apache.dubbo.common.URL)
@Override
public void register(URL url) {
    metadataInfo.addService(url);
}

然后执行子类的

 
org.apache.dubbo.registry.nacos.NacosServiceDiscovery#doRegister
@Override
public void doRegister(ServiceInstance serviceInstance) {
    execute(namingService, service -> {
        Instance instance = toInstance(serviceInstance);
        service.registerInstance(instance.getServiceName(), group, instance);
    });
}

org.apache.dubbo.registry.nacos.NacosNamingServiceWrapper#registerInstance

public void registerInstance(String serviceName, String group, Instance instance) throws NacosException {
    String nacosServiceName = handleInnerSymbol(serviceName);
    InstancesInfo instancesInfo = ConcurrentHashMapUtils.computeIfAbsent(
            registerStatus, new InstanceId(nacosServiceName, group), id -> new InstancesInfo());

        if (instancesInfo.getInstances().isEmpty()) {
            // directly register
            NamingService namingService = nacosConnectionManager.getNamingService();
            accept(() -> namingService.registerInstance(nacosServiceName, group, instance));
            instancesInfo.getInstances().add(new InstanceInfo(instance, namingService));
            return;
        }

}

执行nacos 注册

com.alibaba.nacos.client.naming.NacosNamingService#registerInstance(java.lang.String, java.lang.String, com.alibaba.nacos.api.naming.pojo.Instance)
@Override
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
    NamingUtils.checkInstanceIsLegal(instance);
    clientProxy.registerService(serviceName, groupName, instance);
}

com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy#registerService
@Override
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
    NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance {}", namespaceId, serviceName,
            instance);
    redoService.cacheInstanceForRedo(serviceName, groupName, instance);
    doRegisterService(serviceName, groupName, instance);
}

5、总结:

配置流程:

通过扫描指定包路径下含有@DubboService注解的Bean定义,把扫描出来的Bean 定义属性,全部转移到新建的ServiceBean类型的Bean定义中,为后续导出做准备;

导出流程:

injvm 协议本地导出:

远程导出:

远程导出会使用协议端口通过Netty 绑定来提供端口服务

注册流程:

是远程导出的一个分支流程,会将提供方的服务接口信息,通过nacos-client 的 NamingGrpcClientProxy ,写到Nacos注册中心服务端去

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值