dubbo的服务发布原理分析


前言

Dubbo的服务发布与注册原理是Dubbo框架的核心功能之一,它实现了微服务架构下的服务注册与发现机制。以下是对Dubbo服务发布与注册原理的详细解析
本文是基于 dubbo-3.1.0 版本进行分析


一、服务监听ContextRefreshedEvent

在springboot 中,refresh 是 AbstractApplicationContext 中的一个方法,负责初始化 ApplicationContext 容器,容器必须调用 refresh 才能正常工作是进行,refresh的最后处理了 finishRefresh 方法,改方法会广播一个ContextRefreshedEvent容器刷新完成事件,所有监听了该事件的bean都会去执行相关逻辑处理。

public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            	// 省略无关代码***
                
                // 初始化生命周期处理器,调用生命周期处理器onRefresh方法,发布ContextRefreshedEvent事件,JMX相关处理
                this.finishRefresh();
            
            	// 省略无关代码***
        }
    }
	protected void finishRefresh() {
		// 清除上下文资源缓存(如扫描中的ASM元数据) scanning).
		clearResourceCaches();

		// Initialize lifecycle processor for this context.
		initLifecycleProcessor();

		// Propagate refresh to lifecycle processor first.
		getLifecycleProcessor().onRefresh();

		// 广播刷新完成事件
		publishEvent(new ContextRefreshedEvent(this));

		// Participate in LiveBeansView MBean, if active.
		if (!NativeDetector.inNativeImage()) {
			LiveBeansView.registerApplicationContext(this);
		}
	}

dubbo很好的结合了spring的这一个拓展点,在这个拓展点开始实现服务的发布。可以看到,DubboDeployApplicationListener实现了ContextRefreshedEvent的消息监听

public class DubboDeployApplicationListener implements ApplicationListener<ApplicationContextEvent>, ApplicationContextAware, Ordered {
    private static final Logger logger = LoggerFactory.getLogger(DubboDeployApplicationListener.class);

    private ApplicationContext applicationContext;
    private ApplicationModel applicationModel;
    private ModuleModel moduleModel;

    @Override
    public void onApplicationEvent(ApplicationContextEvent event) {
        if (nullSafeEquals(applicationContext, event.getSource())) {
            if (event instanceof ContextRefreshedEvent) {
                onContextRefreshedEvent((ContextRefreshedEvent) event);
            } else if (event instanceof ContextClosedEvent) {
                onContextClosedEvent((ContextClosedEvent) event);
            }
        }
    }

    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
    	// 获取配置的deployer 进行发布,默认是 DefaultModuleDeployer 
        ModuleDeployer deployer = moduleModel.getDeployer();
        Assert.notNull(deployer, "Module deployer is null");
        // start module
        Future future = deployer.start();

        // if the module does not start in background, await finish
        if (!deployer.isBackground()) {
            try {
            	// 等待发布结束
                future.get();
            } catch (InterruptedException e) {
                logger.warn("Interrupted while waiting for dubbo module start: " + e.getMessage());
            } catch (Exception e) {
                logger.warn("An error occurred while waiting for dubbo module start: " + e.getMessage(), e);
            }
        }
    }

}

DefaultModuleDeployer 中,真正核心的是ServiceConfig,ServiceConfig才是去实际发布的动作

public class DefaultModuleDeployer extends AbstractDeployer<ModuleModel> implements ModuleDeployer {
 
    // DefaultApplicationDeployer
    private ApplicationDeployer applicationDeployer;
 
    @Override
    public synchronized Future start() throws IllegalStateException {
 
        ...
        // 不管是 DefaultApplicationDeployer 还是DefaultModuleDeployer的initialize方法,都是处理相关配置文件
        // 其功能等价于监听器 DubboConfigApplicationListener
        applicationDeployer.initialize();
        initialize();
        // 真正触发 服务注册功能
        exportServices();
        ...
        return startFuture;
    }
 
    private void exportServices() {
        // 从ModuleConfigManager遍历每一个ServiceBean,即dubbo.service
        for (ServiceConfigBase sc : configManager.getServices()) {
            exportServiceInternal(sc);
        }
    }
 
