alibaba / spring-cloud-alibaba

Spring Cloud Alibaba provides a one-stop solution for application development for the distributed solutions of Alibaba middleware.
https://sca.aliyun.com
Apache License 2.0
27.91k stars 8.33k forks source link

Some problems about Dubbo service reference #1760

Open shihuaguo opened 4 years ago

shihuaguo commented 4 years ago

我们鼓励使用英文,如果不能直接使用,可以使用翻译软件,您仍旧可以保留中文原文。 We recommend using English. If you are non-native English speaker, you can use the translation software.

Which Component spring-boot.version:2.3.2.RELEASE spring-cloud.version:Hoxton.SR8 spring-cloud-alibaba.version:2.2.2.RELEASE

Describe what problem you have encountered 1、about DubboCloudRegistry.subscribeURLs

private void subscribeURLs(URL url, Set<String> serviceNames,
            NotifyListener listener) {

        List<URL> subscribedURLs = new LinkedList<>();

        serviceNames.forEach(serviceName -> {

            subscribeURLs(url, subscribedURLs, serviceName,
                    () -> getServiceInstances(serviceName));

        });

        // Notify all
        notifyAllSubscribedURLs(url, subscribedURLs, listener);
    }

why need to iterate all service for each url?Each URL that needs to be subscribed to should belong to only one service。Suppose we subscribe to 10 service providers, and each provider subscribes to 10 services。As we do now, we need to traverse 1000 times. There are 100 subscription URLs, each of which needs to traverse 10 service providers to subscribe, but only one of them is valid.This will greatly increase the service startup time, because the subscription service needs to access each service provider through the network.

2、about DubboCloudRegistry.cloneExportedURLs

private List<URL> cloneExportedURLs(URL subscribedURL,
            List<ServiceInstance> serviceInstances) {

        List<URL> clonedExportedURLs = new LinkedList<>();

        serviceInstances.forEach(serviceInstance -> {

            String host = serviceInstance.getHost();

            getTemplateExportedURLs(subscribedURL, serviceInstances).stream()
                    .map(templateURL -> templateURL.removeParameter(TIMESTAMP_KEY))
                    .map(templateURL -> templateURL.removeParameter(PID_KEY))
                    .map(templateURL -> {
                        String protocol = templateURL.getProtocol();
                        int port = repository.getDubboProtocolPort(serviceInstance,
                                protocol);
                        if (Objects.equals(templateURL.getHost(), host)
                                && Objects.equals(templateURL.getPort(), port)) { // use
                            // templateURL
                            // if
                            // equals
                            return templateURL;
                        }

                        URLBuilder clonedURLBuilder = from(templateURL) // remove the
                                // parameters from
                                // the template
                                // URL
                                .setHost(host) // reset the host
                                .setPort(port); // reset the port

                        return clonedURLBuilder.build();
                    }).forEach(clonedExportedURLs::add);
        });
        return clonedExportedURLs;
    }

As above, we traverse all service instances.However, in each loop, we only get the host of the service instance. We still pass all the service instances to gettemplateexportedurls,and then randomly select a service as the metadata service.

public DubboMetadataService getProxy(List<ServiceInstance> serviceInstances) {

        DubboMetadataService dubboMetadataService = null;

        // attempt to get the proxy of DubboMetadataService in maximum times
        int attempts = serviceInstances.size();

        for (int i = 0; i < attempts; i++) {
            Optional<ServiceInstance> serviceInstance = select(serviceInstances);

            if (serviceInstance.isPresent()) {

                List<URL> dubboMetadataServiceURLs = getDubboMetadataServiceURLs(
                        serviceInstance.get());

                for (URL dubboMetadataServiceURL : dubboMetadataServiceURLs) {
                    dubboMetadataService = createProxyIfAbsent(dubboMetadataServiceURL);
                    if (dubboMetadataService != null) {
                        return dubboMetadataService;
                    }
                }
            }
        }

        return dubboMetadataService;
    }

In DubboMetaServiceProxy.getProxy(List serviceInstances),it doesn't take version numbers and groups into account.So, if we have a service called ServiceA, it has two service providers, one providerA version number is 1.0.0, and the other providerB version number is 2.0.0。If we have a consumer reference serviceA with version number 2.0.0 and random select providerA as metadata service,it will throw exception like "no provider available....".

On the other hand, in the DubboCloudRegistry.cloneExportedURLs Method, it traverses all service instances, but each traversal selects the metaservice randomly. If the same service instance is selected each time, this method is inefficient and repetitive. Why not select the corresponding service instance as the metaservice in each traversal?

Let's take a look at the DubboCloudRegistry.getexportedurls method

private List<URL> getExportedURLs(DubboMetadataService dubboMetadataService,
            URL subscribedURL) {
        String serviceInterface = subscribedURL.getServiceInterface();
        String group = subscribedURL.getParameter(GROUP_KEY);
        String version = subscribedURL.getParameter(VERSION_KEY);
        // The subscribed protocol may be null
        String subscribedProtocol = subscribedURL.getParameter(PROTOCOL_KEY);
        String exportedURLsJSON = dubboMetadataService.getExportedURLs(serviceInterface,
                group, version);
        return jsonUtils.toURLs(exportedURLsJSON).stream()
                .filter(exportedURL -> subscribedProtocol == null
                        || subscribedProtocol.equalsIgnoreCase(exportedURL.getProtocol()))
                .collect(Collectors.toList());
    }

It get group and version from subscribedURL, but these two parameters have no effect for dubboMetadataService.getExportedURLs ,Because of the follow-up dubboMetadataService.getExportedURLs call,It will call dubbometadataserviceinvocationhandler. Eventually, an invoker will be randomly selected to call. The selected invoker has nothing to do with the group and version here. So,how can i implements dubbo's multi-versions control,such as follow: image i hope consumer1 only subscribe to provider1 and consumer2 only subscribe to provider2,because version matches.

3、How to modify the version number of metadataservice? I found that it is defined by the dubbometadataservice interface and cannot be modified:

serviceConfig.setVersion(DubboMetadataService.VERSION);

Describe what information you have read eg. I have read the reference doc of dubbo from http://dubbo.apache.org/

shihuaguo commented 4 years ago

标题可能有些不当,不是问题,而是疑问?可能我对dubbo spring cloud还不太了解,之前在其他issue看到dubbo是可以对接口级进行版本号控制,spring cloud alibaba却是对应用级的控制,如果是这样,那上述疑问都理解错拉

lguoyu commented 3 years ago

我也遇到了相同的问题,感觉_Eventually, an invoker will be randomly selected to call. The selected invoker has nothing to do with the group and version here._这里像是一个bug,这个实例的个性化配置在这里是生效不了的。