dubbo服务端启动源码分析(基于Dubbo 3)

本文基于Dubbo 3.0.2来进行源码研究。
在基于springboot的开发中,dubbot的spring-boot-starter中,DubboAutoConfiguration自动配置是一个关键类,这个类向spring容器注入了如下关键类:

  • ServiceAnnotationPostProcessor
  • DubboBootstrapApplicationListener

其中ServiceAnnotationPostProcessor主要是用来加载被DubbotService注解修饰的类,而DubboBootstrapApplicationListener则会监听spring的相关事件,其中:

public void onApplicationEvent(ApplicationEvent event) {
    
    
        if (isOriginalEventSource(event)) {
    
    
            if (event instanceof DubboAnnotationInitedEvent) {
    
    
                applicationContext.getBean(DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class);
                DubboBootstrap.getInstance().initialize();
            } else if (event instanceof ApplicationContextEvent) {
    
    
                this.onApplicationContextEvent((ApplicationContextEvent) event);
            }
        }
    }
    private void onApplicationContextEvent(ApplicationContextEvent event) {
    
    
        if (DubboBootstrapStartStopListenerSpringAdapter.applicationContext == null) {
    
    
            DubboBootstrapStartStopListenerSpringAdapter.applicationContext = event.getApplicationContext();
        }
        if (event instanceof ContextRefreshedEvent) {
    
    
            onContextRefreshedEvent((ContextRefreshedEvent) event);
        } else if (event instanceof ContextClosedEvent) {
    
    
            onContextClosedEvent((ContextClosedEvent) event);
        }
    }

    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
    
    
        if (dubboBootstrap.getTakeoverMode() == BootstrapTakeoverMode.SPRING) {
    
    
            dubboBootstrap.start();
        }
    }

    private void onContextClosedEvent(ContextClosedEvent event) {
    
    
        if (dubboBootstrap.getTakeoverMode() == BootstrapTakeoverMode.SPRING) {
    
    
            // will call dubboBootstrap.stop() through shutdown callback.
            DubboShutdownHook.getDubboShutdownHook().run();
        }
    }

其中,当spring容器就绪后会发布ContextRefreshedEvent,这时候,会调用DubboBootstrap来将服务发布出来,这是一个总的比较概括的步骤,接下来我们看细节。

首先看ServiceAnnotationPostProcessor,这个类的主要作用是将被如下注解修饰的类注入到了Spring容器中,同时生成对应的ServiceBean注入到Spring容器中,在ServiceBean中持有一个ref对应实际DubboService修饰的类的引用,而后面的服务暴露都是通过ServiceBean来实现,具体执行服务的方法,则是通过ServiceBean的ref对应具体的服务的方法去执行。

 private final static List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
            DubboService.class,
            Service.class,
            com.alibaba.dubbo.config.annotation.Service.class
    );

而在ServiceBean则是对服务接口的封装:

public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
        ApplicationContextAware, BeanNameAware, ApplicationEventPublisherAware {
    
    
   public void afterPropertiesSet() throws Exception {
    
    
        if (StringUtils.isEmpty(getPath())) {
    
    
            if (StringUtils.isNotEmpty(getInterface())) {
    
    
                setPath(getInterface());
            }
        }
        //register service bean and set bootstrap
        DubboBootstrap.getInstance().service(this);
    }        
    
}

这里会将当前ServiceBean注册到DubboBootstrap,然后上面的在ContextRefreshedEvent事件时,会进行

 dubboBootstrap.start();

进行服务的发布:

public DubboBootstrap start() {
    
    
        if (started.compareAndSet(false, true)) {
    
    
            startup.set(false);
            shutdown.set(false);
            awaited.set(false);

            initialize();
            if (logger.isInfoEnabled()) {
    
    
                logger.info(NAME + " is starting...");
            }
            // 1. export Dubbo Services
            exportServices();

            // If register consumer instance or has exported services
            if (isRegisterConsumerInstance() || hasExportedServices()) {
    
    
                // 2. export MetadataService
                exportMetadataService();
                // 3. Register the local ServiceInstance if required
                registerServiceInstance();
            }

            referServices();

            // wait async export / refer finish if needed
            awaitFinish();

            if (isExportBackground() || isReferBackground()) {
    
    
                new Thread(() -> {
    
    
                    while (!asyncExportFinish || !asyncReferFinish) {
    
    
                        try {
    
    
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
    
    
                            logger.error(NAME + " waiting async export / refer occurred and error.", e);
                        }
                    }

                    startup.set(true);
                    if (logger.isInfoEnabled()) {
    
    
                        logger.info(NAME + " is ready.");
                    }
                    onStart();
                }).start();
            } else {
    
    
                startup.set(true);
                if (logger.isInfoEnabled()) {
    
    
                    logger.info(NAME + " is ready.");
                }
                onStart();
            }

            if (logger.isInfoEnabled()) {
    
    
                logger.info(NAME + " has started.");
            }
        }
        return this;
    }
    private void exportServices() {
    
    
        for (ServiceConfigBase sc : configManager.getServices()) {
    
    
            // TODO, compatible with ServiceConfig.export()
            ServiceConfig<?> serviceConfig = (ServiceConfig<?>) sc;
            serviceConfig.setBootstrap(this);
            if (!serviceConfig.isRefreshed()) {
    
    
                serviceConfig.refresh();
            }

            if (sc.shouldExportAsync()) {
    
    
                ExecutorService executor = executorRepository.getServiceExportExecutor();
                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    
    
                    try {
    
    
                        if (!sc.isExported()) {
    
    
                            sc.export();
                            exportedServices.add(sc);
                        }
                    } catch (Throwable t) {
    
    
                        logger.error("export async catch error : " + t.getMessage(), t);
                    }
                }, executor);

                asyncExportingFutures.add(future);
            } else {
    
    
                if (!sc.isExported()) {
    
    
                    sc.export();
                    exportedServices.add(sc);
                }
            }
        }
    }

可以看到,这里的exportServices实际上最终会调用ServiceConfig.export:

// ServiceConfig.java
public synchronized void export() {
    
    
        if (this.shouldExport() && !this.exported) {
    
    
            this.init();

            // check bootstrap state
            if (!bootstrap.isInitialized()) {
    
    
                throw new IllegalStateException("DubboBootstrap is not initialized");
            }

            if (!this.isRefreshed()) {
    
    
                this.refresh();
            }

            if (!shouldExport()) {
    
    
                return;
            }

            if (shouldDelay()) {
    
    
                DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
            } else {
    
    
                doExport();
            }

            if (this.bootstrap.getTakeoverMode() == BootstrapTakeoverMode.AUTO) {
    
    
                this.bootstrap.start();
            }
        }
    }

