在我提出问题之前,请注意实际数字并不代表性能。重要的是它们相对于彼此的价值以及我在两次运行之间获得一致数字(当然是小范围)这一事实。
所以,我在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);
}
}
// 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");
}
}
使用这些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>
唯一的配置属性集是:logging.level.org.springframework.web=ERROR
我使用wrk
:
wrk -t 10 -c 10 -d 40s http://localhost:8080/v1/foo/foo/bar/bar/dog
我每秒得到一致的45K请求。
现在,我添加了4个控制器,与上面的控制器相同,但类名和请求路径除外:
HelloController2
- &gt; /v1/foo/{foo}/bar/{bar}/giraffe
HelloController3
- &gt; /v1/foo/{foo}/bar/{bar}/cat
HelloController4
- &gt; /v1/foo/{foo}/bar/{bar}/tacocat
HelloController5
- &gt; /v1/foo/{foo}/bar/{bar}/parrot
现在,如果我再次运行测试,我平均只能获得35K的RPS。
我希望第二次测试得到更低的值,特别是因为这5条路由共享相同的前缀 - 我可以想象一些实现可能会比其他实现更差 - 但20%似乎很多。
在我开始挖掘spring(boot)源代码之前,我希望也许有人在这里,熟悉路由器/匹配器实现并且可以弄清楚发生了什么。为什么这么大的RPS差异?
在查看Spring源代码后编辑。如果我错了,请纠正我:
路径路径匹配器似乎对每个请求的所有可用路径路径进行线性搜索。对于每个路径路径,如果它没有模式,它只是进行字典查找,一切都很好。但是,路径中有参数(例如我的示例中为{foo}
和{bar}
),它必须转到AntMatcher
以匹配实际路径。匹配器执行从左到右搜索,因此一旦找到不匹配的内容就会失败。由于我的所有路由都以相同的模式开始,因此必须对每个路由进行大量工作,因此在添加更多路由时性能会变差。
顺便说一句,我愿意接受有关如何让这种表现更好的建议。我们的申请因此受到了影响。
答案 0 :(得分:0)
无需更改Spring代码,您可以做的就是重构代码,使其仅使用一个与所有有问题的模式匹配的RequestMapping,并自己编写一些性能算法,将调用委派给正确的类并传递参数。
您可以使用 if 树和一些正则表达式来启动算法,别忘了编译正则表达式并将其保存为常量,这样就不会使用performance issues。
答案 1 :(得分:0)
您可以通过以下几种方式表现得更好:
/v1/foo/{foo}/bar/{bar}/{animal}
并在控制器的每个方法中执行切换案例