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

如何为HTTP服务器的中断信号(Ctrl+C)编写单元测试?

为HTTP服务器中断信号处理逻辑编写单元测试的方案

给定如下小型HTTP服务器实现,需要验证其中断信号处理逻辑的正确性:通过exitChannel <- syscall.SIGINT或syscall.Kill(syscall.Getpid(), syscall.SIGINT)传递中断信号,并确认服务器已完成关闭。

初始服务器实现代码

package main

import (
    "context"
    "fmt"
    "net"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"
)

func SimpleServer() {
    ctx := context.Background()
    server := http.Server{
        Addr:        ":8080",
        BaseContext: func(net.Listener) context.Context { return ctx },
    }

    http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "pong at ", time.Now().String())
    })

    go server.ListenAndServe()

    exitChannel := make(chan os.Signal, 1) // 缓冲大小设为1,避免通知器阻塞
    defer close(exitChannel)
    signal.Notify(exitChannel, os.Interrupt, syscall.SIGTERM) // Docker和Kubernetes通常用SIGTERM发送终止信号

    <-exitChannel

    fmt.Print("\n")
    fmt.Println("shutting down server...")

    err := server.Shutdown(ctx)
    fmt.Println(err)
}

可行解决方案代码

通过重构服务器代码,暴露信号触发通道并新增服务器关闭状态监听通道,即可实现测试需求:

func exampleCaller() {
    exitChannel, serverDownChannel := HTTPServer()

    fmt.Println("3秒后发送终止信号...")
    for i := 3; i > 0; i-- {
        fmt.Println(i)
        time.Sleep(1 * time.Second)
    }
    exitChannel <- syscall.SIGINT
    fmt.Println("终止信号已发送")

    <-serverDownChannel
    fmt.Println("服务器已完全关闭!")
}

func HTTPServer() (chan os.Signal, chan struct{}) {
    responseDelay := 1000 // 响应前的延迟毫秒数

    ctx := context.Background()
    server := http.Server{
        Addr:        ":8080",
        BaseContext: func(net.Listener) context.Context { return ctx },
    }

    http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
        time.Sleep(time.Duration(responseDelay) * time.Millisecond)
        fmt.Fprint(w, "pong at ", time.Now().String())
    })

    fmt.Println("创建通道...")
    exitChannel := make(chan os.Signal, 1)                    // 缓冲大小设为1,避免通知器阻塞
    serverDownChannel := make(chan struct{})                  // 用于通知服务器已关闭的信号通道
    signal.Notify(exitChannel, os.Interrupt, syscall.SIGTERM) // 监听中断和终止信号

    go func() {
        <-exitChannel // 等待中断信号
        fmt.Print("\n")
        fmt.Println("正在关闭服务器...")
        err := server.Shutdown(ctx)
        fmt.Println(err)
    }()

    go func() {
        server.ListenAndServe() // 启动HTTP服务器
        close(serverDownChannel) // 服务器停止后关闭通道,通知外部
    }()

    return exitChannel, serverDownChannel
}

方案说明

  • 重构后的HTTPServer函数返回两个核心通道:exitChannel用于手动发送中断信号,serverDownChannel用于监听服务器是否完全关闭。
  • 在测试逻辑中,通过向exitChannel发送syscall.SIGINT触发服务器关闭流程,随后阻塞等待serverDownChannel关闭,即可确认服务器已完成Shutdown操作。
  • 若使用syscall.Kill(syscall.Getpid(), syscall.SIGINT)替代通道发送信号,signal.Notify会自动捕捉该信号并传递给exitChannel,同样能触发完整的关闭流程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 09:03:19