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

Go应用捕获CTRL-C中断时返回HTTP内部错误响应的实现疑问

解决方案:在Go HTTP服务中优雅中断并返回错误给未完成请求

首先明确核心限制:信号处理函数无法直接访问正在处理的请求的http.ResponseWriter——每个请求的Writer都是独立goroutine的局部变量,信号处理goroutine无法直接遍历或获取这些实例。正确的实现思路是通过全局状态标记+请求处理流程中的主动检查来达成需求。

步骤1:定义并发安全的全局关闭标记

用原子布尔值标记服务是否进入关闭状态,确保多goroutine下的状态读写安全:

import (
    "sync/atomic"
    "net/http"
    "os"
    "os/signal"
    "syscall"
)

var shuttingDown atomic.Bool

步骤2:捕获中断信号并触发关闭标记

在服务启动阶段注册信号监听,收到CTRL-C(SIGINT)或终止信号(SIGTERM)时,将全局标记设为true:

func main() {
    // 注册信号监听
    sigChan := make(chan os.Signal, 1)
    signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
    go func() {
        <-sigChan
        shuttingDown.Store(true)
        // 可选:停止接收新请求,需先手动获取listener
        // listener, _ := net.Listen("tcp", ":8080")
        // listener.Close()
    }()

    // 启动HTTP服务
    http.HandleFunc("/", requestHandler)
    http.ListenAndServe(":8080", nil)
}

步骤3:在请求处理中检查关闭状态

在每个请求的处理逻辑中,关键节点(比如处理开始、数据库操作前)主动检查全局标记,若已触发关闭则直接返回"internal error":

func requestHandler(w http.ResponseWriter, r *http.Request) {
    // 处理前先检查是否正在关闭
    if shuttingDown.Load() {
        w.WriteHeader(http.StatusInternalServerError)
        w.Write([]byte("internal error"))
        return
    }

    // 模拟耗时业务逻辑(如数据库查询)
    // 可在逻辑中间再次检查状态,避免长时间等待后才返回错误
    // if shuttingDown.Load() { ... 返回错误 ... }

    // 正常处理流程
    w.Write([]byte("request processed"))
}

进阶:用Context传递精细化关闭信号

如果需要让正在执行的下游操作(如数据库查询)提前终止,可以结合context.WithCancel传递关闭信号:

import "context"

var shutdownCtx, shutdownCancel = context.WithCancel(context.Background())

func main() {
    sigChan := make(chan os.Signal, 1)
    signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
    go func() {
        <-sigChan
        shutdownCancel()
        shuttingDown.Store(true)
    }()

    http.HandleFunc("/", requestHandler)
    http.ListenAndServe(":8080", nil)
}

func requestHandler(w http.ResponseWriter, r *http.Request) {
    select {
    case <-shutdownCtx.Done():
        w.WriteHeader(http.StatusInternalServerError)
        w.Write([]byte("internal error"))
        return
    default:
    }

    // 将shutdownCtx传递给下游函数,触发提前终止
    // db.QueryContext(shutdownCtx, "SELECT * FROM ...")

    w.Write([]byte("request processed"))
}

注意事项

  • 信号处理函数必须轻量化,禁止在其中执行阻塞操作或访问请求相关资源。
  • 若要完全停止接收新请求,需手动创建并管理HTTP listener,在信号处理中调用listener.Close()。
  • 针对长时间运行的请求,建议在多个关键检查点判断关闭状态,避免用户等待过久才收到错误响应。

内容的提问来源于stack exchange,提问作者sid

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 12:50:52