Dubbo3:DubboInvoker中URL的参数优先级

在consumer侧,RPC参数的优先级如下: referenceconfig > consumerconfig > moduleconfig > applicationconfig > provider url

关于referenceconfig 到 applicationconfig这部分优先级处理逻辑是在ReferenceConfig中处理的,在ReferenceConfig创建Proxy对象时,有一段加载consumer url参数配置的代码:

public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
    
    
	public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
    
    
		...
		Map<String, String> referenceParameters = appendConfig();
		initServiceAppsMapping(referenceParameters);
		...
		ref = createProxy(referenceParameters);
		...
	}
	/**
     * Append all configuration required for service reference.
     *
     * @return reference parameters
     */
    private Map<String, String> appendConfig() {
    
    
        Map<String, String> map = new HashMap<>(16);
		...
        AbstractConfig.appendParameters(map, getApplication());
        AbstractConfig.appendParameters(map, getModule());
        AbstractConfig.appendParameters(map, consumer);
        AbstractConfig.appendParameters(map, this);
        appendMetricsCompatible(map);
        ...
        return map;
    }
}

对于consumer url覆盖provider url的处理有两种,分别对就在了接口级和实例级,接口级的覆盖逻辑在RegistryDirectory中,会先执行完url合并,然后再调用Protocol.refer(serviceType, margedUrl)创建Invokder对象;代码如下:

public class RegistryDirectory<T> extends DynamicDirectory<T> {
    
    
	private Map<URL, Invoker<T>> toInvokers(Map<URL, Invoker<T>> oldUrlInvokerMap, List<URL> urls) {
    
    
        Map<URL, Invoker<T>> newUrlInvokerMap = new ConcurrentHashMap<>(urls == null ? 1 : (int) (urls.size() / 0.75f + 1));
        if (urls == null || urls.isEmpty()) {
    
    
            return newUrlInvokerMap;
        }
        String queryProtocols = this.queryMap.get(PROTOCOL_KEY);
        for (URL providerUrl : urls) {
    
    
            ...
            URL url = mergeUrl(providerUrl);

            // Cache key is url that does not merge with consumer side parameters, regardless of how the consumer combines parameters, if the server url changes, then refer again
            Invoker<T> invoker = oldUrlInvokerMap == null ? null : oldUrlInvokerMap.remove(url);
            if (invoker == null) {
    
     // Not in the cache, refer again
                    boolean enabled = true;
                    if (url.hasParameter(DISABLED_KEY)) {
    
    
                        enabled = !url.getParameter(DISABLED_KEY, false);
                    } else {
    
    
                        enabled = url.getParameter(ENABLED_KEY, true);
                    }
                    if (enabled) {
    
    
                    	// 创建Invokder对象
                        invoker = protocol.refer(serviceType, url);
                    }
              
                ...
                if (invoker != null) {
    
     // Put new invoker in cache
                    newUrlInvokerMap.put(url, invoker);
                }
			...
			}
        }
        return newUrlInvokerMap;
    }

    /**
     * Merge url parameters. the order is: override > -D >Consumer > Provider
     *
     * @param providerUrl
     * @return
     */
    private URL mergeUrl(URL providerUrl) {
    
    
        if (providerUrl instanceof ServiceAddressURL) {
    
    
            providerUrl = overrideWithConfigurator(providerUrl);
        } else {
    
    
        	// 合并 consumer 侧 parameters
            providerUrl = applicationModel.getBeanFactory().getBean(ClusterUtils.class).mergeUrl(providerUrl, queryMap); // Merge the consumer side parameters
            providerUrl = overrideWithConfigurator(providerUrl);
            providerUrl = providerUrl.addParameter(Constants.CHECK_KEY, String.valueOf(false)); // Do not check whether the connection is successful or not, always create Invoker!
        }

        // FIXME, kept for mock
        if (providerUrl.hasParameter(MOCK_KEY) || providerUrl.getAnyMethodParameter(MOCK_KEY) != null) {
    
    
            providerUrl = providerUrl.removeParameter(TAG_KEY);
        }

        if ((providerUrl.getPath() == null || providerUrl.getPath()
                .length() == 0) && DUBBO_PROTOCOL.equals(providerUrl.getProtocol())) {
    
     // Compatible version 1.0
            //fix by tony.chenl DUBBO-44
            String path = directoryUrl.getServiceInterface();
            if (path != null) {
    
    
                int i = path.indexOf('/');
                if (i >= 0) {
    
    
                    path = path.substring(i + 1);
                }
                i = path.lastIndexOf(':');
                if (i >= 0) {
    
    
                    path = path.substring(0, i);
                }
                providerUrl = providerUrl.setPath(path);
            }
        }
        return providerUrl;
    }
}

