dubbo系列第四篇【服务暴露过程源码详解】

5 篇文章 0 订阅

dubbo服务暴露过程
在这里插入图片描述
服务暴露主要的三件事情:
(1)将封装了invoker的expoter放入expoterMap中工服务消费者后面来根据微服务名称(接口名)查询并调用
(2)启动nettyServer
(3)创建注册中心client,以临时节点的身份将服务加入到注册中心

在这里插入图片描述

总结:
前提:获取类中被@Service注解修饰的对象
ServiceAnnotationBeanProcessor实现了BeanDefinitionRegistryPostProcessor,所以在bean注册完之后会调用该类的postProcessBeanDefinitionRegistry方法。

整个ServiceBean的注册流程非常简单,无非是扫描包,找到@Service注解修饰的对象,然后构建一个ServiceBean对象注入到Spring容器中。ServiceBean实现了ApplicationListener接口,Spring容器加载完之后会通知ApplicationListener的实现类,即调用如下方法:

1.Spring容器刷新时会出发事件监听机制,触发dubbo的服务暴露流程【ServiceBean】
2.判断当前service是否已发布,如果已发布则取消当前发布,否则继续
3.判断是否延迟发布,还是直接发布
4.修改发布状态为true,然后开始发布
5.根据注册中心列表和服务协议列表,循环进行service的服务注册(先按照服务协议列表循环,如果协议是2个,注册中心是3个,则暴露的服务有6个)
6.获取注册中心URL地址列表:首先获取`<dubbo:registry/>`中的address属性进行解析,结合其他例如:`<dubbo:application/> <dubbo:service/>` 上的属性,等信息组装注册中心url列表. 判断register是否为true来判断服务是否需要注册
7.判断是否本地服务暴露 协议是否为【inJvm】或 scope的值为【remote】,则进行本地服务暴露exportLocal(),如果不是则进行远程服务暴露
8.使用javaassist动态生产的proxyFactory和url构建出invoker
9.使用当前`<dubbo:service/>`的配置,对invoker进行一次封装,形成真正用于暴露的invoker
10.进行本地服务暴露【doLocalExport】
11.【DubboProtocol#export】远程服务暴露,将exporter放入到exporterMap中,供消费者调用**
12.【DubboProtocol#openServer】真正的远程服务暴露(同步转异步【ExchangeServer】,异步启动netty Server服务端,绑定端口)**
13.【ZookeeperRegistry#doRegister】服务暴露完成后,将服务提供者注册到注册中心(启动zk客户端,创建对应的临时节点)**
14.完成服务注册

入口:org.apache.dubbo.config.spring.ServiceBean#onApplicationEvent

// 当Spring容器刷新时会触发其执行
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
    // 若当前service尚未发布,也没有取消发布,则进行发布
    if (!isExported() && !isUnexported()) {
        if (logger.isInfoEnabled()) {
            logger.info("The service ready on spring started. service: " + getInterface());
        }
        // 发布服务
        export();
    }
}

/**
* @since 2.6.5
*/
@Override
public void export() {
   super.export();
   // Publish ServiceBeanExportedEvent
   publishExportEvent();
}
 public synchronized void export() {
        // 检查并更新子配置
        checkAndUpdateSubConfigs();

        // 若配置的是不发布服务,则直接结束
        if (!shouldExport()) {
            return;
        }

        // 判断是否延迟发布
        if (shouldDelay()) {
            // 定义定时任务     this::doExport Lambda中的实例方法引用
            DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
        } else {
            // 直接发布
            doExport();
        }
    }
protected synchronized void doExport() {
        if (unexported) {
            throw new IllegalStateException("The service " + interfaceClass.getName() + " has already unexported!");
        }
        if (exported) {
            return;
        }
        // 修改发布状态
        exported = true;

        if (StringUtils.isEmpty(path)) {
            path = interfaceName;
        }

        doExportUrls();
    }
