已传请求参数仍间歇性抛出MissingServletRequestParameterException问题排查
问题描述
我有一个Spring Boot 2.7.3应用,定义了如下控制器:
@RestController @EnableAutoConfiguration public class TrainController { @CrossOrigin(origins = "http://localhost:3000") @RequestMapping(value = "/trains/history", method = RequestMethod.GET) public List<TrainStatus> getTrainStatusesForTimestamp( @RequestParam long timestamp ) { // do stuff } }
调用该API端点通常正常,本地运行无问题,但在生产环境高负载场景下(比如并行重复调用该接口,同时大量调用应用中多控制器的其他API),日志中出现如下信息:
2022-09-06 20:48:37.939 DEBUG 19282 --- [https-openssl-nio-443-exec-10] o.s.w.f.CommonsRequestLoggingFilter : Before request [GET /trains/history?timestamp=1662511707] 2022-09-06 20:48:37.945 WARN 19282 --- [https-openssl-nio-443-exec-10] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'timestamp' for method parameter type long is not present] 2022-09-06 20:48:37.945 DEBUG 19282 --- [https-openssl-nio-443-exec-10] o.s.w.f.CommonsRequestLoggingFilter : After request [GET /trains/history?timestamp=1662511707]
(上述CommonsRequestLoggingFilter的DEBUG日志来自我根据文档定义的Bean,用于确认必填的timestamp参数是否存在)
当抛出该异常时,响应为400 Bad Request。我已确认客户端确实携带了timestamp请求参数,Spring Boot应用日志也验证了这一点,但高负载下仍间歇性出现该异常。请问这是什么原因?是否触发了Tomcat的连接或线程限制?目前应用内存尚有充足余量。
原因分析
这种高负载下间歇性出现的参数丢失问题,并非Tomcat连接/线程限制直接导致,更可能是以下场景引发:
- Filter拦截器提前读取请求流:若应用中存在自定义或第三方Filter(如监控、安全组件),在高负载并发场景下,这些Filter可能提前调用
request.getParameter()或读取请求输入流。由于HttpServletRequest的输入流仅能读取一次,后续Spring MVC的参数解析器就无法正确获取参数,进而抛出缺失参数的异常。 - Tomcat参数解析并发漏洞:极端高负载下,Tomcat内部的参数解析逻辑可能出现线程安全问题,导致
parameterMap未正确填充查询参数。 - 请求流转异常:若请求经过内部转发/重定向且未正确传递查询参数,也可能出现该现象,但结合日志中明确显示查询字符串存在的情况,此场景概率极低。
解决方案
针对上述可能的原因,可按以下步骤排查修复:
排查并修复Filter逻辑:
- 检查所有注册的Filter,尤其是在
CommonsRequestLoggingFilter之前执行的Filter,确认是否存在提前读取请求参数或输入流的操作。 - 若存在此类操作,使用
ContentCachingRequestWrapper包装请求,让参数可被多次读取。示例代码:@Component public class CustomCachingFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); filterChain.doFilter(wrappedRequest, response); } }
- 检查所有注册的Filter,尤其是在
强制Tomcat提前解析参数:
- 在
application.properties中添加配置,让Tomcat在请求进入Servlet之前完成参数解析:
该配置可避免后续Spring MVC解析参数时出现异常。server.tomcat.force-parse-parameters=true
- 在
优化Tomcat线程池配置:
- 虽然不是直接诱因,但合理的线程池配置可避免高负载下的线程耗尽问题,参考配置:
server.tomcat.max-threads=200 server.tomcat.min-spare-threads=50
- 虽然不是直接诱因,但合理的线程池配置可避免高负载下的线程耗尽问题,参考配置:
内容的提问来源于Stack Exchange,提问作者James Pizzurro
相关产品推荐
相关产品推荐

