Dubbo超时详解之直连

背景

某小伙伴发现调用dubbo接口频繁超时~

首先参考Dubbo超时控制

该小伙伴还使用了dubbo直连技术 Dubbo配置直连

同时dubbo的provider还使用了springboot做服务发布

默认场景下超时都是10000ms

1
2
3
4
5
6
7
8
@Bean
public ProviderConfig provider(Dubbo dubbo) {
ProviderConfig providerConfig = new ProviderConfig();
providerConfig.setTimeout(dubbo.getTimeOut());
providerConfig.setOwner(dubbo.getOwner());
providerConfig.setGroup(dubbo.getGroup());
return providerConfig;
}

那么为何出现大量的调用失败呢???

1
cause: Waiting server-side response timeout by scan timer. start time: XXX, end time: XXX, client elapsed: 44 ms, server elapsed: 1001 ms, timeout: 1000 ms, request: Request

分析

首先由于报错出现的问题是在服务端的报错 但是由于服务端普遍设置了超时高达10s那么为何均在1s发生了报错呢???

在上文中Dubbo超时控制可以发现dubbo的默认超时就是1s 那么是否可以认为是由于某些原因【直连】导致timeout没有生效呢???

分析refer是如何构建的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private T createProxy(Map<String, String> map) {
//
if (url != null && url.length() > 0) { // 用户指定URL,指定的URL可能是对点对直连地址,也可能是注册中心URL
String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
if (us != null && us.length > 0) {
for (String u : us) {
URL url = URL.valueOf(u);
if (url.getPath() == null || url.getPath().length() == 0) {
url = url.setPath(interfaceName);
}
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
} else {
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
}
//
return (T) proxyFactory.getProxy(invoker);
}

关键代码列出~很明显此时走的直连 自然是执行ClusterUtils.mergeUrl(url, map)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public static URL mergeUrl(URL remoteUrl, Map<String, String> localMap) {
Map<String, String> map = new HashMap<String, String>();
Map<String, String> remoteMap = remoteUrl.getParameters();
if (remoteMap != null && remoteMap.size() > 0) {
map.putAll(remoteMap);
//线程池配置不使用提供者的
map.remove(Constants.THREAD_NAME_KEY);
map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.THREAD_NAME_KEY);
map.remove(Constants.THREADPOOL_KEY);
map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.THREADPOOL_KEY);
map.remove(Constants.CORE_THREADS_KEY);
map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.CORE_THREADS_KEY);
map.remove(Constants.THREADS_KEY);
map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.THREADS_KEY);
map.remove(Constants.QUEUES_KEY);
map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.QUEUES_KEY);
map.remove(Constants.ALIVE_KEY);
map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.ALIVE_KEY);
}
if (localMap != null && localMap.size() > 0) {
map.putAll(localMap);
}
if (remoteMap != null && remoteMap.size() > 0) {
// 版本号使用提供者的
String dubbo = remoteMap.get(Constants.DUBBO_VERSION_KEY);
if (dubbo != null && dubbo.length() > 0) {
map.put(Constants.DUBBO_VERSION_KEY, dubbo);
}
String version = remoteMap.get(Constants.VERSION_KEY);
if (version != null && version.length() > 0) {
map.put(Constants.VERSION_KEY, version);
}
String group = remoteMap.get(Constants.GROUP_KEY);
if (group != null && group.length() > 0) {
map.put(Constants.GROUP_KEY, group);
}
String methods = remoteMap.get(Constants.METHODS_KEY);
if (methods != null && methods.length() > 0) {
map.put(Constants.METHODS_KEY, methods);
}
// 合并filter和listener
String remoteFilter = remoteMap.get(Constants.REFERENCE_FILTER_KEY);
String localFilter = localMap.get(Constants.REFERENCE_FILTER_KEY);
if (remoteFilter != null && remoteFilter.length() > 0
&& localFilter != null && localFilter.length() > 0) {
localMap.put(Constants.REFERENCE_FILTER_KEY, remoteFilter + "," + localFilter);
}
String remoteListener = remoteMap.get(Constants.INVOKER_LISTENER_KEY);
String localListener = localMap.get(Constants.INVOKER_LISTENER_KEY);
if (remoteListener != null && remoteListener.length() > 0
&& localListener != null && localListener.length() > 0) {
localMap.put(Constants.INVOKER_LISTENER_KEY, remoteListener + "," + localListener);
}
}
return remoteUrl.clearParameters().addParameters(map);
}

这边很明显不会拼接任何关于timeout的参数【而是从localMap中获取的】

当然如果timeOut在localMap【其实就是referenceConfig的各个属性】的数据成功获取到也是能映射成功的。

但是基于如下认识

对于消费能力基本上提供者更加清楚 因此默认情况下会由服务端自己定义消费者数量 和timeout等

而至于为何注册中心的消费者可以使用到timeOut?请看上集Dubbo超时控制

解决

  1. 消费者增加timeout
  2. 直连url上增加timeOut的paramter

横展开

所有从注册中心获取的参数都无法作用在直连上 当注意~