private void doExportUrls() {
        // 加载多注册中心(形成标准化的注册中心URL)
        List<URL> registryURLs = loadRegistries(true);
        // 遍历当前service的所有服务暴露协议
        for (ProtocolConfig protocolConfig : protocols) {
            String pathKey = URL.buildKey(getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);
            ProviderModel providerModel = new ProviderModel(pathKey, ref, interfaceClass);
            ApplicationModel.initProviderModel(pathKey, providerModel);
            // 使用一个协议将service暴露到所有注册中心
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }

这里我们进入到loadRegisteries(true)这个加载多注册中心的方法

/**
     *
     * Load the registry and conversion it to {@link URL}, the priority order is: system property > dubbo registry config
     *
     * @param provider whether it is the provider side
     * @return
     */
    protected List<URL> loadRegistries(boolean provider) {
        // check && override if necessary
        List<URL> registryList = new ArrayList<URL>();
        // 若注册中心不为空
        if (CollectionUtils.isNotEmpty(registries)) {
            // 遍历所有注册中心
            for (RegistryConfig config : registries) {
                // 获取<dubbo:registry/>的address属性
                String address = config.getAddress();
                // 若没有设置address属性,则对所有ip均成立
                if (StringUtils.isEmpty(address)) {
                    address = ANYHOST_VALUE;
                }
                // 若当前不是直连
                if (!RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
                    // 该map中用于存放要组装url的元数据
                    Map<String, String> map = new HashMap<String, String>();
                    // 将<dubbo:application/>中的属性写入到map
                    appendParameters(map, application);
                    // 将<dubbo:service/>中的属性写入到map
                    appendParameters(map, config);
                    // 将path写入到map
                    map.put(PATH_KEY, RegistryService.class.getName());
                    appendRuntimeParameters(map);
                    if (!map.containsKey(PROTOCOL_KEY)) {
                        map.put(PROTOCOL_KEY, DUBBO_PROTOCOL);
                    }
                    // 从一个地址解析出多个URL
                    List<URL> urls = UrlUtils.parseURLs(address, map);

                    // 将原来的类似于 zookeeper://localhost:2181 格式的地址,格式化为
                    // registry://localhost:2181?....&registry=zookeeper
                    for (URL url : urls) {
                        url = URLBuilder.from(url)
                                .addParameter(REGISTRY_KEY, url.getProtocol())
                                .setProtocol(REGISTRY_PROTOCOL)
                                .build();
                        // 若当前为提供者,且需要注册,  或  当前为消费者,且需要订阅
                        // 则记录下这个URL
                        if ((provider && url.getParameter(REGISTER_KEY, true))
                                || (!provider && url.getParameter(SUBSCRIBE_KEY, true))) {
                            registryList.add(url);
                        }
                    }
                }
            }
        }
        return registryList;
    }

然后继续我们的doExportUrlsFor1Protocol,使用一个协议将所有的service暴露到所有注册中心

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

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

    appendRuntimeParameters(map);
    appendParameters(map, metrics);
    appendParameters(map, application);
    appendParameters(map, module);
    // remove 'default.' prefix for configs from ProviderConfig
    // appendParameters(map, provider, Constants.DEFAULT_KEY);
    appendParameters(map, provider);
    appendParameters(map, protocolConfig);
    appendParameters(map, this);
    if (CollectionUtils.isNotEmpty(methods)) {
        for (MethodConfig method : methods) {
            appendParameters(map, method, method.getName());
            String retryKey = method.getName() + ".retry";
            if (map.containsKey(retryKey)) {
                String retryValue = map.remove(retryKey);
                if ("false".equals(retryValue)) {
                    map.put(method.getName() + ".retries", "0");
                }
            }
            List<ArgumentConfig> arguments = method.getArguments();
            if (CollectionUtils.isNotEmpty(arguments)) {
                for (ArgumentConfig argument : arguments) {
                    // convert argument type
                    if (argument.getType() != null && argument.getType().length() > 0) {
                        Method[] methods = interfaceClass.getMethods();
                        // visit all methods
                        if (methods != null && methods.length > 0) {
                            for (int i = 0; i < methods.length; i++) {
                                String methodName = methods[i].getName();
                                // target the method, and get its signature
                                if (methodName.equals(method.getName())) {
                                    Class<?>[] argtypes = methods[i].getParameterTypes();
                                    // one callback in the method
                                    if (argument.getIndex() != -1) {
                                        if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {
                                            appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                                        } else {
                                            throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
                                        }
                                    } else {
                                        // multiple callbacks in the method
                                        for (int j = 0; j < argtypes.length; j++) {
                                            Class<?> argclazz = argtypes[j];
                                            if (argclazz.getName().equals(argument.getType())) {
                                                appendParameters(map, argument, method.getName() + "." + j);
                                                if (argument.getIndex() != -1 && argument.getIndex() != j) {
                                                    throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    } else if (argument.getIndex() != -1) {
                        appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                    } else {
                        throw new IllegalArgumentException("Argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
                    }

                }
            }
        } // end of methods for
    }

	// 是否是泛化调用
    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 = Wrapper.getWrapper(interfaceClass).getMethodNames();
        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)), ","));
        }
    }
    if (!ConfigUtils.isEmpty(token)) {
        if (ConfigUtils.isDefault(token)) {
            map.put(TOKEN_KEY, UUID.randomUUID().toString());
        } else {
            map.put(TOKEN_KEY, token);
        }
    }
    // export service
    // 获取到host和porrt
    String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
    Integer port = this.findConfigedPorts(protocolConfig, name, map);
    // 使用暴露协议名称、host、port、service及元数据(map)组装为一个URL
    URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);

	// ConfiguratorFactory用于对现有暴露协议进行再配置的SPI扩展
    if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
            .hasExtension(url.getProtocol())) {
        url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
    }
	// 获取<dubbo:service/>的scope属性
    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)
        // 若scope的值不为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)
        // 若scope的值不为local,则进行远程服务暴露
        if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
            if (!isOnlyInJvm() && logger.isInfoEnabled()) {
                logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
            }
            if (CollectionUtils.isNotEmpty(registryURLs)) {
            	// 遍历所有注册中心
                for (URL registryURL : registryURLs) {
                    //if protocol is only injvm ,not register
                    // 若当前协议为injvm,则说明当前仅做本地暴露,这里的远程暴露直接结束
                    if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
                        continue;
                    }
                    url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
                    URL monitorUrl = loadMonitor(registryURL);
                    if (monitorUrl != null) {
                        url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
                    }
                    if (logger.isInfoEnabled()) {
                        logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                    }

                    // For providers, this is used to enable custom proxy to generate invoker
                    String proxy = url.getParameter(PROXY_KEY);
                    if (StringUtils.isNotEmpty(proxy)) {
                        registryURL = registryURL.addParameter(PROXY_KEY, proxy);
                    }
					// 使用URL构建出invoker
                    Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
                    // 使用当前<dubbo:service/>的配置,对invoker进行一次封装,形成真正用于暴露的invoker
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
					// 远程服务暴露(会注册到注册中心)
                    Exporter<?> exporter = protocol.export(wrapperInvoker);
                    // 加入到exporters列表中
                    exporters.add(exporter);
                }
            } else {
            	// 处理没有注册中心的情况
                Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
                DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
				// 服务暴露(不注册到注册中心)
                Exporter<?> exporter = protocol.export(wrapperInvoker);
                exporters.add(exporter);
            }
            /**
             * @since 2.7.0
             * ServiceData Store
             */
            MetadataReportService metadataReportService = null;
            if ((metadataReportService = getMetadataReportService()) != null) {
                metadataReportService.publishProvider(url);
            }
        }
    }
    this.urls.add(url);
}

