0%

排查 2.6.x dubbo-admin 消费者管理页面崩溃问题

最近在多 dubbo-admin 的改造,期间发现服务治理模块的消费者页面出现崩溃的问题。看提示视乎时因为 URL 参数解析导致的,接下来分析一下问题出现的原因。

问题表象

如果发现这个问题,那么后面基本上很大概率会再次出现,出现这个问题时页面会给出提示语句,下面看下错误页面给出的提示信息:

1
2
There was an unexpected error (type=Internal Server Error,status=500).
Illegal query string "application=xxx&category=consumers&check=falsegroup=&interface=xxx.xxx..."

看到这个错误信息可以去控制台看下错误日志,找到异常跑出的代码块,我查找到出错的类是“com.alibaba.dubboadmin.registry.common.route.ParseUtils”这个类的“parseQuery”方法。

问题分析

上面定位到了代码出错的位置,下面来看看这个方法的代码块,来看看这个方法具体做了哪些操作。

1
2
private static Pattern QUERY_PATTERN = Pattern
.compile("([&=]?)\\s*([^&=\\s]+)");
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
/**
* Parse Query String into Map. For strings that have only Key, key3 = </ code> is ignored.
*
* @param keyPrefix In the output of the Map Key plus a unified prefix.
* @param query Query String,For example: <code>key1=value1&key2=value2</code>
* @return When Query String is <code>key1=value1&key2=value2</code>, and prefix is <code>pre.</code>,
* then <code>Map{pre.key1=value1, pre.key=value2}</code> will be returned.
*/
// FIXME Is it reasonable to throw an IllegalStateException??
public static Map<String, String> parseQuery(String keyPrefix, String query) {
if (query == null)
return new HashMap<String, String>();
if (keyPrefix == null)
keyPrefix = "";

Matcher matcher = QUERY_PATTERN.matcher(query);
Map<String, String> routeQuery = new HashMap<String, String>();
String key = null;
while (matcher.find()) { // Match one by one
String separator = matcher.group(1);
String content = matcher.group(2);
if (separator == null || separator.length() == 0
|| "&".equals(separator)) {
if (key != null)
throw new IllegalStateException("Illegal query string \""
+ query + "\", The error char '" + separator
+ "' at index " + matcher.start() + " before \""
+ content + "\".");
key = content;
} else if ("=".equals(separator)) {
if (key == null)
throw new IllegalStateException("Illegal query string \""
+ query + "\", The error char '" + separator
+ "' at index " + matcher.start() + " before \""
+ content + "\".");
routeQuery.put(keyPrefix + key, content);
key = null;
} else {
if (key == null)
throw new IllegalStateException("Illegal query string \""
+ query + "\", The error char '" + separator
+ "' at index " + matcher.start() + " before \""
+ content + "\".");
}
}
/*if (key != null)
throw new IllegalStateException("Illegal route rule \"" + query
+ "\", The error in the end char: " + key);*/
return routeQuery;
}

可以看到上面有正则表达式的用法,主要是用来解析 URL(application=xxx&category=consumers&check=false&group=&interface=xxx.xxx) 中的 Key 和 Value 的。它这里的正则表达式可以查阅出来是“([&=]?)\s*([^&=\s]+)”。这段代码的核心就是分析这个正则表达式的作用,主体部分是由两个括号组成的,上面的“matcher.group(1)”和“matcher.group(2)”就是用来取这两个括号中匹配到的内容,第一个括号中表示的内容是匹配“&”、“=”零次或者一次,第二个括号表示匹配非“&”、“=”、“\s”符号的文本。

根据上面分析的语法规则,第一次匹配到的是“application”,第二次匹配到的是“=xxx”,第三次匹配到的是“&category”,第四次匹配到的是“=consumers”……,下面根据正则表达式测试网站给出完整的匹配结果:

1
2
3
4
5
6
7
8
9
10
共找到 9 处匹配:
application
=xxx
&category
=consumers
&check
=false
&group
&interface
=xxx.xxx

知道匹配的结果之后再来看看哪些地方会抛异常,上述代码有三个地方会抛出异常,第一处是“key”不为空的时候,正常情况下如果匹配到等号后面的值,“key”就会被置空。但上面有一种情况就是“&group=”后面没有值,“&group”后面就直接匹配到了“&interface”,如果连续两次匹配到的是“&xxx”,就会出现“key”没有被置空的情况,上面的异常就是因为这种问题产生的。第二处抛异常是匹配到了 Value,但是没有匹配到 Key,比如漏掉了“&xxx”,连续两次出现了“=xxx”。第三种异常还没想到什么情况会遇到,可能永远不会遇到,因为根据表达式的含义“separator”能出现的所有情况都在前面进行处理了。

解决办法

这个异常很明显可以看出是 dubbo-admin 和 dubbo 关于参数校验的标准不一样造成的,正常情况下 key 和 value 应该成对出现,我这里为了不想程序报错,允许了这种非正常参数的出现。我这里遇到不成对出现的场景是 node 应用也要去调用 dubbo 的接口,不清楚 node 那边是如何往 zookeeper 写数据的,后续看下 java 中的 dubbo 对于这种非正常的数据是如何处理的。

如果觉得我的文章对您有用,请查看广告(Google Ads)或扫码。您的支持将鼓励我继续创作!