即使提供了请求参数,也会间歇性地抛出MissingServlet RequestParameterException



我有一个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端点,同时在多个控制器上调用我的应用程序定义的其他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]

(CommonsRequestLoggingFilterDEBUG日志行来自我根据此文档定义的bean;我很好奇所需的timestamp参数是否真的被定义了,这就是我添加它的原因。(

此外,当抛出这些错误的MissingServletRequestParameterException异常时,响应是一个400坏请求。我已经从客户端确认了timestamp确实被包含为请求参数,Spring Boot应用程序日志似乎也证实了这一点,但我在高负载下偶尔会看到这些异常。

怎么回事?我是否达到了Tomcat定义的某种连接或线程限制?据我所知,该应用程序在内存方面有足够的额外空间。

提前感谢任何人能提供的任何帮助!

作为参考,以下是我发现的一些明显类似的问题:

  • 是否存在QueryString但HttpServlet请求.getParameterMap((为空的情况

在阅读了这篇博客文章后,我相信我已经弄清楚了发生了什么:我有另一个过滤器PublicApiFilter在一组单独的API端点上运行,该端点异步调用一个函数,在该函数中,我将请求对象,即HttpServletRequest的实例传递给它,并调用它提供的各种方法。这些请求上的异步操作似乎会影响后续请求,甚至是对PublicApiFilter未涵盖的其他API端点的请求。通过删除我使用的@Async注释,我可以简单地使此函数的调用同步而不是异步,现在问题似乎已经解决了!

以下是我的一些代码片段,以防有朝一日对其他人有用:

@EnableScheduling
@SpringBootApplication // same as @Configuration @EnableAutoConfiguration @ComponentScan
@EnableAsync
public class Application implements WebMvcConfigurer, AsyncConfigurer {
// ...
@Override  // AsyncConfigurer
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(1);
executor.setQueueCapacity(1);
executor.setThreadNamePrefix("Async-");
executor.initialize();
return executor;
}
@Override  // AsyncConfigurer
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
@Component
public class PublicApiFilter extends GenericFilterBean {
private final PublicApiService publicApiService;
@Autowired
public PublicApiFilter(PublicApiService publicApiService) {
this.publicApiService = publicApiService;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// ...
chain.doFilter(request, response);
this.publicApiService.logRequest(httpRequest);
}
}
@Service
public class PublicApiService {
// ...
@Async  // <- simply removing this annotation appears to have done the trick!
public void logRequest(HttpServletRequest request) {
// invoke request.getRequestURI(), request.getHeader(...), request.getRemoteAddr, and request.getParameterMap() for logging purposes
}
}

不要将HttpServlet请求传递到任何异步方法中

解决上述问题的必读书目:

  1. 永远不要将请求传递给异步线程!有坑
  2. 如何在springboot中正确使用异步线程中的请求
  3. 偶尔的MissingServlet请求参数异常,谁移动了我的参数

相关内容

  • 没有找到相关文章

最新更新