实例级服务发现场景中使用的是ServiceDiscoveryRegistryDirectory和InstanceAddressURL,对于consumer side parameters和provider side parameters也是做了更灵活的处理,主要逻辑是InstanceAddressURL中,会先判断parameterKey是否为provider side优先,如果不是,则优先从consumer side parameters中取值;代码如下:

public class InstanceAddressURL extends URL {
    
    
	@Override
    public String getParameter(String key) {
    
    
        ...
		// 判断是否优先从consumer side 取
        if (consumerParamFirst(key)) {
    
    
            URL consumerUrl = RpcContext.getServiceContext().getConsumerUrl();
            if (consumerUrl != null) {
    
    
                String v = consumerUrl.getParameter(key);
                if (StringUtils.isNotEmpty(v)) {
    
    
                    return v;
                }
            }
        }

        String protocolServiceKey = getProtocolServiceKey();
        if (isEmpty(protocolServiceKey)) {
    
    
            return getInstanceParameter(key);
        }
        return getServiceParameter(protocolServiceKey, key);
    }

	private boolean consumerParamFirst(String key) {
    
    
        if (CollectionUtils.isNotEmpty(providerFirstParams)) {
    
    
            return !providerFirstParams.contains(key);
        } else {
    
    
            return true;
        }
    }
}

在InstanceAddressURL中会先通过providerFirstParams进行判断,InstanceAddressURL.providerFirstParams是由ServiceDiscoveryRegistryDirectory赋值的,代码如下:

public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> {
    
    
	

	public ServiceDiscoveryRegistryDirectory(Class<T> serviceType, URL url) {
    
    
        super(serviceType, url);
        moduleModel = getModuleModel(url.getScopeModel());

		// ProviderFirstParams SPI
        Set<ProviderFirstParams> providerFirstParams = url.getOrDefaultApplicationModel().getExtensionLoader(ProviderFirstParams.class).getSupportedExtensionInstances();
        if (CollectionUtils.isEmpty(providerFirstParams)) {
    
    
            this.providerFirstParams = null;
        } else {
    
    
            if (providerFirstParams.size() == 1) {
    
    
                this.providerFirstParams = Collections.unmodifiableSet(providerFirstParams.iterator().next().params());
            } else {
    
    
                Set<String> params = new HashSet<>();
                for (ProviderFirstParams paramsFilter : providerFirstParams) {
    
    
                    if (paramsFilter.params() == null) {
    
    
                        break;
                    }
                    params.addAll(paramsFilter.params());
                }
                this.providerFirstParams = Collections.unmodifiableSet(params);
            }
        }

    }

	private Map<String, Invoker<T>> toInvokers(Map<String, Invoker<T>> oldUrlInvokerMap, List<URL> urls) {
    
    
        Map<String, Invoker<T>> newUrlInvokerMap = new ConcurrentHashMap<>(urls == null ? 1 : (int) (urls.size() / 0.75f + 1));
        if (urls == null || urls.isEmpty()) {
    
    
            return newUrlInvokerMap;
        }
        for (URL url : urls) {
    
    
            InstanceAddressURL instanceAddressURL = (InstanceAddressURL) url;
            ...
            //InstanceAddressURL对象设置providerFirstParams
            instanceAddressURL.setProviderFirstParams(providerFirstParams);

            // Override provider urls if needed
            if (enableConfigurationListen) {
    
    
                instanceAddressURL = overrideWithConfigurator(instanceAddressURL);
            }

            ...
            invoker = protocol.refer(serviceType, instanceAddressURL);
            ...
        }
        return newUrlInvokerMap;
    }
}

如果要设置provider 优先字段可以通过实现ProviderFirstParams SPI 添加,默认的DefaultProviderFirstParams设置了5个provider 优先字段:release, dubbo, methods, timestamp, tag
代码如下:

public class DefaultProviderFirstParams implements ProviderFirstParams {
    
    
    private final static Set<String> PARAMS = Collections.unmodifiableSet(new HashSet<String>() {
    
    {
    
    
        addAll(Arrays.asList(RELEASE_KEY, DUBBO_VERSION_KEY, METHODS_KEY, TIMESTAMP_KEY, TAG_KEY));
    }});

    @Override
    public Set<String> params() {
    
    
        return PARAMS;
    }

}

猜你喜欢

转载自blog.csdn.net/Mr_rain/article/details/124992928