Go语言中如何在用户断开连接时强制关闭HTTP请求连接?
如何在用户中断下载时关闭对目标服务器的HTTP连接?
你的问题很典型——当客户端中途断开下载连接时,你的服务依然保持着对目标服务器的HTTP连接,导致资源浪费。这是因为默认的http.Get请求没有绑定可取消的上下文,而且客户端断开的信号没有传递到目标服务器的请求上。
问题根源
原来的代码里,当用户停止下载时,io.Copy会因为客户端连接关闭返回错误,但此时对目标服务器的GET请求还在后台运行:HTTP客户端不会主动取消请求,直到响应体被完全读取或者服务器超时,而resp.Body.Close()只是关闭本地的读取流,不会主动通知目标服务器终止连接,所以连接会被占用一段时间。
解决方案:用Context取消请求
Go的net/http包支持通过**上下文(Context)**来取消正在进行的HTTP请求。我们可以监听客户端的连接关闭事件,一旦触发就取消对目标服务器的请求,让连接立即释放。
修改后的核心代码如下:
import ( "context" "errors" "fmt" "io" "net/http" "strconv" ) // 假设这是你的处理函数 func yourDownloadHandler(w http.ResponseWriter, r *http.Request, vid /* 你的vid类型 */, format /* 你的format类型 */, url /* 你的url类型 */) { // 创建可取消的上下文,继承请求的上下文(比如处理超时等) ctx, cancel := context.WithCancel(r.Context()) defer cancel() // 函数结束时确保取消,避免资源泄漏 // 监听客户端连接关闭事件:当用户停止下载/关闭浏览器时,这个通道会收到信号 go func() { <-w.CloseNotify() cancel() // 触发取消,通知HTTP客户端终止对目标服务器的请求 }() // 创建带上下文的GET请求(替代原来的http.Get) req, err := http.NewRequestWithContext(ctx, "GET", url.String(), nil) if err != nil { fmt.Fprintln(w, "Failed to create request:", err.Error()) return } // 发送请求 resp, httpErr := http.DefaultClient.Do(req) if httpErr != nil { // 如果是因为客户端断开导致的取消错误,无需返回给用户 if errors.Is(httpErr, context.Canceled) { return } fmt.Fprintln(w, "Http Request Failed:", httpErr.Error()) return } defer resp.Body.Close() // 必须关闭响应体,防止资源泄漏 // 设置响应头(和原来的逻辑一致) w.Header().Set("Content-Disposition", "attachment; filename="+vid.Title+"."+format.Extension) w.Header().Set("Content-Type", resp.Header.Get("Content-Type")) w.Header().Set("Content-Length", strconv.Itoa(int(resp.ContentLength))) // 复制响应体到客户端,同时处理可能的错误(比如客户端断开) _, copyErr := io.Copy(w, resp.Body) if copyErr != nil { // 客户端断开时会走到这里,此时cancel已经调用,请求已被取消 return } }
关键细节说明
- 上下文绑定:使用
http.NewRequestWithContext替代http.Get,让请求和可取消的上下文绑定,这样调用cancel()时,HTTP客户端会主动关闭与目标服务器的连接。 - 监听连接关闭:
w.CloseNotify()返回一个通道,当客户端关闭连接时,通道会被触发。我们用goroutine监听这个通道,一旦触发就调用cancel()。 - 资源清理:
defer cancel()和defer resp.Body.Close()确保不管函数如何退出,资源都会被正确清理,避免泄漏。 - 错误处理:判断
httpErr是否为context.Canceled,这种情况是客户端主动断开,不需要给用户返回错误信息。
这样修改后,当用户停止下载时,你的服务会立即取消对目标服务器的请求,连接会被及时释放,不会再出现资源占用的问题。
内容的提问来源于stack exchange,提问作者Manish Champaneri
相关产品推荐
相关产品推荐

