JavaScript的Abortable Fetch是否关闭HTTP连接?服务端如何感知该事件?
fetch abort的HTTP连接行为
主流浏览器执行fetch的abort操作时,会直接终止对应请求的传输链路:
- HTTP/1.1场景下会直接关闭对应的TCP连接,服务端会收到连接断开的信号
- HTTP/2场景下会发送RST_STREAM帧终止当前请求对应的流,不会影响同连接上的其他请求,服务端同样能感知到该请求的终止信号
不存在浏览器默默保留连接等响应回来再丢弃的情况,服务端可以拿到这个终止事件。
Jersey/Grizzly框架感知请求中止的实现方案
你可以通过Grizzly原生请求对象的生命周期监听器来感知fetch终止事件,进而终止正在执行的高成本数据库查询,推荐配合Jersey的异步端点实现,示例代码如下:
import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.container.AsyncResponse; import javax.ws.rs.container.Suspended; import javax.ws.rs.core.Context; import org.glassfish.grizzly.http.server.Request; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; @Path("/query") public class HeavyQueryResource { // 自行初始化业务线程池,不要用Jersey默认的IO线程池执行长耗时任务 private final ThreadPoolExecutor businessThreadPool = initThreadPool(); private final DatabaseService databaseService = initDatabaseService(); @GET public void executeHeavyQuery(@Suspended AsyncResponse asyncResponse, @Context Request grizzlyNativeRequest) { // 提交数据库查询任务,拿到Future对象用于后续取消 Future<?> queryFuture = businessThreadPool.submit(() -> { try { Object queryResult = databaseService.runCostlyQuery(); // 查询完成后返回结果 asyncResponse.resume(queryResult); } catch (InterruptedException e) { // 任务被中断,直接终止,无需返回结果 asyncResponse.cancel(); } catch (Exception e) { asyncResponse.resume(e); } }); // 注册请求生命周期监听器,感知客户端断开事件 grizzlyNativeRequest.addLifecycleListener(lifecycleEvent -> { // 请求被取消或提前结束时触发 if (lifecycleEvent == Request.LifecycleEvent.CANCELLED || lifecycleEvent == Request.LifecycleEvent.COMPLETED) { if (!queryFuture.isDone()) { // 中断正在运行的查询任务 queryFuture.cancel(true); // 额外补充数据库查询的资源清理逻辑(如有需要) databaseService.cleanupQueryResources(queryFuture); } } }); } }
注意事项
- 你的数据库查询逻辑需要支持线程中断,主流关系型数据库的JDBC驱动都默认支持线程中断触发查询终止,少数特殊数据库需要额外配置
- 如果使用同步端点实现,可以在查询逻辑中周期性尝试往响应输出流写入空的心跳数据,一旦抛出
Broken pipe或ClosedChannelException即可判定客户端已断开,终止查询即可 - Grizzly 2.4及以上版本完整支持HTTP/2场景下的请求终止感知,无需额外适配
内容的提问来源于stack exchange,提问作者Andrew Rueckert
相关产品推荐
相关产品推荐