	private void exportServiceInternal(ServiceConfigBase sc) {
        ServiceConfig<?> serviceConfig = (ServiceConfig<?>) sc;
        if (!serviceConfig.isRefreshed()) {
            serviceConfig.refresh();
        }
        if (sc.isExported()) {
            return;
        }
        // 异步发布 默认同步
        if (exportAsync || 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(getIdentifier() + " export async catch error : " + t.getMessage(), t);
                }
            }, executor);

            asyncExportingFutures.add(future);
        } else {
            if (!sc.isExported()) {
            	// 开始发布
                sc.export();
                exportedServices.add(sc);
            }
        }
    }
}

二、服务发布ServiceConfig

1、时序图

ServiceConfig,结合暴露服务时序,我们发现ServiceConfig开始,进行服务的暴露。
在这里插入图片描述

2、serviceConfig.export()

synchronized 双重检查锁,保证服务只发布一次

public class ServiceConfig<T> extends ServiceConfigBase<T> {
    @Override
    public void export() {
    	// 如果已经发布过
        if (this.exported) {
            return;
        }

        // ensure start module, compatible with old api usage
        getScopeModel().getDeployer().start();
		// 防止并发
        synchronized (this) {
            if (this.exported) {
                return;
            }
            if (!this.isRefreshed()) {
                this.refresh();
            }
            if (this.shouldExport()) {
                this.init();
                if (shouldDelay()) {
                	// 延迟发布
                    doDelayExport();
                } else {
                	// 开始进行发布
                    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();
    }

}

3、serviceConfig.doExportUrls()

在 serviceConfig.doExportUrls() 中主要做了两件事:

  • 获取所有注册中心URL
  • 遍历所有 protocols进行注册发布
public class ServiceConfig<T> extends ServiceConfigBase<T> {
	protected List<ProtocolConfig> protocols;

	private void doExportUrls() {
        ModuleServiceRepository repository = getScopeModel().getServiceRepository();
        ServiceDescriptor serviceDescriptor;
        final boolean serverService = ref instanceof ServerService;
        if (serverService) {
            serviceDescriptor = ((ServerService) ref).getServiceDescriptor();
            repository.registerService(serviceDescriptor);
        } else {
            serviceDescriptor = repository.registerService(getInterfaceClass());
        }
        // ProviderModel 表示服务提供者模型,此对象中存储了与服务提供者相关的信息
		// 比如服务的配置信息,服务实例等。每个被导出的服务对应一个 ProviderModel
        providerModel = new ProviderModel(getUniqueServiceName(),
            ref,
            serviceDescriptor,
            getScopeModel(),
            serviceMetadata, interfaceClassLoader);

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

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

		// 获取注册中心地址
        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);
            // 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);
        }

        providerModel.setServiceUrls(urls);
    }
}

registryURLs地址如下所示,注册模式为zookeeper

registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-springboot-start-provider
&dubbo=2.0.2&pid=5536&qos.enable=false&registry=zookeeper&release=3.1.0&timestamp=1724516467041

4、serviceConfig.doExportUrlsFor1Protocol()

	private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
		// 构建配置参数
        Map<String, String> map = buildAttributes(protocolConfig);

        // remove null key and null value
        map.keySet().removeIf(key -> key == null || map.get(key) == null);
        // init serviceMetadata attachments
        serviceMetadata.getAttachments().putAll(map);
		// 构建url 默认使用dubbo协议 host port
        URL url = buildUrl(protocolConfig, map);
		// 暴露相应的url
        exportUrl(url, registryURLs);
    }

构建好的url如下所示

dubbo://192.168.0.101:20880/org.sjl.dubbo.AsyncProvider?
anyhost=true
&application=dubbo-springboot-start-provider
&background=false
&bind.ip=192.168.0.101
&bind.port=20880
&deprecated=false
&dubbo=2.0.2
&dynamic=true
&generic=false
&interface=org.sjl.dubbo.AsyncProvider
&methods=sayHiAsync,sayHello,sayHelloAsync
&pid=22524
&qos.enable=false
&release=3.1.0
&side=provider
&timeout=30000
&timestamp=1724516860685

4、serviceConfig.exportUrl()

    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);
                if (!isGeneric(generic) && !getScopeModel().isInternal()) {
                    MetadataUtils.publishServiceDefinition(url, providerModel.getServiceModel(), getApplicationModel());
                }
            }
        }
        this.urls.add(url);
    }