可以看到在ServiceConfig中进行export的时候主要如下几步:

  • init
  • refresh
  • doExport
  1. 首先是init
public void init() {
    
    
        if (this.initialized.compareAndSet(false, true)) {
    
    
            if (this.bootstrap == null) {
    
    
                this.bootstrap = DubboBootstrap.getInstance();
                this.bootstrap.initialize();
            }
            this.bootstrap.service(this);
            ExtensionLoader<ServiceListener> extensionLoader = ExtensionLoader.getExtensionLoader(ServiceListener.class);
            this.serviceListeners.addAll(extensionLoader.getSupportedExtensionInstances());

            this.checkAndUpdateSubConfigs();
        }

        initServiceMetadata(provider);
        serviceMetadata.setServiceType(getInterfaceClass());
        serviceMetadata.setTarget(getRef());
        serviceMetadata.generateServiceKey();
    }

在init的时候,首先会加载ServiceListener,相当于是监听器,init的时候主要是加载相关的配置和转化
接下来是refresh:

public void refresh() {
    
    
        refreshed.set(true);
        try {
    
    
            // check and init before do refresh
            preProcessRefresh();

            Environment environment = ApplicationModel.getEnvironment();
            List<Map<String, String>> configurationMaps = environment.getConfigurationMaps();

            // Search props starts with PREFIX in order
            String preferredPrefix = null;
            for (String prefix : getPrefixes()) {
    
    
                if (ConfigurationUtils.hasSubProperties(configurationMaps, prefix)) {
    
    
                    preferredPrefix = prefix;
                    break;
                }
            }
            if (preferredPrefix == null) {
    
    
                preferredPrefix = getPrefixes().get(0);
            }
            // Extract sub props (which key was starts with preferredPrefix)
            Collection<Map<String, String>> instanceConfigMaps = environment.getConfigurationMaps(this, preferredPrefix);
            Map<String, String> subProperties = ConfigurationUtils.getSubProperties(instanceConfigMaps, preferredPrefix);
            InmemoryConfiguration subPropsConfiguration = new InmemoryConfiguration(subProperties);

            if (logger.isDebugEnabled()) {
    
    
                String idOrName = "";
                if (StringUtils.hasText(this.getId())) {
    
    
                    idOrName = "[id=" + this.getId() + "]";
                } else {
    
    
                    String name = ReflectUtils.getProperty(this, "getName");
                    if (StringUtils.hasText(name)) {
    
    
                        idOrName = "[name=" + name + "]";
                    }
                }
            }
            Method[] methods = getClass().getMethods();
            for (Method method : methods) {
    
    
                if (MethodUtils.isSetter(method)) {
    
    
                    String propertyName = extractPropertyName(method.getName());
                
                    String kebabPropertyName = StringUtils.convertToSplitName(propertyName, "-");

                    try {
    
    
                        String value = StringUtils.trim(subPropsConfiguration.getString(kebabPropertyName));

                        if (StringUtils.hasText(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value) &&
                                !isIgnoredAttribute(getClass(), propertyName)) {
    
    
                            value = environment.resolvePlaceholders(value);
                            method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
                        }
                    } catch (Exception e) {
    
    
            
                    }
                } else if (isParametersSetter(method)) {
    
    
                    String propertyName = extractPropertyName(method.getName());
                    String value = StringUtils.trim(subPropsConfiguration.getString(propertyName));
                    Map<String, String> parameterMap = null;
                    if (StringUtils.hasText(value)) {
    
    
                        parameterMap = StringUtils.parseParameters(value);
                    } else {
    
    
                        parameterMap = ConfigurationUtils.getSubProperties(subProperties, PARAMETERS);
                    }
                    invokeSetParameters(convert(parameterMap, ""));
                }
            }

            processExtraRefresh(preferredPrefix, subPropsConfiguration);

        } catch (Exception e) {
    
    
            throw new IllegalStateException("Failed to override field value of config bean: "+this, e);
        }
        postProcessRefresh();
    }

refresh主要是把配置读取并设置到当前ServiceConfig的相关属性赋值到对应的配置上。

最终服务暴露发布是在doExport中:

protected synchronized void doExport() {
    
    
        if (unexported) {
    
    
            throw new IllegalStateException("The service " + interfaceClass.getName() + " has already unexported!");
        }
        if (exported) {
    
    
            return;
        }

        if (StringUtils.isEmpty(path)) {
    
    
            path = interfaceName;
        }
        doExportUrls();
        exported();
    }
    private void doExportUrls() {
    
    
        ServiceRepository repository = ApplicationModel.getServiceRepository();
        ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
        repository.registerProvider(
                getUniqueServiceName(),
                ref,
                serviceDescriptor,
                this,
                serviceMetadata
        );

        List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);

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

这里首先会获取到注册中心大地址,这里可以看到,注册中心的地址返回的是一个List,证明Dubbo可以进行多注册中心注册,然后会将服务注册到注册中心去,注册完之后进行实际的服务发布

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
    
    
        Map<String, String> map = buildAttributes(protocolConfig);
        serviceMetadata.getAttachments().putAll(map);
        URL url = buildUrl(protocolConfig, registryURLs, map);
        exportUrl(url, registryURLs);
    }

这里首先拼接生成URL,在Dubbo中URL是一个很重要的概念,相关的参数、服务信息都放在URL中,ProtocolConfig就是我们在文件中配置protocol的相关信息配置,这里看到,一个服务可以通过多个协议暴露:

