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

Golang服务端优雅终止长连接方案求助(避免中断请求)

解决方案:基于连接状态的优雅长连接关闭方案

你的核心问题是当前的定时关闭逻辑没有区分连接是否正在处理请求,直接粗暴关闭导致请求中断。解决思路是仅在连接处于空闲状态时执行关闭操作,通过跟踪http.ConnState的状态变化,动态控制超时计时器的启停,确保正在处理的请求不受影响。

改造后的连接监视器实现

import (
    "net"
    "net/http"
    "sync"
    "time"
)

type ConnectionWatcher struct {
    mu            sync.Mutex
    connections   map[net.Conn]*connection
    idleTimeout   time.Duration  // 连接空闲超时时间
    maxLifetime   time.Duration  // 连接最大生命周期(可选)
}

func NewConnectionWatcher(idleTimeout, maxLifetime time.Duration) *ConnectionWatcher {
    return &ConnectionWatcher{
        mu:          sync.Mutex{},
        connections: make(map[net.Conn]*connection),
        idleTimeout: idleTimeout,
        maxLifetime: maxLifetime,
    }
}

type connection struct {
    conn        net.Conn
    timer       *time.Timer
    mu          sync.Mutex
    idleTimeout time.Duration
    createTime  time.Time
    maxLifetime time.Duration
}

// kill 安全关闭连接并清理资源,避免重复关闭
func (c *connection) kill() {
    c.mu.Lock()
    defer c.mu.Unlock()
    
    if c.conn != nil {
        _ = c.conn.Close()
        c.conn = nil
    }
    if c.timer != nil {
        c.timer.Stop()
        c.timer = nil
    }
}

// onActive 连接进入请求处理状态,停止超时计时器
func (c *connection) onActive() {
    c.mu.Lock()
    defer c.mu.Unlock()
    
    if c.timer != nil {
        c.timer.Stop()
        c.timer = nil
    }
}

// onIdle 连接进入空闲状态,启动超时检查
func (c *connection) onIdle() {
    c.mu.Lock()
    defer c.mu.Unlock()

    // 先检查是否超过最大生命周期,是则直接关闭
    if time.Since(c.createTime) >= c.maxLifetime {
        c.kill()
        return
    }

    // 停止旧计时器并启动新的空闲超时计时器
    if c.timer != nil {
        c.timer.Stop()
    }
    c.timer = time.AfterFunc(c.idleTimeout, c.kill)
}

func (cw *ConnectionWatcher) OnStateChange(conn net.Conn, state http.ConnState) {
    cw.mu.Lock()
    c, exists := cw.connections[conn]
    cw.mu.Unlock()

    switch state {
    case http.StateNew:
        // 初始化新连接监控
        newConn := &connection{
            conn:        conn,
            idleTimeout: cw.idleTimeout,
            createTime:  time.Now(),
            maxLifetime: cw.maxLifetime,
        }
        cw.mu.Lock()
        cw.connections[conn] = newConn
        cw.mu.Unlock()
    case http.StateActive:
        // 连接正在处理请求,停止超时计时器
        if exists {
            c.onActive()
        }
    case http.StateIdle:
        // 连接进入空闲状态,启动超时检查
        if exists {
            c.onIdle()
        }
    case http.StateHijacked, http.StateClosed:
        // 连接被劫持或关闭,清理资源
        if exists {
            c.kill()
            cw.mu.Lock()
            delete(cw.connections, conn)
            cw.mu.Unlock()
        }
    }
}

服务器端注册与配置

func main() {
    // 配置:空闲10秒后关闭,连接最长存活30分钟
    cw := NewConnectionWatcher(10*time.Second, 30*time.Minute)
    r := mux.NewRouter()
    r.HandleFunc("/test", ServeHTTP).Methods(http.MethodGet)
    
    server := http.Server{
        Addr:            ":9007",
        Handler:         r,
        ConnState:       cw.OnStateChange,
        IdleConnTimeout: 60 * time.Second, // 保留全局兜底超时
    }
    log.Fatal(server.ListenAndServe())
}

方案优势

  1. 优雅关闭不中断请求:仅在连接处于StateIdle状态时执行关闭操作,完全不会影响正在处理的请求。
  2. 双重超时控制:
    • 空闲超时:连接空闲指定时间后自动关闭,避免长期占用资源
    • 最大生命周期:确保连接不会永久存活,强制客户端定期重新拨号以刷新服务发现结果
  3. 并发安全:通过多层互斥锁保证连接状态和计时器操作的线程安全,避免竞态条件。
  4. 资源自动清理:连接关闭或被劫持时自动清理映射和计时器,无内存泄漏风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 19:05:29