【问题标题】:Java URI regexp takes too longJava URI 正则表达式耗时过长
【发布时间】:2011-08-29 20:23:12
【问题描述】:

我的 Java 应用程序中有一个 servlet 过滤器,以确保用户使用最新的文章和类别 URI。问题是,根据分析器的结果,这个过滤器需要(自己)大约 40% 的请求总时间(即使是简单的 URI“/”)(内部操作是不平凡的,它的动态网页带有巨大的菜单,文章排名等)。

public class NameFilter implements Filter {

    private ArticleServiceIface articleService;
    private CategoryServiceIface categoryService;
    private UrlRewriteServiceIface urlRewriteService;
    private Pattern pattern = Pattern.compile("^(?>.*?)/(article|category)/(\\d+)/(?>.*)$");

    public void init(FilterConfig filterConfig) throws ServletException {
        ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(filterConfig.getServletContext());
        articleService = (ArticleServiceIface) ctx.getBean("articleService");
        categoryService = (CategoryServiceIface) ctx.getBean("categoryService");
        urlRewriteService = (UrlRewriteServiceIface) ctx.getBean("urlRewriteService");
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        String uri = ((HttpServletRequest) request).getRequestURI();
        Matcher matcher = pattern.matcher(uri);
        String currUri;
        if (matcher.matches()) {
            if (matcher.group(1).equals("article")) {
                Long articleId = Long.valueOf(matcher.group(2));

                ArticleDTO a = articleService.getById(articleId);
                currUri = urlRewriteService.getUrl(a.getId());
            } else {
                Long categoryId = Long.valueOf(matcher.group(2));

                CategoryDTO c = categoryService.getById(categoryId);
                currUri = urlRewriteService.getCategoryUrl(c.getId());
            }
        } else { //does not match neighter article nor category
            chain.doFilter(request, response);
            return;
        }
        if (currUri.equals(uri)) {
            chain.doFilter(request, response);
        } else {
            HttpServletResponse res = (HttpServletResponse) response;
            res.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
            res.setHeader("Location", currUri);
            res.getWriter().close();
        }


    }

    public void destroy() {
    }
}

我花了几个小时调试和分析它,尝试了许多不同的方法来制定正则表达式,但结果总是一样的。

瓶颈似乎在matches方法中,它被递归调用,在某些时候它出于某种原因迭代地调用模式匹配(几千次)......

感谢您的任何建议。

编辑:Profiler results(对我来说似乎很奇怪...根据调试器,这应该是解析 URI == "/" )


EDIT2:当前正则表达式:

 private static Pattern pattern = Pattern.compile(".*?/(article|category)/(\\d+)/.*");

结果还是一样。我会尝试用

来衡量它
  System.out.print(System.currTimeMillis - time)

EDIT3:结论:它可能是 netbeans profiler 错误...

我试过这段代码和URI“/”

    long time = System.currentTimeMillis();
    if (matcher.matches()) {
        if (matcher.group(1).equals("article")) {
            Long articleId = Long.valueOf(matcher.group(2));

            ArticleDTO a = articleService.getById(articleId);
            currUri = urlRewriteService.getUrl(a.getId());
        } else {
            Long categoryId = Long.valueOf(matcher.group(2));

            CategoryDTO c = categoryService.getById(categoryId);
            currUri = urlRewriteService.getCategoryUrl(c.getId());
        }
    } else { //does not match neighter article nor category
        System.out.println(System.currentTimeMillis() - time);
        ....

输出始终为 0。因此在我看来,netbeans 分析器出于某种原因正在为该方法添加时间。

但是感谢大家的帮助和合作,我已经学会了一些正则表达式技巧。

【问题讨论】:

  • 您能否提供一些需要很长时间的示例 URI?我针对/ 尝试了您的模式,速度非常快。
  • 打印 uri。是你想的那样吗?顺便说一句,你能解释一下有效的 url:s 是什么样的吗? /a/b/article/666/hello.txt 有效吗?
  • @ccoakley:在这个正则表达式/(article|category)/(\\d+)/ 中,两个组都在捕获组,正如您在我的回答中看到的那样。要使它们成为非捕获组,请像这样使用它:/(?:article|category)/(?:\\d+)/
  • @ccoakley:我理解你的意思,但由于 OP 试图匹配 URIs /en/article/123/articleName 和 /article/123/articleName 因此 [^/]* 在这里不起作用。如果不是这样,我也会使用[^/]*,我认为它比.*?更好
  • @anubhava:谢谢。我的困惑实际上源于代码中组的原始编号(2,3 vs 1,2)。他们已经被纠正了(或者我疯了),这就是我认为事情应该是的样子。这就是为什么我被自己绊倒了。但是,是的,我的 [^/]* 建议不好。

标签: java regex filter


【解决方案1】:

实际上没有必要在你的模式中使用 Lookbehinds。以下代码适用于我并且在相当快的时间内:

long l = System.currentTimeMillis();
Pattern p = Pattern.compile("^.*?/(article|category)/(\\d+)/.*$");
Matcher m = p.matcher("/category/1012/Grafy");
System.out.println("Matches: " + m.matches());
System.out.println("Group1: " + m.group(1) + ", Group2: " + m.group(2));
System.out.println("Time taken: " + (System.currentTimeMillis()-l));

输出

Matches: true
Group1: category, Group2: 1012
Time taken: 0

编辑像这样尝试 find() 而不是 matches():

long l = System.currentTimeMillis();
p = Pattern.compile("/(article|category)/(\\d+)/");
m = p.matcher("/en/article/123/articleName");
System.out.println("Matches: " + m.find());
System.out.println("Group1: " + m.group(1) + ", Group2: " + m.group(2));
System.out.println("Time taken: " + (System.currentTimeMillis()-l));

【讨论】:

  • 我的初始代码看起来几乎一样。我已经测试了您的正则表达式,分析器的结果如前所述 - 大约需要 40% 的时间。有趣的是,如果我循环执行此操作,我可能会在一秒钟内检查大约 300 万个模式。所以接下来我要尝试的是性能是否不受分析器的影响。
  • 嗯,好的,还有 1 条建议。为此在 EDIT 部分添加了代码。如果您只关心 group1 和 group2,为什么还要尝试匹配完整的 UR。所以我修改了正则表达式并使用了 find() 方法,你能检查一下吗?
  • 结果几乎相同。我已经编辑了我的问题,看来这整件事源于一些错误或我对我正在使用的分析器的误读......但感谢您的帮助和建议。
猜你喜欢
  • 2013-10-09
  • 1970-01-01
  • 1970-01-01
  • 2015-12-29
  • 2014-11-25
  • 1970-01-01
  • 1970-01-01
  • 2010-09-07
  • 1970-01-01
相关资源
最近更新 更多