dubbo 本地调用源码解析地址文章

// org.apache.dubbo.registry.integration.RegistryProtocol
@Override
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        // 从invoker中获取到注册中心URL
        URL registryUrl = getRegistryUrl(originInvoker);
        // url to export locally
        // 从invoker中获取到提供者URL
        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.
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);

        providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
        //export invoker
        // 远程暴露
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

        // url to registry
        // 获取注册中心
        final Registry registry = getRegistry(originInvoker);
        final URL registeredProviderUrl = getRegisteredProviderUrl(providerUrl, registryUrl);
        ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker,
                registryUrl, registeredProviderUrl);
        //to judge if we need to delay publish
        boolean register = registeredProviderUrl.getParameter("register", true);
        if (register) {
            // 将提供者注册到注册中心(注意在zk中创建的节点是一个临时节点,如果服务提供者挂了则节点会被删除)
            register(registryUrl, registeredProviderUrl);
            providerInvokerWrapper.setReg(true);
        }

        // Deprecated! Subscribe to override rules in 2.6.x or before.
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);

        exporter.setRegisterUrl(registeredProviderUrl);
        exporter.setSubscribeUrl(overrideSubscribeUrl);
        //Ensure that a new exporter instance is returned every time export
        return new DestroyableExporter<>(exporter);
    }
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

        if (!filters.isEmpty()) {
            for (int i = filters.size() - 1; i >= 0; i--) {
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                last = new Invoker<T>() {

                    @Override
                    public Class<T> getInterface() {
                        return invoker.getInterface();
                    }

                    @Override
                    public URL getUrl() {
                        return invoker.getUrl();
                    }

                    @Override
                    public boolean isAvailable() {
                        return invoker.isAvailable();
                    }

                    @Override
                    public Result invoke(Invocation invocation) throws RpcException {
                        Result asyncResult;
                        try {
                            asyncResult = filter.invoke(next, invocation);
                        } catch (Exception e) {
                            // onError callback
                            if (filter instanceof ListenableFilter) {
                                Filter.Listener listener = ((ListenableFilter) filter).listener();
                                if (listener != null) {
                                    listener.onError(e, invoker, invocation);
                                }
                            }
                            throw e;
                        }
                        return asyncResult;
                    }

                    @Override
                    public void destroy() {
                        invoker.destroy();
                    }

                    @Override
                    public String toString() {
                        return invoker.toString();
                    }
                };
            }
        }

        return new CallbackRegistrationInvoker<>(last, filters);
    }	
