Spring MVC中SseEmitter调用curl出现transfer closed报错
解决SseEmitter使用中curl返回"transfer closed with outstanding read data remaining"的问题
我之前在做SSE相关的Spring MVC项目时,也碰到过一模一样的curl错误,咱们来拆解下你的代码问题:
问题根源分析
你的代码里有两个关键问题导致了这个错误:
- 线程池未正确关闭:用
Executors.newSingleThreadExecutor()创建的线程池会持有非守护线程,如果不手动关闭,这些线程会一直存活,导致容器无法正常回收连接资源,当SSE完成后连接被强制中断,curl就会抛出"transfer closed with outstanding read data remaining"。 - SSE消息发送不够规范:直接用
emitter.send()传字符串虽然能工作,但缺少SSE规范的消息结构,偶尔会导致客户端识别异常。
修复后的代码
@RequestMapping(path = "/finite/{count}", method = RequestMethod.GET) public SseEmitter getFiniteMessages(@PathVariable int count){ LOG.info("Request received for finite messages."); final SseEmitter emitter = new SseEmitter(); // 创建带命名的线程池,同时设置线程为守护线程 ExecutorService service = Executors.newSingleThreadExecutor(r -> { Thread workerThread = new Thread(r); workerThread.setName("SSE-Finite-Message-Worker"); workerThread.setDaemon(true); // 守护线程不会阻止JVM退出 return workerThread; }); service.execute(() -> { try { for (int i = 0; i < count; i++) { // 用Spring推荐的SseEmitter.event()构建规范的SSE消息 emitter.send(SseEmitter.event() .data(LocalTime.now().toString(), MediaType.TEXT_PLAIN)); Thread.sleep(200); } // 发送完成后正常结束Emitter emitter.complete(); } catch (Exception e) { LOG.error("Error sending SSE messages", e); emitter.completeWithError(e); } finally { // 无论成功失败,都关闭线程池释放资源 service.shutdown(); } }); // 添加超时处理,防止连接长时间闲置 emitter.onTimeout(() -> { LOG.warn("SSE connection timed out"); emitter.complete(); service.shutdownNow(); }); return emitter; }
关键修复点说明
- 守护线程设置:把工作线程设为守护线程,避免线程池残留的非守护线程阻止应用正常关闭或资源回收。
- 线程池手动关闭:在finally块中调用
service.shutdown(),确保任务执行完毕后回收线程池,避免线程泄漏。 - 规范SSE消息构建:使用
SseEmitter.event()方法构建消息,符合Server-Sent Events的标准格式,减少客户端解析问题。 - 超时处理:给Emitter添加超时回调,防止客户端异常断开后连接一直占用服务器资源。
测试建议
测试的时候可以给curl加上-N参数禁用缓冲,这样能实时看到每一条SSE消息:
curl -N -X GET http://localhost:8080/score/sse/finite/4
内容的提问来源于stack exchange,提问作者sorabh solanki
相关产品推荐
相关产品推荐