获取export的范围,这个是通过dubbo.provider.scope来配置的,可选项有remote和local,表示是否需要发布远程服务。默认情况下既会暴露本地服务也会暴露远程服务。

5、serviceConfig.exportRemote()

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 + " to registry " + registryURL.getAddress());
                    } else {
                        logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                    }
                }
				// 将url封装到registryURL 中进行传输
                doExportUrl(registryURL.putAttribute(EXPORT_KEY, url), true);
            }

        } else {

            if (logger.isInfoEnabled()) {
                logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
            }
			// 没有registryURL ,则直接将url进行传输
            doExportUrl(url, true);
        }


        return url;
    }

6、serviceConfig.doExportUrl()

    protocolSPI = this.getExtensionLoader(Protocol.class).getAdaptiveExtension();
    @SuppressWarnings({"unchecked", "rawtypes"})
    private void doExportUrl(URL url, boolean withMetaData) {
    	// 包装成invoker,所有执行的本质都是invoker 
    	// 自适应扩展点,默认使用javaasisst 代理
    	// ref:接口实现  interfaceClass:接口  url:registerUrl
        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
        if (withMetaData) {
            invoker = new DelegateProviderMetaDataInvoker(invoker, this);
        }
        // spi 机制,自适应扩展点 默认走dubbo协议
        Exporter<?> exporter = protocolSPI.export(invoker);
        exporters.add(exporter);
    }

通过javaasisst生成一个代理类,将要发布的接口、实现类、注册url封装起来,统一代理

三、协议 Protocol

1、protocol spi扩展点

filter=org.apache.dubbo.rpc.cluster.filter.ProtocolFilterWrapper
listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=org.apache.dubbo.rpc.support.MockProtocol
serializationwrapper=org.apache.dubbo.rpc.protocol.ProtocolSerializationWrapper

dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol
rest=org.apache.dubbo.rpc.protocol.rest.RestProtocol
grpc=org.apache.dubbo.rpc.protocol.grpc.GrpcProtocol
tri=org.apache.dubbo.rpc.protocol.tri.TripleProtocol
registry=org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol
service-discovery-registry=org.apache.dubbo.registry.integration.RegistryProtocol
qos=org.apache.dubbo.qos.protocol.QosProtocolWrapper

2、Protocol$Adaptive

是一个动态生成的适配器类,它会根据传递进来的参数来选择合适的协议来发布服务。

@SPI("dubbo")
ublic class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
	public void destroy() {
        throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }

    public int getDefaultPort() {
        throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }

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

    public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg1;
        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.refer(arg0, arg1);
    }

    public java.util.List getServers() {
        throw new UnsupportedOperationException("The method public default java.util.List org.apache.dubbo.rpc.Protocol.getServers() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }
}

在这里插入图片描述

根据当前传递过来的Invoker中携带的url为registerUrl,协议头是service-discovery-registry,获得的扩展点就是RegistryProtocol。
在调用RegistryProtocol之前,dubbo通过动态加载Wrapper进行功能的增强
ExtensionLoader.loadClass中

	private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
                           boolean overridden) {
          // 省略其它代码 ***
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            cacheAdaptiveClass(clazz, overridden);
            // 如果是包装类
        } else if (isWrapperClass(clazz)) {
            cacheWrapperClass(clazz);
        }  // 省略其它代码 ***
       
    }
 
    protected boolean isWrapperClass(Class<?> clazz) {
        Constructor<?>[] constructors = clazz.getConstructors();
        // 判断构造函数
        for (Constructor<?> constructor : constructors) {
            if (constructor.getParameterTypes().length == 1 && constructor.getParameterTypes()[0] == type) {
                return true;
            }
        }
        return false;
    } 
    // 例如ProtocolListenerWrapper就符合
    public ProtocolListenerWrapper(Protocol protocol) {
        if (protocol == null) {
            throw new IllegalArgumentException("protocol == null");
        }
        this.protocol = protocol;
    }

3、ProtocolWrapper

org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);

public T getExtension(String name) {
    // 省略其它代码 ***
    Object instance = holder.get();
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
            	// 构建
                instance = createExtension(name);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}

 
private T createExtension(String name) {
     // 省略其它代码 ***
     	// 对cacheWrapperClass集合进行判断,如果集合不为空,则进行包装
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (CollectionUtils.isNotEmpty(wrapperClasses)) {
            for (Class<?> wrapperClass : wrapperClasses) {
                instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
            }
        }
        return instance;
    // 省略其它代码 ***
}

