添加更多控制器时,Spring mvc性能会显着下降

Vic*_*aci 8 java performance spring spring-mvc

在我提出问题之前,请注意实际数字不代表性能.重要的是它们相对于彼此的价值以及我在运行之间获得一致数字(当然在小范围内)的事实.

所以,我在Jetty上运行以下Spring启动应用程序:

// Application.java
package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
Run Code Online (Sandbox Code Playgroud)
// HelloController1.java
package hello;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/v1/foo/{foo}/bar/{bar}/dog")
@RestController
public class HelloController1 {
    @RequestMapping(method = RequestMethod.GET)
    @ResponseBody
    public ResponseEntity<String> m() {
        return ResponseEntity.ok("dog");
    }
}
Run Code Online (Sandbox Code Playgroud)

使用这些Maven依赖项:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>LATEST</version>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jetty</artifactId>
        <version>LATEST</version>
    </dependency>
</dependencies>
Run Code Online (Sandbox Code Playgroud)

唯一的配置属性集是: logging.level.org.springframework.web=ERROR


我用wrk以下方法对应用程序进

wrk -t 10 -c 10 -d 40s http://localhost:8080/v1/foo/foo/bar/bar/dog
Run Code Online (Sandbox Code Playgroud)

我每秒得到一致的45K请求.

现在,我添加了4个控制器,与上面的控制器相同,但类名和请求路径除外:

  1. HelloController2 - > /v1/foo/{foo}/bar/{bar}/giraffe
  2. HelloController3 - > /v1/foo/{foo}/bar/{bar}/cat
  3. HelloController4 - > /v1/foo/{foo}/bar/{bar}/tacocat
  4. HelloController5 - > /v1/foo/{foo}/bar/{bar}/parrot

现在,如果我再次运行测试,我平均只能获得35K RPS.

我希望第二个测试能够获得更低的值,特别是因为这5个路由共享相同的前缀 - 我可以想象一些实现可能会比其他实现更差 - 但20%似乎很多.

在我开始挖掘spring(boot)源代码之前,我希望也许有人在这里,熟悉路由器/匹配器的实现,并可以弄清楚发生了什么.为什么这么大的RPS差异?


看一下Spring源代码后编辑.如果我错了,请纠正我:

似乎路径路径匹配器对每个请求的所有可用路径路径进行线性搜索.对于每个路径路径,如果它没有模式,它只是进行字典查找,一切都很好.然而,它的路径具有参数(如{foo}{bar}在我的例子),它有去AntMatcher,以匹配实际路径.匹配器执行从左到右搜索,因此一旦找到不匹配的内容就会失败.由于我的所有路由都以相同的模式开始,因此必须对每个路由进行大量工作,因此在添加更多路由时性能会变差.

顺便说一句,我愿意接受如何使这项表现更好的建议.我们的申请因此而受苦.

deF*_*tas 0

在不更改 Spring 代码的情况下,您可以做的就是重构您的代码,仅使用一个匹配所有有问题的模式的 RequestMapping,并自己编写一些高性能算法,将调用委托给正确的类并传递参数。

您可以使用if树和一些正则表达式来启动算法,不要忘记编译正则表达式并将其保存为常量,这样您就不会遇到性能问题