Spring Boot 3.4.1请求大小过滤器触发后连接拒绝问题
问题描述
环境:Spring Boot 3.4.1、Java 21
我编写了一个RequestSizeFilter过滤器,用于检查请求大小是否超过可配置的最大值,超出则返回413状态码。过滤器代码如下:
@Component @Order(Ordered.HIGHEST_PRECEDENCE) @Slf4j public class RequestSizeFilter implements Filter { @Value("${fam.max_request_size:10485760}") private int maxContentBytes; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException { try { if (request.getContentLength() > maxContentBytes) { throw new RuntimeException(); } else { chain.doFilter(request, response); } } catch (Exception e) { log.error("Detected overlarge request of %s bytes. Rejecting.".formatted(request.getContentLength())); HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setStatus(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE); httpResponse.getWriter().println("Request exceeds maximum size of %s bytes".formatted(maxContentBytes)); } } }
运行端到端集成测试时,过滤器能正确识别超大请求并返回对应状态和错误信息,但触发拦截后,后续所有访问该端点的测试均失败,报错如下:
org.springframework.web.reactive.function.client.WebClientRequestException: Connection refused: localhost/[0:0:0:0:0:0:0:1]:8080 at app//org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException$9(ExchangeFunctions.java:137) Caused by: io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: localhost/[0:0:0:0:0:0:0:1]:8080 Caused by: java.net.ConnectException: Connection refused at java.base/sun.nio.ch.Net.pollConnect(Native Method) at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:682) at java.base/sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:973) at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:336) at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:339) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:776) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Thread.java:1583)
测试使用基于Spring WebTestClient自定义的客户端,仅配置了基础URL和默认授权头。禁用触发过滤器的测试后,其他测试均可通过;且测试顺序不固定时,同一测试在触发过滤器前运行通过,触发后则失败。推测过滤器导致服务器状态不稳定,想问需要执行什么操作让服务恢复接受连接的状态?
问题分析与修复方案
问题根源
过滤器存在两个核心问题,导致服务器连接异常:
- 未处理请求体读取:请求超限时直接返回响应,但未读取并丢弃请求体。Servlet容器会认为连接仍在处理请求,导致后续请求无法复用连接,甚至引发连接资源泄漏。
- 异常处理不规范:直接抛出
RuntimeException干扰容器正常流程,且未确保响应被正确提交,进一步加剧连接状态异常。
修复步骤
无需重置服务,修改过滤器代码即可解决问题:
@Component @Order(Ordered.HIGHEST_PRECEDENCE) @Slf4j public class RequestSizeFilter implements Filter { @Value("${fam.max_request_size:10485760}") private int maxContentBytes; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; int contentLength = httpRequest.getContentLength(); if (contentLength > maxContentBytes) { // 读取并丢弃请求体,释放连接资源 try (InputStream inputStream = httpRequest.getInputStream()) { byte[] buffer = new byte[1024]; while (inputStream.read(buffer) != -1) { // 丢弃读取内容 } } catch (IOException e) { log.warn("Failed to read oversized request body", e); } // 返回413响应并确保提交 log.error("Detected overlarge request of {} bytes. Rejecting.", contentLength); httpResponse.setStatus(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE); httpResponse.setContentType("text/plain"); httpResponse.getWriter().println("Request exceeds maximum size of {} bytes".formatted(maxContentBytes)); httpResponse.flushBuffer(); return; } // 请求大小合规,继续执行过滤器链 chain.doFilter(request, response); } }
关键改进点
- 读取并丢弃请求体:确保Servlet容器认为请求已处理完成,避免连接资源被占用。
- 移除不必要的异常抛出:直接处理超限逻辑,不干扰容器正常流程。
- 强制提交响应:调用
flushBuffer()确保响应完全发送,释放连接供后续请求使用。
额外测试建议
若修改后仍有问题,可检查:
- 测试类是否添加
@DirtiesContext注解:如果测试会改变应用状态,该注解可在测试后重置Spring上下文(优先修复过滤器代码,此为兜底方案)。 - WebTestClient配置:确保客户端每次请求后正确释放连接。
内容的提问来源于stack exchange,提问作者Steve Gazzo
相关产品推荐
相关产品推荐

