Spring Framework 6.0.23升级至6.1.0后出现ServletOutputStream写入失败问题
Spring 6.1升级后JBoss EAP 8中ServletOutputStream提前关闭(UT010029)问题分析
问题场景
项目基于JBoss EAP 8,将Spring Framework从6.0.23升级至6.1.0后,控制器出现UT010029: Stream is closed异常,日志显示根源为org.springframework.web.context.request.async.AsyncRequestNotUsableException,最终指向io.undertow.servlet.spec.ServletOutputStreamImpl写入时流已关闭。
为复现问题编写测试接口:
@RequestMapping(value = "/xxx/xxx", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") @ResponseBody public String xxx(HttpServletRequest request, HttpServletResponse response, @RequestBody String jsonText) throws Throwable { // 模拟长时间业务处理 Thread.sleep(100000); // ... 业务逻辑 return jsonTextRslt; }
测试结果对比:
- Spring 6.0版本:等待100秒后正常返回200 OK;
- Spring 6.1版本:请求处理到90秒时流被关闭,100秒后触发写入失败异常。
原因分析
- Undertow默认超时设置:JBoss EAP 8中Undertow子系统的
read-timeout默认值为90秒,该参数原本控制服务器读取客户端请求数据的超时,但在Spring 6.1的逻辑下,请求处理阶段的时长也会触发该检测——当服务器处理请求的时间超过90秒,Undertow会主动关闭连接流。 - Spring 6.1的逻辑调整:Spring Framework 6.1对请求生命周期管理做了优化,新增了对容器连接状态的严格检测。当Undertow因超时关闭流后,Spring会立即识别到请求已不可用,抛出
AsyncRequestNotUsableException;而Spring 6.0版本未实现该级别的检测,即使流已关闭,仍会尝试写入响应,最终侥幸完成返回(实际这是潜在的不稳定因素)。
解决方案合理性验证
通过修改JBoss的domain.xml或standalone.xml中Undertow子系统的http-listener,添加read-timeout="120000"(120秒)的方案是合理且符合规范的:
- 该配置直接覆盖Undertow的默认超时,确保业务最长处理时间(100秒)在超时范围内,避免流被提前关闭;
- 配置需根据实际业务场景调整:若存在更长时间的处理逻辑,需同步延长
read-timeout值,但不建议设置过长,避免闲置连接占用服务器资源; - 此方案本质是让容器的超时策略适配业务需求,而非修改Spring核心逻辑,属于容器层面的标准配置调整,无侵入性且易于维护。
内容的提问来源于stack exchange,提问作者Arteta
相关产品推荐
相关产品推荐

