一个springmvc使用不当引发的血案
2018-08-09 本文已影响171人
黄云斌huangyunbin
先说结论:是@FrameworkController的坑
一个同事和我反馈他们的health-check接口都很慢,在容器(2核)只有1000qps不到,这让我很想不通,health-check接口是没有任何逻辑的,直接返回一个ok而已
而且重点是这个health-check接口是公司的框架提供的,应该是没问题的啊
@FrameworkController
public class HealthCheckController {
@RequestMapping(value = "/_health_check", method = RequestMethod.GET)
public ResponseEntity<String> healthCheck() {
return new ResponseEntity<String>("ok", HttpStatus.OK);
}
}
然后我自己压测看了下,确实和他说的那样,1000qps左右。用jprofile看了下
image.pngcpu大部分都是在AccessController.doPrivileged这个方法上,这是个native方法
image.png看到调用链发现,springmvc做接口匹配的时候,会构造ProduceMediaTypeExpression,这个对象的log居然不是静态字段,每次都会创建。
image.pnggetFactory 会拿ClassLoader
image.png拿ClassLoader会做权限检查
image.png看起来一切事情都非常明白了,就是ProduceMediaTypeExpression比较傻逼,log这个字段居然都是不是静态字段。
但是health-check这种非常明确的接口,不应该做各种复杂的springmvc匹配逻辑啊。再次压测也注意到有匹配失败的
image.png
这个就有点想不通了,health-check是非常容易匹配的啊,然后我远程debug一下。发现是@FrameworkController的坑
image.png@FrameworkController是放在FrameworkControllerHandlerMapping的,这个maping的放在最后的。
开始要去RequestMappingHandlerMapping匹配,匹配逻辑是先从urlmap中看有不有,health_check不在urlmap中,然后就挨个尝试复杂匹配。都匹配不到才到FrameworkControllerHandlerMapping。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
List<T> directPathMatches = this.urlMap.get(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.handlerMethods.keySet(), matches, request);
}
if (!matches.isEmpty()) {
......
}
else {
return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
}
}