至此形成的调用链为

ProtocolFilterWrapper -> ProtocolListenerWrapper -> QosProtocolWrapper -> RegistryProtocol
  • ProtocolFilterWrapper,对invoker进行filter的包装,实现请求的过滤
  • ProtocolListenerWrapper, 用于服务export时候插入监听机制
  • QosprotocolWrapper, 如果当前配置了注册中心,则会启动一个Qos server.qos是dubbo的在线运维命令,dubbo2.5.8新版本重构了telnet模块,提供了新的telnet命令支持,新版本的telnet端口与dubbo协议的端口是不同的端口,默认为22222

4、RegistryProtocol.export()

    @Override
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    	// step1:获取注册中心地址 service-discovery-registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?***
        URL registryUrl = getRegistryUrl(originInvoker);
        // 这里是获得服务提供者的url dubbo://192.168.0.101:20880/org.sjl.dubbo.AsyncProvider?anyhost=true&***
        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.
        // 订阅override数据。在admin控制台可以针对服务进行治理,比如修改权重,修改路由机制等
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
        Map<URL, NotifyListener> overrideListeners = getProviderConfigurationListener(providerUrl).getOverrideListeners();
        overrideListeners.put(registryUrl, overrideSubscribeListener);

        providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
        //export invoker
        // 服务发布
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

        // url to registry 获取注册中心
        final Registry registry = getRegistry(registryUrl);
        final URL registeredProviderUrl = getUrlToRegistry(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) {
        	// 存在注册中心,需要注册
            register(registry, registeredProviderUrl);
        }

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


        exporter.setRegisterUrl(registeredProviderUrl);
        exporter.setSubscribeUrl(overrideSubscribeUrl);

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

在RegistryProtocol.export中,有两个核心流程

  • 调用 doLocalExport启动本地服务,也就是netty server
  • 调用 register方法进行服务地址的注册

5、RegistryProtocol.doLocalExport()

public class RegistryProtocol implements Protocol {

	// 启动本地服务
	private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
		// key 为 getProviderUrl
        String key = getCacheKey(originInvoker);

		// map.computeIfAbsent(key, Function):解决RMI重复暴露端口冲突的问题,已经暴露的服务不再暴露。
        return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
            Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
            
            // protocol.export(invokerDelegate) 进行进一步的协议相关的服务发布
            return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
        });
    }
    
}

在这里插入图片描述

protocol是通过依赖注入来初始化的一个协议扩展点,并且我们可以看到这个protocol.export()方法上增加了@Adaptive注解,表示它是一个动态适配的扩展点,当前没有指定protocol,走默认的dubbo,形成的调用链为
ProtocolListenerWrapper ->QosProtocolWrapper ->ProtocolFilterWrapper-DubboProtocol

6、DubboProtocol.export

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

        // export service.
        String key = serviceKey(url);
        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);

        //export a 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 stub proxy support event ,but no stub methods founded."));
                }

            }
        }
		// 开启一个服务
        openServer(url);
        optimizeSerialization(url);

        return exporter;
    }

6、DubboProtocol.openServer

双重检查锁,一个服务地址 host:port 只允许创建一个服务

    private void openServer(URL url) {
        checkDestroyed();
        // find server.
        // 获取 host:port,并将其作为服务器实例的 key,用于标识当前的服务器实例
        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
            // 重置url相关配置
            server.reset(url);
        }
    }

7、DubboProtocol.createServer

    private ProtocolServer createServer(URL 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();

		// 默认走DEFAULT_REMOTING_SERVER : netty
		// 通过 SPI 检测是否存在 server 参数所代表的 Transporter 拓展,不存在则抛出异常
        String transporter = url.getParameter(SERVER_KEY, DEFAULT_REMOTING_SERVER);
        if (StringUtils.isNotEmpty(transporter) && !url.getOrDefaultFrameworkModel().getExtensionLoader(Transporter.class).hasExtension(transporter)) {
            throw new RpcException("Unsupported server type: " + transporter + ", url: " + url);
        }
		// 创建ExchangeServer.
        ExchangeServer server;
        try {
            server = Exchangers.bind(url, requestHandler);
        } catch (RemotingException e) {
            throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
        }

        transporter = url.getParameter(CLIENT_KEY);
        if (StringUtils.isNotEmpty(transporter) && !url.getOrDefaultFrameworkModel().getExtensionLoader(Transporter.class).hasExtension(transporter)) {
            throw new RpcException("Unsupported client type: " + transporter);
        }

        DubboProtocolServer protocolServer = new DubboProtocolServer(server);
        loadServerProperties(protocolServer);
        return protocolServer;
    }

