You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 00:23:23