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

221 阅读4分钟
原文链接: www.itgrocery.cn

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

问题表象

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


                                                        
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”方法。

问题分析

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


                                                        
private static Pattern QUERY_PATTERN = Pattern
            .compile("([&=]?)\\s*([^&=\\s]+)");

 

                                                        
/**
     * 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”……,下面根据正则表达式测试网站给出完整的匹配结果:


                                                        
共找到 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 对于这种非正常的数据是如何处理的。