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

Java 添加更多控制器时,Spring mvc性能会显著下降,java,spring,performance,spring-mvc,Java,Spring,Performance,Spring Mvc,在我提问之前,请注意,实际数字并不代表绩效。重要的是它们彼此之间的相对价值,以及我在两次跑步之间得到一致的数字(当然是在一个小范围内) 因此,我在Jetty上运行了以下spring boot应用程序: // Application.java package hello; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplic

在我提问之前,请注意,实际数字并不代表绩效。重要的是它们彼此之间的相对价值,以及我在两次跑步之间得到一致的数字(当然是在一个小范围内)

因此,我在Jetty上运行了以下spring boot应用程序:

// 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);
    }
}

我每秒收到45K个请求

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

  • HelloController2
    ->
    /v1/foo/{foo}/bar/{bar}/giraffe
  • HelloController3
    ->
    /v1/foo/{foo}/bar/{bar}/cat
  • HelloController4
    ->
    /v1/foo/{foo}/bar/{bar}/tacocate
  • HelloController5
    ->
    /v1/foo/{foo}/bar/{bar}/parrot
  • 现在,如果我再次运行测试,平均只能得到35K RPM

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

    在我开始钻研spring(boot)源代码之前,我希望这里可能有人熟悉路由器/匹配器的实现,并能弄清楚发生了什么。为什么RPS差异如此之大


    浏览Spring源代码后进行编辑。如果我错了,请纠正我:

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


    顺便说一句,我愿意接受关于如何使这项工作做得更好的建议。我们的应用程序正因此而受到影响。

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


    您可以使用if树和一些正则表达式启动算法,不要忘记编译正则表达式并将其保存为常量,这样您就不会有了。

    您的编辑听起来差不多正确。在这方面有一些改进;看见
    // 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");
        }
    }
    
    <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>
    
    wrk -t 10 -c 10 -d 40s http://localhost:8080/v1/foo/foo/bar/bar/dog