private URL buildUrl(ProtocolConfig protocolConfig, List<URL> registryURLs, Map<String, String> params) {
    
    
        String name = protocolConfig.getName();
        if (StringUtils.isEmpty(name)) {
    
    
            name = DUBBO;
        }

        // export service
        String host = findConfigedHosts(protocolConfig, registryURLs, params);
        Integer port = findConfigedPorts(protocolConfig, name, params);
        URL url = new ServiceConfigURL(name, null, null, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), params);

        // You can customize Configurator to append extra parameters
        if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .hasExtension(url.getProtocol())) {
    
    
            url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
        }

        return url;
    }
    private Map<String, String> buildAttributes(ProtocolConfig protocolConfig) {
    
    

        Map<String, String> map = new HashMap<String, String>();
        map.put(SIDE_KEY, PROVIDER_SIDE);

        // append params with basic configs,
        ServiceConfig.appendRuntimeParameters(map);
        AbstractConfig.appendParameters(map, getMetrics());
        AbstractConfig.appendParameters(map, getApplication());
        AbstractConfig.appendParameters(map, getModule());
        // remove 'default.' prefix for configs from ProviderConfig
        // appendParameters(map, provider, Constants.DEFAULT_KEY);
        AbstractConfig.appendParameters(map, provider);
        AbstractConfig.appendParameters(map, protocolConfig);
        AbstractConfig.appendParameters(map, this);
        MetadataReportConfig metadataReportConfig = getMetadataReportConfig();
        if (metadataReportConfig != null && metadataReportConfig.isValid()) {
    
    
            map.putIfAbsent(METADATA_KEY, REMOTE_METADATA_STORAGE_TYPE);
        }

        // append params with method configs,
        if (CollectionUtils.isNotEmpty(getMethods())) {
    
    
            getMethods().forEach(method -> appendParametersWithMethod(method, map));
        }

        if (ProtocolUtils.isGeneric(generic)) {
    
    
            map.put(GENERIC_KEY, generic);
            map.put(METHODS_KEY, ANY_VALUE);
        } else {
    
    
            String revision = Version.getVersion(interfaceClass, version);
            if (revision != null && revision.length() > 0) {
    
    
                map.put(REVISION_KEY, revision);
            }

            String[] methods = methods(interfaceClass);
            if (methods.length == 0) {
    
    
                logger.warn("No method found in service interface " + interfaceClass.getName());
                map.put(METHODS_KEY, ANY_VALUE);
            } else {
    
    
                map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
            }
        }

        /**
         * Here the token value configured by the provider is used to assign the value to ServiceConfig#token
         */
        if (ConfigUtils.isEmpty(token) && provider != null) {
    
    
            token = provider.getToken();
        }

        if (!ConfigUtils.isEmpty(token)) {
    
    
            if (ConfigUtils.isDefault(token)) {
    
    
                map.put(TOKEN_KEY, UUID.randomUUID().toString());
            } else {
    
    
                map.put(TOKEN_KEY, token);
            }
        }

        return map;
    }

这里的生成的url,实际上是ServiceConfigURL:

public ServiceConfigURL(String protocol,
               String username,
               String password,
               String host,
               int port,
               String path,
               Map<String, String> parameters) {
    
    
        this(new PathURLAddress(protocol, username, password, path, host, port), URLParam.parse(parameters), null);
    }

    public ServiceConfigURL(String protocol,
                            String username,
                            String password,
                            String host,
                            int port,
                            String path,
                            Map<String, String> parameters,
                            Map<String, Object> attributes) {
    
    
        this(new PathURLAddress(protocol, username, password, path, host, port), URLParam.parse(parameters), attributes);
    }

    protected <T extends URL> T newURL(URLAddress urlAddress, URLParam urlParam) {
    
    
        return (T) new ServiceConfigURL(urlAddress, urlParam, attributes);
    }

最终拼接是在:

// AbstractConfigurator.java
public URL configure(URL url) {
    
    
        // If override url is not enabled or is invalid, just return.
        if (!configuratorUrl.getParameter(ENABLED_KEY, true) || configuratorUrl.getHost() == null || url == null || url.getHost() == null) {
    
    
            return url;
        }
        String apiVersion = configuratorUrl.getParameter(CONFIG_VERSION_KEY);
        if (StringUtils.isNotEmpty(apiVersion)) {
    
    
            String currentSide = url.getSide();
            String configuratorSide = configuratorUrl.getSide();
            if (currentSide.equals(configuratorSide) && CONSUMER.equals(configuratorSide) && 0 == configuratorUrl.getPort()) {
    
    
                url = configureIfMatch(NetUtils.getLocalHost(), url);
            } else if (currentSide.equals(configuratorSide) && PROVIDER.equals(configuratorSide) &&
                    url.getPort() == configuratorUrl.getPort()) {
    
    
                url = configureIfMatch(url.getHost(), url);
            }
        }
        else {
    
    
            url = configureDeprecated(url);
        }
        return url;
    }
    private URL configureIfMatch(String host, URL url) {
    
    
        if (ANYHOST_VALUE.equals(configuratorUrl.getHost()) || host.equals(configuratorUrl.getHost())) {
    
    
            // TODO, to support wildcards
            String providers = configuratorUrl.getParameter(OVERRIDE_PROVIDERS_KEY);
            if (StringUtils.isEmpty(providers) || providers.contains(url.getAddress()) || providers.contains(ANYHOST_VALUE)) {
    
    
                String configApplication = configuratorUrl.getApplication(configuratorUrl.getUsername());
                String currentApplication = url.getApplication(url.getUsername());
                if (configApplication == null || ANY_VALUE.equals(configApplication)
                        || configApplication.equals(currentApplication)) {
    
    
                    Set<String> conditionKeys = new HashSet<String>();
                    conditionKeys.add(CATEGORY_KEY);
                    conditionKeys.add(Constants.CHECK_KEY);
                    conditionKeys.add(DYNAMIC_KEY);
                    conditionKeys.add(ENABLED_KEY);
                    conditionKeys.add(GROUP_KEY);
                    conditionKeys.add(VERSION_KEY);
                    conditionKeys.add(APPLICATION_KEY);
                    conditionKeys.add(SIDE_KEY);
                    conditionKeys.add(CONFIG_VERSION_KEY);
                    conditionKeys.add(COMPATIBLE_CONFIG_KEY);
                    conditionKeys.add(INTERFACES);
                    for (Map.Entry<String, String> entry : configuratorUrl.getParameters().entrySet()) {
    
    
                        String key = entry.getKey();
                        String value = entry.getValue();
                        boolean startWithTilde = startWithTilde(key);
                        if (startWithTilde || APPLICATION_KEY.equals(key) || SIDE_KEY.equals(key)) {
    
    
                            if (startWithTilde) {
    
    
                                conditionKeys.add(key);
                            }
                            if (value != null && !ANY_VALUE.equals(value)
                                    && !value.equals(url.getParameter(startWithTilde ? key.substring(1) : key))) {
    
    
                                return url;
                            }
                        }
                    }
                    return doConfigure(url, configuratorUrl.removeParameters(conditionKeys));
                }
            }
        }
        return url;
    }

可以看到,这里会判断是provider端还是consumer端,分别进行URL的组装,说白了就是根据你的配置来进行封装,有些没有的,就默认了。
然后就是根据拼接封装的URL来进行服务的发布:

