当前位置:首页 > 文章列表 > Golang > Go问答 > Go NATS Go 客户端 Drain 与 Close 有什么区别

Go NATS Go 客户端 Drain 与 Close 有什么区别

来源:17golang原创 2026-10-30 21:55:34 0浏览 收藏

服务要正常下线时,Go NATS 客户端通常应该优先考虑 Drain;只有不需要等待订阅和发布收尾,或者排水已经失败、必须立即释放资源时,才使用 Close。两者最终都会让连接进入关闭状态,但中间是否给消息处理留下收尾机会,完全不同。

官方文档:https://docs.nats.io/

要点速览
  • Drain 会把订阅放入排水状态,等订阅收尾后再处理发布端,最终关闭连接。
  • Close 直接关闭连接,并释放 Flush、NextMsg 等阻塞调用。
  • 正常发布、服务滚动下线、响应型订阅优先用 Drain;进程即将被强制终止或排水失败时,用 Close 做兜底。
  • DrainTimeout 默认是 30 秒,应结合 ClosedCB 和 IsDraining 观察退出状态。

故障现场:为什么服务退出时不能只看 Close

一次服务滚动发布中,进程收到停止信号后直接执行 nc.Close()。连接确实很快消失了,但正在回调里的响应还没有完成,等待中的 NextMsg 也被释放。问题不在于 Close “失效”,而在于它表达的是“现在就结束连接”,并不表达“让已经接收的工作先完成”。

官方 nats.go 对 Close 的定义很直接:关闭到服务器的连接,并释放包括 Flush 和 NextMsg 在内的阻塞调用。它适合硬关闭,但不适合把“剩余消息是否需要被处理完”交给库替你猜。

Drain 的语义则是先让连接进入排水状态,订阅立即进入排水状态;订阅完成后,发布端也会被排空,之后不能再发布新消息,最后连接关闭。官方 README 还特别把连接 Drain 标为 responder 的优先收尾方式,并说明调用 Drain 后不需要再调用 Close。

服务停止信号与 nats.Conn 的 Drain、Close、订阅、发布缓冲和关闭回调静态边界说明图
图1:结构说明图,展示 nats.Conn 的 Drain 与 Close 边界,不是运行截图或消息投递证据。

Drain 和 Close 的差异,关键在“是否给收尾留时间”

比较项DrainClose
主要意图优雅停止连接,让已接收的订阅和发布有机会收尾立即关闭连接,释放等待中的调用
订阅进入排水状态,继续处理允许完成的回调连接关闭,未完成的处理不能依赖继续执行
发布排水完成后不再接受新的发布,随后关闭不等待业务层发布收尾
结束状态排水完成后自动关闭,不需要正常追加 Close直接进入关闭状态
返回值返回 error,应记录或处理没有返回值
典型场景滚动发布、服务退出、响应型订阅者紧急终止、排水失败兜底、无需保留消息处理窗口

这里最容易写错的是把两者串成固定模板:nc.Drain(); nc.Close()。正常路径只需要 Drain;如果 Drain 返回错误,或者业务明确接受丢弃剩余工作的代价,再执行 Close 作为兜底。不要用“最后总要 Close”来掩盖自己没有定义退出策略。

把退出动作写成可解释的故障时间线

一个可维护的退出过程应该能回答三个问题:停止信号到来后,新的工作是否还会进入;已经进入回调的工作由谁完成;连接最终关闭时,进程如何知道结果。对应到 nats.go,可以把观察点放在 IsDraining、DrainTimeout 和 ClosedCB 上。

package main

import (
    "log"
    "time"

    "github.com/nats-io/nats.go"
)

func connect() (*nats.Conn, error) {
    opts := nats.GetDefaultOptions()
    opts.Servers = []string{"nats://127.0.0.1:4222"}
    opts.DrainTimeout = 10 * time.Second // 给订阅和发布收尾设置明确上限。
    opts.ClosedCB = func(nc *nats.Conn) {
        // ClosedCB 表示连接不再保持打开,可在这里记录退出结果。
        log.Printf("nats connection closed: draining=%v", nc.IsDraining())
    }
    return opts.Connect()
}

这个配置不是把 Drain 变成带 context 的同步函数,而是给客户端的排水过程设置上限。ClosedCB 用来接收最终关闭通知;IsDraining 适合在状态记录或退出协调中识别当前连接是否处在排水阶段。

nats.Options、DrainTimeout、nats.Conn、IsDraining、ClosedCB 与阻塞调用静态关系说明图
图2:配置与状态说明图,展示排水超时、状态查询和阻塞调用释放之间的静态关系,不是运行结果。

正常下线时,让 Drain 成为唯一的收尾动作

对于响应型订阅者,回调通常还要向请求方发布回复。此时连接收到停止信号后,直接 Close 会把“回复是否已经写入连接”变成一个竞态;Drain 则把订阅和发布端都纳入同一个结束语义。

import "log"

func shutdown(nc *nats.Conn) {
    // 正常下线优先 Drain;它完成后连接会自动关闭。
    if err := nc.Drain(); err != nil {
        // 排水失败时不再等待,直接释放连接作为硬关闭兜底。
        log.Printf("nats drain failed: %v", err)
        nc.Close()
    }
}

注意这个示例中的 Close 只在 Drain 报错时出现。若 Drain 成功,关闭动作由 Drain 的生命周期完成;如果把 Close 无条件放在成功路径后面,就很难从日志里分辨“优雅完成”还是“直接中断”。

三种场景怎么选

场景建议原因
服务滚动发布、容器收到正常停止信号Drain保留已接收消息和响应的收尾窗口
进程即将被强制终止,已经没有等待价值Close立即释放连接和阻塞调用
Drain 返回错误或超过可接受的退出预算Close 兜底避免退出流程无限等待,把失败显式记录下来

如果业务要求“任何消息都不能丢”,仅仅选择 Drain 还不够,还要给回调处理时间、控制发布方的退出顺序,并为超时后的重试或补偿定义策略。Drain 是客户端连接的收尾语义,不是业务消息可靠性的替代品。

常见问题

Drain 成功后还要再调用 Close 吗?

正常不需要。nats.go 的 Conn.Drain 文档说明,发布端排水完成后连接会关闭;官方 README 也提示 Drain 后不需要 Close。

Close 会不会等待当前回调执行完?

不要把 Close 当成等待回调完成的机制。它的文档重点是关闭连接并释放 Flush、NextMsg 等阻塞调用;需要优雅收尾时应使用 Drain。

DrainTimeout 应该设置多长?

按回调的正常处理时长、发布缓冲量和进程退出预算共同决定。默认值是 30 秒;如果服务只能在更短窗口内退出,就显式设置并准备好 Close 兜底。

IsDraining 能代替 ClosedCB 吗?

不能。IsDraining 反映排水状态,ClosedCB 用于接收连接最终关闭的通知;一个是过程状态,一个是结束信号。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go NATS Go 客户端怎样给请求响应设置截止时间Go NATS Go 客户端怎样给请求响应设置截止时间
上一篇
Go NATS Go 客户端怎样给请求响应设置截止时间
Go JetStream PubAck 的 Duplicate 字段怎样判断服务器去重结果
下一篇
Go JetStream PubAck 的 Duplicate 字段怎样判断服务器去重结果
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    798次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    791次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    877次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    848次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    679次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码