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