private void exportUrl(URL url, List<URL> registryURLs) {
    
    
        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)) {
    
    
                url = exportRemote(url, registryURLs);
                MetadataUtils.publishServiceDefinition(url);
            }

        }
        this.urls.add(url);
    }
    private URL exportRemote(URL url, List<URL> registryURLs) {
    
    
        if (CollectionUtils.isNotEmpty(registryURLs)) {
    
    
            for (URL registryURL : registryURLs) {
    
    
                if (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())) {
    
    
                    url = url.addParameterIfAbsent(SERVICE_NAME_MAPPING_KEY, "true");
                }

                //if protocol is only injvm ,not register
                if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
    
    
                    continue;
                }

                url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
                URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
                if (monitorUrl != null) {
    
    
                    url = url.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);
                }

                if (logger.isInfoEnabled()) {
    
    
                    if (url.getParameter(REGISTER_KEY, true)) {
    
    
                        logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url.getServiceKey() + " to registry " + registryURL.getAddress());
                    } else {
    
    
                        logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url.getServiceKey());
                    }
                }

                doExportUrl(registryURL.putAttribute(EXPORT_KEY, url), true);
            }

        } else {
    
    

            if (MetadataService.class.getName().equals(url.getServiceInterface())) {
    
    
                MetadataUtils.saveMetadataURL(url);
            }

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

            doExportUrl(url, true);
        }


        return url;
    }
    

如果是对外发布,则调用exportRemote,然后执行服务发布在这:

  private void doExportUrl(URL url, boolean withMetaData) {
    
    
        Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
        if (withMetaData) {
    
    
            invoker = new DelegateProviderMetaDataInvoker(invoker, this);
        }
        Exporter<?> exporter = PROTOCOL.export(invoker);
        exporters.add(exporter);
    }

可以看到,这里首先是根据URL获取 Invoker,然后通过PROTOCOL(dubbo中默认的是DubboProtocol)进行发布,可以看到这里将实际的服务ref的引用传递过去了,如果是通过jdk方式,则返回的:

// JdkProxyFactory.java
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    
    
        return new AbstractProxyInvoker<T>(proxy, type, url) {
    
    
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {
    
    
                Method method = proxy.getClass().getMethod(methodName, parameterTypes);
                return method.invoke(proxy, arguments);
            }
        };
    }

默认的是javassist:

// JavassistProxyFactory.java
 public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    
    
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        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 {
    
    
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

可以看到获取Invoker的时候,会将ServiceBean中持有的实际服务的ref引用传递下去,后续实际执行的时候都是通过调用ref来实现

PROTOCOL则是通过Dubbo自定义的SPI机制加载:

 private static final Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

默认情况下,Protocol是DubboProtocol,,但是需要注意的是,这里后面获取的是getAdaptiveExtension,获取到的是Protocol$Adaptive
这就是相当于是一个Wrapper,实际上进行export的时候是调用具体的Protocol去执行的:

public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
    
    
public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
    
    
if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}

但是在Dubbo的SPI机制中,获取指定的SPI的时候,也会去获取对应的wrapper,如果在定义的路径里面,一个类的构造函数传入的类型和自己的类型一致,那么Dubbo的SPI认为这就是一个Wrapper,会先构造一个Warpper出来,然后在吧实际的类型T实例注入进去。上述首先获取到的是 Protocol$Adaptive,然后实际执行方法的却还是rg.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName) 这里获取到的就是个被warapper封装的Protocol而在Dubbo中,目前有Wrapper的有这个几个:
在这里插入图片描述

Dubbo中会按照order进行排序,最终相当于是ProtocolListenerWrapper => ProtocolFilterWrapper => Protocol$Adaptive => Protocol这个方式封装了Protocol。

默认情况下最终还是调用了DubboProtocol进行服务的发布,我们看下其实现:


public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    
    
        URL url = invoker.getUrl();

        // export service.
        String key = serviceKey(url);
        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."));
                }

            }
        }

        openServer(url);
        optimizeSerialization(url);

        return exporter;
    }

进过一系列判断之后,首先进行的是openServer处理:

private void openServer(URL url) {
    
    
        // find server.
        String key = url.getAddress();
        //client can export a service which's 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));
                    }else {
    
    
                        server.reset(url);
                    }
                }
            } else {
    
    
                // server supports reset, use together with override
                server.reset(url);
            }
        }
    }

这里判断一个服务的server是否启动时根据host+ip为key进行判断的,如果这个key代表的server没有创建,那么进行创建:

private ProtocolServer createServer(URL url) {
    
    
        url = URLBuilder.from(url)
                // send readonly event when server closes, it's enabled by default
                .addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString())
                // enable heartbeat by default
                .addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT))
                .addParameter(CODEC_KEY, DubboCodec.NAME)
                .build();
        String str = url.getParameter(SERVER_KEY, DEFAULT_REMOTING_SERVER);

        if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
    
    
            throw new RpcException("Unsupported server type: " + str + ", url: " + url);
        }

        ExchangeServer server;
        try {
    
    
            server = Exchangers.bind(url, requestHandler);
        } catch (RemotingException e) {
    
    
            throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
        }

        str = url.getParameter(CLIENT_KEY);
        if (str != null && str.length() > 0) {
    
    
            Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
            if (!supportedTypes.contains(str)) {
    
    
                throw new RpcException("Unsupported client type: " + str);
            }
        }

        return new DubboProtocolServer(server);
    }

这里创建的server是一个ExchangeServer,在Exchangers通过SPI机制来加载实际的Exchanger

server = Exchangers.bind(url, requestHandler);
 public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    
    
        if (url == null) {
    
    
            throw new IllegalArgumentException("url == null");
        }
        if (handler == null) {
    
    
            throw new IllegalArgumentException("handler == null");
        }
        url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
        return getExchanger(url).bind(url, handler);
    }
     public static Exchanger getExchanger(URL url) {
    
    
        String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
        return getExchanger(type);
    }

这里要获取Exchanger,默认为HeaderExchanger,其bind方法为:

// HeaderExchanger.java
 public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    
    
        return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
    }

而这里在通过Transporters中通过SPI加载对应的Transporter

public static RemotingServer bind(URL url, ChannelHandler... handlers) throws RemotingException {
    
    
        if (url == null) {
    
    
            throw new IllegalArgumentException("url == null");
        }
        if (handlers == null || handlers.length == 0) {
    
    
            throw new IllegalArgumentException("handlers == null");
        }
        ChannelHandler handler;
        if (handlers.length == 1) {
    
    
            handler = handlers[0];
        } else {
    
    
            handler = new ChannelHandlerDispatcher(handlers);
        }
        return getTransporter().bind(url, handler);
    }
public static Transporter getTransporter() {
    
    
        return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
    }

而在dubbo中默认的TransporterNettyTransporter,其bind方法为:

  public RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException {
    
    
        return new NettyServer(url, handler);
    }

可以看到这里实际上返回的就是一个NettyServer:

