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

客户端向GraphQL订阅服务器发送关闭信号方式及ctx.Done()触发疑问

GraphQL订阅中ctx.Done()的触发场景解析

我正在开发一个GraphQL服务器,其中包含一个订阅API,以下是我在gqlgen文档中找到的示例代码:

// CurrentTime is the resolver for the currentTime field.
func (r *subscriptionResolver) CurrentTime(ctx context.Context) (<-chan *model.Time, error) {
    // First you'll need to `make()` your channel. Use your type here!
    ch := make(chan *model.Time)

    // You can (and probably should) handle your channels in a central place outside of `schema.resolvers.go`.
    // For this example we'll simply use a Goroutine with a simple loop.
    go func() {
        // Handle deregistration of the channel here. Note the `defer`
        defer close(ch)

        for {
            // In our example we'll send the current time every second.
            time.Sleep(1 * time.Second)
            fmt.Println("Tick")

            // Prepare your object.
            currentTime := time.Now()
            t := &model.Time{
                UnixTime:  int(currentTime.Unix()),
                TimeStamp: currentTime.Format(time.RFC3339),
            }

            // The subscription may have got closed due to the client disconnecting.
            // Hence we do send in a select block with a check for context cancellation.
            // This avoids goroutine getting blocked forever or panicking,
            select {
            case <-ctx.Done(): // This runs when context gets cancelled. Subscription closes.
                fmt.Println("Subscription Closed")
                // Handle deregistration of the channel here. `close(ch)`
                return // Remember to return to end the routine.
            
            case ch <- t: // This is the actual send.
                // Our message went through, do nothing 
            }
        }
    }()

    // We return the channel and no error.
    return ch, nil
}

我想了解接收到ctx.Done()信号时的具体情况:该信号是客户端取消订阅或关闭订阅时发送的?还是可能因闲置超时自动触发?另外,服务端设置的超时是否会触发ctx.Done()信号?


触发ctx.Done()的核心场景:

  • 客户端主动终止:当客户端主动取消订阅(调用客户端SDK的取消方法)、关闭连接(比如关闭浏览器标签页、断开WebSocket)时,gqlgen会自动取消当前订阅绑定的Context,触发ctx.Done()。这是代码注释中提到的最常见场景。
  • 服务端闲置超时触发:如果你的GraphQL服务配置了订阅的闲置超时(比如通过gqlgen配置项、底层WebSocket服务器的超时设置),当客户端在指定时间内无任何交互(包括心跳包),服务端会主动终止订阅,此时也会触发ctx.Done()。
  • 服务端主动配置的超时:如果你在Resolver或服务端中间件中,通过context.WithTimeout或context.WithDeadline给订阅Context设置了超时,当超时时间到达时,Context会被取消,进而触发ctx.Done()。默认情况下gqlgen不会给订阅Context添加超时,需要你主动配置。

补充说明:代码中defer close(ch)的写法很合理,当ctx.Done()触发后,goroutine退出前会关闭channel,能有效避免goroutine泄漏和资源浪费。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 02:46:07