Spring REST服务执行重任务前检测客户端连接方案问询
检测Spring REST服务中客户端FIN包断开连接的实现方案
1. 基于Servlet容器API获取底层Socket状态
Spring依托Servlet容器(如Tomcat、Jetty)运行,可通过HttpServletRequest直接获取底层Socket连接,以此检测客户端是否发送了FIN包:
Tomcat适配示例
import org.apache.catalina.connector.Request; import org.apache.catalina.connector.RequestFacade; import java.lang.reflect.Field; import java.net.Socket; // 在控制器或拦截器中执行检测 public boolean checkClientDisconnected(HttpServletRequest request) throws Exception { RequestFacade requestFacade = (RequestFacade) request; // 通过反射获取Tomcat内部Request对象 Field requestField = RequestFacade.class.getDeclaredField("request"); requestField.setAccessible(true); Request tomcatRequest = (Request) requestField.get(requestFacade); // 获取底层Socket并检查输入流状态 Socket socket = tomcatRequest.getSocket().getSocket(); // available()返回-1表示输入流已因FIN包关闭 return socket.getInputStream().available() == -1; }
2. 封装可复用的检测工具类
为兼容多容器,可封装工具类适配不同Servlet实现:
import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Field; import java.net.Socket; public class ConnectionChecker { public static boolean isClientDisconnected(HttpServletRequest request) throws Exception { // Tomcat 处理逻辑 if (request instanceof RequestFacade) { RequestFacade facade = (RequestFacade) request; Field requestField = RequestFacade.class.getDeclaredField("request"); requestField.setAccessible(true); Request tomcatRequest = (Request) requestField.get(facade); Socket socket = tomcatRequest.getSocket().getSocket(); return socket.getInputStream().available() == -1; } // Jetty 处理逻辑示例 else if (request instanceof org.eclipse.jetty.server.Request) { org.eclipse.jetty.server.Request jettyRequest = (org.eclipse.jetty.server.Request) request; Socket socket = jettyRequest.getHttpChannel().getEndPoint().getSocket(); return socket.getInputStream().available() == -1; } // 其他容器可自行扩展适配 return false; } }
3. 在业务流程中集成检测
任务启动前单次检测
在控制器方法中,执行重计算前先验证连接状态:
@GetMapping("/heavy-task") public ResponseEntity<String> startHeavyTask(HttpServletRequest request) throws Exception { // 检测客户端是否已正常断开 if (ConnectionChecker.isClientDisconnected(request)) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("客户端已断开,终止任务"); } // 模拟前置阻塞逻辑 Thread.sleep(5000); // 可选:再次检测后启动任务 if (!ConnectionChecker.isClientDisconnected(request)) { String result = heavyTaskService.execute(); return ResponseEntity.ok(result); } else { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("客户端已断开,终止任务"); } }
任务执行中周期性检测
如果任务是异步执行,可将Socket对象传入任务线程,周期性检测:
import java.net.Socket; import java.util.concurrent.CompletableFuture; @Async public CompletableFuture<String> runAsyncHeavyTask(Socket socket) throws Exception { int totalSteps = 10; for (int step = 0; step < totalSteps; step++) { // 每10秒检测一次连接状态 Thread.sleep(10000); if (socket.getInputStream().available() == -1) { throw new RuntimeException("客户端已断开,终止计算"); } // 执行单步计算逻辑 doPartialCalculation(step); } return CompletableFuture.completedFuture("计算完成"); }
注意:异步任务中持有Socket需注意线程安全,避免内存泄漏,任务结束后及时释放资源。
4. 局限性说明
- 仅能检测客户端正常发送FIN包的场景,无法识别异常断开(如网络中断、客户端进程崩溃);
- 依赖具体Servlet容器的内部API,跨容器部署时需要针对性适配;
- Spring会在控制器处理前解析完请求流,因此通过请求流EOF判断无效,必须直接读取底层Socket的输入流状态。
内容的提问来源于stack exchange,提问作者Coco Liliace
相关产品推荐
相关产品推荐