// NettyServer.java
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
    
    
        // you can customize name and type of client thread pool by THREAD_NAME_KEY and THREADPOOL_KEY in CommonConstants.
        // the handler will be wrapped: MultiMessageHandler->HeartbeatHandler->handler
        super(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME), ChannelHandlers.wrap(handler, url));
    }
 // AbstractServer.java
 public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
    
    
        super(url, handler);
        localAddress = getUrl().toInetSocketAddress();

        String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
        int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
        if (url.getParameter(ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
    
    
            bindIp = ANYHOST_VALUE;
        }
        bindAddress = new InetSocketAddress(bindIp, bindPort);
        this.accepts = url.getParameter(ACCEPTS_KEY, DEFAULT_ACCEPTS);
        try {
    
    
            doOpen();
            if (logger.isInfoEnabled()) {
    
    
                logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
            }
        } catch (Throwable t) {
    
    
            throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName()
                    + " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
        }
        executor = executorRepository.createExecutorIfAbsent(url);
    }
  // NettyServer.java
  protected void doOpen() throws Throwable {
    
    
        bootstrap = new ServerBootstrap();

        bossGroup = NettyEventLoopFactory.eventLoopGroup(1, "NettyServerBoss");
        workerGroup = NettyEventLoopFactory.eventLoopGroup(
                getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
                "NettyServerWorker");

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

        boolean keepalive = getUrl().getParameter(KEEP_ALIVE_KEY, Boolean.FALSE);

        bootstrap.group(bossGroup, workerGroup)
                .channel(NettyEventLoopFactory.serverSocketChannelClass())
                .option(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
                .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
                .childOption(ChannelOption.SO_KEEPALIVE, keepalive)
                .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .childHandler(new ChannelInitializer<SocketChannel>() {
    
    
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
    
    
                        // FIXME: should we use getTimeout()?
                        int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
                        NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
                        if (getUrl().getParameter(SSL_ENABLED_KEY, false)) {
    
    
                            ch.pipeline().addLast("negotiation",
                                    SslHandlerInitializer.sslServerHandler(getUrl(), nettyServerHandler));
                        }
                        ch.pipeline()
                                .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();

    }     

这里就是我们常见的Netty启动流程,这里可以看到,我们是可以自己设置Netty的IO线程数量,如果没配置,默认是当前机器可用线程数和32取最小值.
另外我们需要重点关注的就是Dubbo中的NettyHandler,我们看下Dubbo中的Handler是怎么封装的,
Dubbo定义了一个ChannelHandler接口,类结构层次如下:

在这里插入图片描述

首先是在DubboProtocol中实现了一个内部实现类ExchangeHandlerAdapter:

private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
    
    

        @Override
        public CompletableFuture<Object> reply(ExchangeChannel channel, Object message) throws RemotingException {
    
    

            if (!(message instanceof Invocation)) {
    
    
                throw new RemotingException(channel, "Unsupported request: "
                        + (message == null ? null : (message.getClass().getName() + ": " + message))
                        + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
            }

            Invocation inv = (Invocation) message;
            Invoker<?> invoker = getInvoker(channel, inv);
            // need to consider backward-compatibility if it's a callback
            if (Boolean.TRUE.toString().equals(inv.getObjectAttachments().get(IS_CALLBACK_SERVICE_INVOKE))) {
    
    
                String methodsStr = invoker.getUrl().getParameters().get("methods");
                boolean hasMethod = false;
                if (methodsStr == null || !methodsStr.contains(",")) {
    
    
                    hasMethod = inv.getMethodName().equals(methodsStr);
                } else {
    
    
                    String[] methods = methodsStr.split(",");
                    for (String method : methods) {
    
    
                        if (inv.getMethodName().equals(method)) {
    
    
                            hasMethod = true;
                            break;
                        }
                    }
                }
                if (!hasMethod) {
    
    
                    logger.warn(new IllegalStateException("The methodName " + inv.getMethodName()
                            + " not found in callback service interface ,invoke will be ignored."
                            + " please update the api interface. url is:"
                            + invoker.getUrl()) + " ,invocation is :" + inv);
                    return null;
                }
            }
            RpcContext.getServiceContext().setRemoteAddress(channel.getRemoteAddress());
            Result result = invoker.invoke(inv);
            return result.thenApply(Function.identity());
        }

        @Override
        public void received(Channel channel, Object message) throws RemotingException {
    
    
            if (message instanceof Invocation) {
    
    
                reply((ExchangeChannel) channel, message);

            } else {
    
    
                super.received(channel, message);
            }
        }

        @Override
        public void connected(Channel channel) throws RemotingException {
    
    
            invoke(channel, ON_CONNECT_KEY);
        }

        @Override
        public void disconnected(Channel channel) throws RemotingException {
    
    
            if (logger.isDebugEnabled()) {
    
    
                logger.debug("disconnected from " + channel.getRemoteAddress() + ",url:" + channel.getUrl());
            }
            invoke(channel, ON_DISCONNECT_KEY);
        }

        private void invoke(Channel channel, String methodKey) {
    
    
            Invocation invocation = createInvocation(channel, channel.getUrl(), methodKey);
            if (invocation != null) {
    
    
                try {
    
    
                    received(channel, invocation);
                } catch (Throwable t) {
    
    
                    logger.warn("Failed to invoke event method " + invocation.getMethodName() + "(), cause: " + t.getMessage(), t);
                }
            }
        }
        private Invocation createInvocation(Channel channel, URL url, String methodKey) {
    
    
            String method = url.getParameter(methodKey);
            if (method == null || method.length() == 0) {
    
    
                return null;
            }

            RpcInvocation invocation = new RpcInvocation(method, url.getParameter(INTERFACE_KEY), "", new Class<?>[0], new Object[0]);
            invocation.setAttachment(PATH_KEY, url.getPath());
            invocation.setAttachment(GROUP_KEY, url.getGroup());
            invocation.setAttachment(INTERFACE_KEY, url.getParameter(INTERFACE_KEY));
            invocation.setAttachment(VERSION_KEY, url.getVersion());
            if (url.getParameter(STUB_EVENT_KEY, false)) {
    
    
                invocation.setAttachment(STUB_EVENT_KEY, Boolean.TRUE.toString());
            }

            return invocation;
        }
    };

我们在建立NettyServer的时候,会传入这个:

// DubboProtocol.java
ExchangeServer server;
        try {
    
    
            server = Exchangers.bind(url, requestHandler);
        } catch (RemotingException e) {
    
    
            throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
        }
// HeaderExchanger
 public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    
    
        return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
    }
// Transporters.java
public static RemotingServer bind(URL url, ChannelHandler... handlers) throws RemotingException {
    
    
        if (url == null) {
    
    
            throw new IllegalArgumentException("url == null");
        }
        if (handlers == null || handlers.length == 0) {
    
    
            throw new IllegalArgumentException("handlers == null");
        }
        ChannelHandler handler;
        if (handlers.length == 1) {
    
    
            handler = handlers[0];
        } else {
    
    
            handler = new ChannelHandlerDispatcher(handlers);
        }
        return getTransporter().bind(url, handler);
    }
// NettyTrasporter.java
public RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException {
    
    
        return new NettyServer(url, handler);
    }

public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
    
    
        super(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME), ChannelHandlers.wrap(handler, url));
    }
     return ChannelHandlers.getInstance().wrapInternal(handler, url);
    }
    return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)
                .getAdaptiveExtension().dispatch(handler, url)));
    }