8、ExchangeServer.bin

    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");
        // 调用 HeaderExchanger 的 bind 方法创建 ExchangeServer 实例
        return getExchanger(url).bind(url, handler);
    }
	 public static Exchanger getExchanger(URL url) {
	 	// 默认header
        String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
        return url.getOrDefaultFrameworkModel().getExtensionLoader(Exchanger.class).getExtension(type);
    }

headerExchanger.bind中,我们需要关注transporters.bind

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

transporters.bind

public class Transporters {
    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(url).bind(url, handler);
    }
    public static Transporter getTransporter(URL url) {
        return url.getOrDefaultFrameworkModel().getExtensionLoader(Transporter.class).getAdaptiveExtension();
    }
}

对最开始的protocol分析,可以看出url.getOrDefaultFrameworkModel().getExtensionLoader(Transporter.class).getAdaptiveExtension()会基于Transporter$Adaptive方法进行适配,因为没有在外层指定通讯,这里走的是默认netty通讯,执行的是NettyTransporter

三、Netty

1、NettyTransporter.bind

public class NettyTransporter implements Transporter {

    public static final String NAME = "netty";

    @Override
    public RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException {
    	// 创建一个nettyserver
        return new NettyServer(url, handler);
    }
}

2、NettyServer

public class NettyServer extends AbstractServer {

    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(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME), ChannelHandlers.wrap(handler, url));

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

    /**
     * Init and start netty server
     *
     * @throws Throwable
     */
     // 初始化和启动netty服务
    @Override
    protected void doOpen() throws Throwable {
        bootstrap = new ServerBootstrap();

        bossGroup = createBossGroup();
        workerGroup = createWorkerGroup();

        final NettyServerHandler nettyServerHandler = createNettyServerHandler();
        channels = nettyServerHandler.getChannels();

        initServerBootstrap(nettyServerHandler);

        // bind
        ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
        channelFuture.syncUninterruptibly();
        channel = channelFuture.channel();

    }
    
    protected void initServerBootstrap(NettyServerHandler nettyServerHandler) {
        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", new SslServerTlsHandler(getUrl()));
                        }
                        ch.pipeline()
                                .addLast("decoder", adapter.getDecoder())
                                .addLast("encoder", adapter.getEncoder())
                                .addLast("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
                                .addLast("handler", nettyServerHandler);
                    }
                });
    }
}

public abstract class AbstractServer extends AbstractEndpoint implements RemotingServer {
    public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
        super(url, handler);
        executorRepository = url.getOrDefaultApplicationModel().getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
        localAddress = getUrl().toInetSocketAddress();

		// 获取 host :port
        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 {
        	// NettyServer.doOpen 启动服务器
            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);
        }
        executors.add(executorRepository.createExecutorIfAbsent(url));
    }
}

这里用到了一个handler来处理客户端传递过来的请求:nettyServerHandler
从DubboProtocol类的requestHandler属性到NettyServer的nettyServerHandler属性。​这一路会经历很多Handler,经过层层封装,最后才封装成NettyServerHandler

它会经历如下Handler,前五个是netty调用链,后面就是dubbo的调用链了
NettyServerHandler-> NettyServer-> MultiMessageHandler-> ​HeartbeatHandler-> AllChannelHandler
-> ​DecodeHandler-> HeaderExchangeHandler-> ​ExchangeHandler

当客户端连接服务端,或者发送数据到服务端的时候​,
首先会由NettyServerHandler处理请求,然后依次将请求传递下去,最后到ExchangeHandler.


总结

Dubbo的服务发布主要做了以下几件事情:

  • 读取配置
  • 初始化配置对象
  • 组装服务URL
  • 创建代理类并缓存
  • 注册服务到注册中心
  • 创建网络通信服务器(netty)
  • 监控服务状态(​Heartbeat)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值