#org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
@Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        URL url = invoker.getUrl();

        // export service.
        String key = serviceKey(url);
        // 生成exporter
        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
        // 将exporter存放到exporterMap中,将来消费者的调用请求到来后,会首先从这里查找
        // 其所需要的exporter
        exporterMap.put(key, exporter);

        //export an stub service for dispatching event
        Boolean isStubSupportEvent = url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT);
        Boolean isCallbackservice = url.getParameter(IS_CALLBACK_SERVICE, false);
        if (isStubSupportEvent && !isCallbackservice) {
            String stubServiceMethods = url.getParameter(STUB_EVENT_METHODS_KEY);
            if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
                if (logger.isWarnEnabled()) {
                    logger.warn(new IllegalStateException("consumer [" + url.getParameter(INTERFACE_KEY) +
                            "], has set stubproxy support event ,but no stub methods founded."));
                }

            } else {
                stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
            }
        }
        // 启动NettyServer
        openServer(url);
        optimizeSerialization(url);

        return exporter;
    }

private void openServer(URL url) {
        // find server.
        // 获取key:  ip:port
        String key = url.getAddress();
        //client can export a service which's only for server to invoke
        // 从url中获取isServer属性值,isServer为true,表示当前为provider
        boolean isServer = url.getParameter(IS_SERVER_KEY, true);
        if (isServer) {
            // dubbo-check
            ExchangeServer server = serverMap.get(key);
            if (server == null) {
                synchronized (this) {
                    server = serverMap.get(key);
                    if (server == null) {
                        // createServer(url)中最终会创建一个NettyServer
                        serverMap.put(key, createServer(url));
                    }
                }
            } else {
                // server supports reset, use together with override
                server.reset(url);
            }
        }
    }

参考 https://www.cnblogs.com/ouhaitao/p/14294873.html

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值