// AllDispatcher.java
 public ChannelHandler dispatch(ChannelHandler handler, URL url) {
    
    
        return new AllChannelHandler(handler, url);
    }

可以看到在DubboProtocol中,我们开始只是生成了一个ExchangeHandlerAdapter的内部实现类,然后逐步往后传递:

  1. HeaderExchangeHandler封装ExchangeHandlerAdapter
  2. DecodeHandler封装HeaderExchangeHandler
  3. AllChannelHandler封装DecodeHandler
  4. HeartbeatHandler封装AllChannelHandler
  5. MultiMessageHandler封装HeartbeatHandler

总结下来的封装顺序 MultiMessageHandler -> HeartbeatHandler -> AllChannelHandler -> DecodeHandler -> DecodeHandler -> HeaderExchangeHandler -> ExchangeHandlerAdapter
我们以RECEIVED事件来看看整个处理流程,但是在介绍事件处理流程之前,我们首先看下这块,启动NettyServer的相关处理:
在这里插入图片描述
NettyServer(基于Netty4)的类结构如图,可以看到,其也实现ChannelHandler接口,而实际Handler实现,则是交由上面MultiMessageHandler去实现的,而在NettyServer启动的时候,会传入对应编解码器,其实就是两个ChannelHandler,Dubbo中的编解码器的实现是DubboCountCodec,其内部持有一个DubboCodec,进行实际的编解码工作,这里采用装饰模式来实现,这里会将对应的二级制流转换为对应的Request或者Response,如果想要看Dubbo的详细编解码实现可以查看这个类就行了。需要注意的是请求发出的Request在解码阶段Requestdata部分实际上只是一个DecodeableRpcInvocation,并没有进行实际数据的解析,这部分是放在了后面的DecodeHandler中。
Dubbo中实际处理相关业务的是NettyServerHandler:

// NettyServer.java
// 这里传入的this就是NettyServer自己
final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);

当数据流来到时:

// NettyServerHandler.java
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    
    
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
        handler.received(channel, msg);
    }

可以看到这里就是通过传入的Handler去实现,最终实际上是在MultiMessageHandler

// MultiMessageHandler.java
public void received(Channel channel, Object message) throws RemotingException {
    
    
        if (message instanceof MultiMessage) {
    
    
            MultiMessage list = (MultiMessage) message;
            for (Object obj : list) {
    
    
                try {
    
    
                    handler.received(channel, obj);
                } catch (ExecutionException e) {
    
    
                    logger.error("MultiMessageHandler received fail.", e);
                    handler.caught(channel, e);
                }
            }
        } else {
    
    
            handler.received(channel, message);
        }
    }

可以看到MultiMessageHandler功能就是讲复合消息拆开,而MultiMessageHandler这里的handler则是HeartbeatHandler

public void received(Channel channel, Object message) throws RemotingException {
    
    
        setReadTimestamp(channel);
        if (isHeartbeatRequest(message)) {
    
    
            Request req = (Request) message;
            if (req.isTwoWay()) {
    
    
                Response res = new Response(req.getId(), req.getVersion());
                res.setEvent(HEARTBEAT_EVENT);
                channel.send(res);
                if (logger.isInfoEnabled()) {
    
    
                    int heartbeat = channel.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);
                    if (logger.isDebugEnabled()) {
    
    
                        logger.debug("Received heartbeat from remote channel " + channel.getRemoteAddress()
                                + ", cause: The channel has no data-transmission exceeds a heartbeat period"
                                + (heartbeat > 0 ? ": " + heartbeat + "ms" : ""));
                    }
                }
            }
            return;
        }
        if (isHeartbeatResponse(message)) {
    
    
            if (logger.isDebugEnabled()) {
    
    
                logger.debug("Receive heartbeat response in thread " + Thread.currentThread().getName());
            }
            return;
        }
        handler.received(channel, message);
    }

可以看到HeartbeatHandler主要功能就是处理心跳相关信息,不是心跳信息则交给内部handler实现,HeartbeatHandler这里的handerl是AllChannelHandler

    public void received(Channel channel, Object message) throws RemotingException {
    
    
        ExecutorService executor = getPreferredExecutorService(message);
        try {
    
    
            executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
        } catch (Throwable t) {
    
    
            if(message instanceof Request && t instanceof RejectedExecutionException){
    
    
                sendFeedback(channel, (Request) message, t);
                return;
            }
            throw new ExecutionException(message, channel, getClass() + " error when process received event .", t);
        }
    }

可以看到在AllChannelHandler里面处理接收的时候,是通过线程池俩处理的,如果是处理Request则默认的是一个Fixed的ThreadPoolExecutor,其默认大小是200,并且这里,如果是消费者端,则只有一个ThreadPoolExecutor,但是如果是服务端,则绑定每个端口都有一个ThreadPoolExecutor。
而在ChannelEventRunnable中处理消息则是直接使用AllChannelHandler的handler:

// ChannelEventRunnable.java
public void run() {
    
    
        if (state == ChannelState.RECEIVED) {
    
    
            try {
    
    
                handler.received(channel, message);
            } catch (Exception e) {
    
    }
        } else {
    
    
            switch (state) {
    
    
            case CONNECTED:
                try {
    
    
                    handler.connected(channel);
                } catch (Exception e) {
    
    }
                break;
            case DISCONNECTED:
                try {
    
    
                    handler.disconnected(channel);
                } catch (Exception e) {
    
    }
                break;
            case SENT:
                try {
    
    
                    handler.sent(channel, message);
                } catch (Exception e) {
    
    }
                break;
            case CAUGHT:
                try {
    
    
                    handler.caught(channel, exception);
                } catch (Exception e) {
    
    }
                break;
            default:
                logger.warn("unknown state: " + state + ", message is " + message);
            }
        }

    }

AllChannelHandler的handler则是DecodeHandler:

// DecodeHandler.java
public void received(Channel channel, Object message) throws RemotingException {
    
    
        if (message instanceof Decodeable) {
    
    
            decode(message);
        }

        if (message instanceof Request) {
    
    
            decode(((Request) message).getData());
        }

        if (message instanceof Response) {
    
    
            decode(((Response) message).getResult());
        }

        handler.received(channel, message);
    }
private void decode(Object message) {
    
    
        if (message instanceof Decodeable) {
    
    
            try {
    
    
                ((Decodeable) message).decode();
            } // ~ end of catch
        } // ~ end of if
    } 

这里的工作就是进行decode,还记得上面说的,在编解码器中返回的是Request或者Response对象,但是databa部分并没有解析,在这里进行实际的解码,我们以请求为例,Request中的data部分实际上是DecodeableRpcInvocation,我们看其实现:

// DecodeableRpcInvocation.java
public void decode() throws Exception {
    
    
        if (!hasDecoded && channel != null && inputStream != null) {
    
    
            try {
    
    
                decode(channel, inputStream);
            } catch (Throwable e) {
    
    
                if (log.isWarnEnabled()) {
    
    
                    log.warn("Decode rpc invocation failed: " + e.getMessage(), e);
                }
                request.setBroken(true);
                request.setData(e);
            } finally {
    
    
                hasDecoded = true;
            }
        }
    }
public Object decode(Channel channel, InputStream input) throws IOException {
    
    
        ObjectInput in = CodecSupport.getSerialization(channel.getUrl(), serializationType)
                .deserialize(channel.getUrl(), input);
        this.put(SERIALIZATION_ID_KEY, serializationType);

        String dubboVersion = in.readUTF();
        request.setVersion(dubboVersion);
        setAttachment(DUBBO_VERSION_KEY, dubboVersion);

        String path = in.readUTF();
        setAttachment(PATH_KEY, path);
        String version = in.readUTF();
        setAttachment(VERSION_KEY, version);

        setMethodName(in.readUTF());

        String desc = in.readUTF();
        setParameterTypesDesc(desc);

        try {
    
    
            if (ConfigurationUtils.getSystemConfiguration().getBoolean(SERIALIZATION_SECURITY_CHECK_KEY, true)) {
    
    
                CodecSupport.checkSerialization(path, version, serializationType);
            }
            Object[] args = DubboCodec.EMPTY_OBJECT_ARRAY;
            Class<?>[] pts = DubboCodec.EMPTY_CLASS_ARRAY;
            if (desc.length() > 0) {
    
    
//                if (RpcUtils.isGenericCall(path, getMethodName()) || RpcUtils.isEcho(path, getMethodName())) {
    
    
//                    pts = ReflectUtils.desc2classArray(desc);
//                } else {
    
    
                ServiceRepository repository = ApplicationModel.getServiceRepository();
                ServiceDescriptor serviceDescriptor = repository.lookupService(path);
                if (serviceDescriptor != null) {
    
    
                    MethodDescriptor methodDescriptor = serviceDescriptor.getMethod(getMethodName(), desc);
                    if (methodDescriptor != null) {
    
    
                        pts = methodDescriptor.getParameterClasses();
                        this.setReturnTypes(methodDescriptor.getReturnTypes());
                    }
                }
                if (pts == DubboCodec.EMPTY_CLASS_ARRAY) {
    
    
                    if (!RpcUtils.isGenericCall(desc, getMethodName()) && !RpcUtils.isEcho(desc, getMethodName())) {
    
    
                        throw new IllegalArgumentException("Service not found:" + path + ", " + getMethodName());
                    }
                    pts = ReflectUtils.desc2classArray(desc);
                }
//                }

                args = new Object[pts.length];
                for (int i = 0; i < args.length; i++) {
    
    
                    try {
    
    
                        args[i] = in.readObject(pts[i]);
                    } catch (Exception e) {
    
    
                        if (log.isWarnEnabled()) {
    
    
                            log.warn("Decode argument failed: " + e.getMessage(), e);
                        }
                    }
                }
            }
            setParameterTypes(pts);

            Map<String, Object> map = in.readAttachments();
            if (map != null && map.size() > 0) {
    
    
                Map<String, Object> attachment = getObjectAttachments();
                if (attachment == null) {
    
    
                    attachment = new LinkedHashMap<>();
                }
                attachment.putAll(map);
                setObjectAttachments(attachment);
            }

            //decode argument ,may be callback
            for (int i = 0; i < args.length; i++) {
    
    
                args[i] = decodeInvocationArgument(channel, this, pts, i, args[i]);
            }

            setArguments(args);
            String targetServiceName = buildKey((String) getAttachment(PATH_KEY),
                    getAttachment(GROUP_KEY),
                    getAttachment(VERSION_KEY));
            setTargetServiceUniqueName(targetServiceName);
        } catch (ClassNotFoundException e) {
    
    
            throw new IOException(StringUtils.toString("Read invocation data failed.", e));
        } finally {
    
    
            if (in instanceof Cleanable) {
    
    
                ((Cleanable) in).cleanup();
            }
        }
        return this;
    }

可以看到,在这里会进行实际的解析,获取如下重要信息:-

  • 请求的服务路径
  • 请求的服务方法名称
  • 请求的服务的版本号
  • 请求服务方法参数信息
  • 请求服务方法参数入参

根据这些信息,找到实际的服务提供者,并设置方法参数。
处理完这些之后,接下来还是给DecodeHandler的内部handler去处理,这里对应就是HeaderExchangeHandler:

// HeaderExchangeHandler.java
public void received(Channel channel, Object message) throws RemotingException {
    
    
        final ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);
        if (message instanceof Request) {
    
    
            // handle request.
            Request request = (Request) message;
            if (request.isEvent()) {
    
    
                handlerEvent(channel, request);
            } else {
    
    
                if (request.isTwoWay()) {
    
    
                    handleRequest(exchangeChannel, request);
                } else {
    
    
                    handler.received(exchangeChannel, request.getData());
                }
            }
        } else if (message instanceof Response) {
    
    
            handleResponse(channel, (Response) message);
        } else if (message instanceof String) {
    
    
            if (isClientSide(channel)) {
    
    
               ....;
            } else {
    
    
                String echo = handler.telnet(channel, (String) message);
                if (echo != null && echo.length() > 0) {
    
    
                    channel.send(echo);
                }
            }
        } else {
    
    
            handler.received(exchangeChannel, message);
        }
    }
   void handleRequest(final ExchangeChannel channel, Request req) throws RemotingException {
    
    
        Response res = new Response(req.getId(), req.getVersion());
        if (req.isBroken()) {
    
    
            Object data = req.getData();

            String msg;
            if (data == null) {
    
    
                msg = null;
            } else if (data instanceof Throwable) {
    
    
                msg = StringUtils.toString((Throwable) data);
            } else {
    
    
                msg = data.toString();
            }
            res.setErrorMessage("Fail to decode request due to: " + msg);
            res.setStatus(Response.BAD_REQUEST);

            channel.send(res);
            return;
        }
        Object msg = req.getData();
        try {
    
    
            CompletionStage<Object> future = handler.reply(channel, msg);
            future.whenComplete((appResult, t) -> {
    
    
                try {
    
    
                    if (t == null) {
    
    
                        res.setStatus(Response.OK);
                        res.setResult(appResult);
                    } else {
    
    
                        res.setStatus(Response.SERVICE_ERROR);
                        res.setErrorMessage(StringUtils.toString(t));
                    }
                    channel.send(res);
                } catch (RemotingException e) {
    
    }
            });
        } catch (Throwable e) {
    
    
            res.setStatus(Response.SERVICE_ERROR);
            res.setErrorMessage(StringUtils.toString(e));
            channel.send(res);
        }
    }

可以看到,这里分方法有返回值和无返回值两种情况,如果无返回值,则调用的还是HeaderExchangeHandler内部的handler去处理,而如果有返回值,则调用的HeaderExchangeHandler内部的handler的reply方法处理。
好,这里的handler就是我们在开头说的DubboProtocol中的内部实现类ExchangeHandlerAdapter,我们在这里在贴一下源码:

// DubboProtocol.java
private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
    
    
        @Override
        public CompletableFuture<Object> reply(ExchangeChannel channel, Object message) throws RemotingException {
    
    

            if (!(message instanceof Invocation)) {
    
    
                throw new RemotingException(channel, "Unsupported request: "
                        + (message == null ? null : (message.getClass().getName() + ": " + message))
                        + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
            }

            Invocation inv = (Invocation) message;
            Invoker<?> invoker = getInvoker(channel, inv);
            // need to consider backward-compatibility if it's a callback
            if (Boolean.TRUE.toString().equals(inv.getObjectAttachments().get(IS_CALLBACK_SERVICE_INVOKE))) {
    
    
                String methodsStr = invoker.getUrl().getParameters().get("methods");
                boolean hasMethod = false;
                if (methodsStr == null || !methodsStr.contains(",")) {
    
    
                    hasMethod = inv.getMethodName().equals(methodsStr);
                } else {
    
    
                    String[] methods = methodsStr.split(",");
                    for (String method : methods) {
    
    
                        if (inv.getMethodName().equals(method)) {
    
    
                            hasMethod = true;
                            break;
                        }
                    }
                }
                if (!hasMethod) {
    
    
                    logger.warn(new IllegalStateException("The methodName " + inv.getMethodName()
                            + " not found in callback service interface ,invoke will be ignored."
                            + " please update the api interface. url is:"
                            + invoker.getUrl()) + " ,invocation is :" + inv);
                    return null;
                }
            }
            RpcContext.getServiceContext().setRemoteAddress(channel.getRemoteAddress());
            Result result = invoker.invoke(inv);
            return result.thenApply(Function.identity());
        }

        @Override
        public void received(Channel channel, Object message) throws RemotingException {
    
    
            if (message instanceof Invocation) {
    
    
                reply((ExchangeChannel) channel, message);

            } else {
    
    
                super.received(channel, message);
            }
        }

        @Override
        public void connected(Channel channel) throws RemotingException {
    
    
            invoke(channel, ON_CONNECT_KEY);
        }

        @Override
        public void disconnected(Channel channel) throws RemotingException {
    
    
            if (logger.isDebugEnabled()) {
    
    
                logger.debug("disconnected from " + channel.getRemoteAddress() + ",url:" + channel.getUrl());
            }
            invoke(channel, ON_DISCONNECT_KEY);
        }

        private void invoke(Channel channel, String methodKey) {
    
    
            Invocation invocation = createInvocation(channel, channel.getUrl(), methodKey);
            if (invocation != null) {
    
    
                try {
    
    
                    received(channel, invocation);
                } catch (Throwable t) {
    
    
                    logger.warn("Failed to invoke event method " + invocation.getMethodName() + "(), cause: " + t.getMessage(), t);
                }
            }
        }
        private Invocation createInvocation(Channel channel, URL url, String methodKey) {
    
    
            String method = url.getParameter(methodKey);
            if (method == null || method.length() == 0) {
    
    
                return null;
            }

            RpcInvocation invocation = new RpcInvocation(method, url.getParameter(INTERFACE_KEY), "", new Class<?>[0], new Object[0]);
            invocation.setAttachment(PATH_KEY, url.getPath());
            invocation.setAttachment(GROUP_KEY, url.getGroup());
            invocation.setAttachment(INTERFACE_KEY, url.getParameter(INTERFACE_KEY));
            invocation.setAttachment(VERSION_KEY, url.getVersion());
            if (url.getParameter(STUB_EVENT_KEY, false)) {
    
    
                invocation.setAttachment(STUB_EVENT_KEY, Boolean.TRUE.toString());
            }

            return invocation;
        }
    };

可以看到,这里的received最终都是调用了reply方法,在reply方法中,首选是根据url参数获取DubboExporter在获取其Invoker,通过前面的分析我们可以发现,这里的Invoker实际上是一个DelegateProviderMetaDataInvoker内部持有一个AbstractProxyInvoker的内部实现。默认是通过JavassistProxyFactory生成:

public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    
    
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        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 {
    
    
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

最终在Wrapper中Dubbo会动态生成一个包装类,持有一个实际的服务,最终通过实际服务区执行,我们这里的Invoker是AbstractProxyInvoker:

public Result invoke(Invocation invocation) throws RpcException {
    
    
        try {
    
    
            Object value = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
            CompletableFuture<Object> future = wrapWithFuture(value);
            CompletableFuture<AppResponse> appResponseFuture = future.handle((obj, t) -> {
    
    
                AppResponse result = new AppResponse(invocation);
                if (t != null) {
    
    
                    if (t instanceof CompletionException) {
    
    
                        result.setException(t.getCause());
                    } else {
    
    
                        result.setException(t);
                    }
                } else {
    
    
                    result.setValue(obj);
                }
                return result;
            });
            return new AsyncRpcResult(appResponseFuture, invocation);
        } catch (InvocationTargetException e) {
    
    
            if (RpcContext.getServiceContext().isAsyncStarted() && !RpcContext.getServiceContext().stopAsync()) {
    
    
                logger.error("Provider async started, but got an exception from the original method, cannot write the exception back to consumer because an async result may have returned the new thread.", e);
            }
            return AsyncRpcResult.newDefaultAsyncResult(null, e.getTargetException(), invocation);
        } catch (Throwable e) {
    
    
            throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

AbstractProxyInvoker有两个内部实现:基于JDK和基于Javassist,基于JDK是直接通过反射来调用,基于Javassist通过warapper,调用实际的服务来执行。

这是一个总的流程,其中有一些细节这里没有特别说明,比如看贴出来的一些代码,可以发现如果是有返回值的,Dubbo实际上是通过异步的方式去执行,然后每次调用都有一个ID,通过判断ID对应channel是否返回值,来判断调用是否完成,后续有时间还会接着研究下。

猜你喜欢

转载自blog.csdn.net/LeoHan163/article/details/120025757