natsx

package
v0.19.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

README

go-toolbox/pkg/natsx

GitHub go.mod Go version GoDoc License

一个功能丰富、高性能的 Go 语言 NATS 客户端易用性封装库,提供泛型事件订阅、广播、批量流式消费和消费者池管理

📖 特性 & 文档导航

特性 说明 文档
🔌 客户端封装 基于 *nats.Conn 的轻量封装,不管理连接生命周期 📘 快速开始
📨 消息发布 泛型事件发布、带重试发布、请求-响应模式 📙 消息发布
📬 普通订阅 QueueSubscribe 负载均衡模式,泛型自动反序列化 📗 事件订阅
📡 广播订阅 所有订阅者都收到消息,适合事件通知 📕 广播订阅
📦 批量流式消费 JetStream Pull 模式批量拉取,支持批量大小和等待时间 📓 批量消费
🏊 消费者池 局部消费者池和全局消费者池,基于 go-toolbox WorkerPool 🏊 消费者池
🔁 重试与应答决策 ErrPermanent 永久失败终止、指数退避、无限重投、最大重试上限 📗 事件订阅
🔍 上下文传播 ContextInjector 消息级注入 trace 等跨服务上下文 📗 事件订阅
⚙️ 可配置参数 重试次数、批量大小、等待时间、ACK 超时等 ⚙️ 配置选项
🔗 JetStream 原生 JetStream 支持,持久化消息和流处理 🔗 JetStream
🛡️ 错误处理 结构化错误定义,支持错误检查 🛡️ 错误处理

📖 完整文档:查看 文档中心 了解所有功能和学习路径

📦 安装

go get github.com/HitGameAI/apex-toolbox/pkg/natsx

🚀 快速开始

import (
    "github.com/nats-io/nats.go"
    natsx "github.com/HitGameAI/apex-toolbox/pkg/natsx"
)

// 1. 创建 NATS 连接(由调用方管理连接生命周期)
conn, _ := nats.Connect("nats://127.0.0.1:4222")

// 2. 创建客户端
client, _ := natsx.NewClient(conn)
defer client.Close()

// 3. 发布事件
natsx.PublishEvent(client, "user.created", &UserCreated{Name: "张三"})

// 4. 订阅事件(泛型自动反序列化)
natsx.Subscribe[UserCreated](client, "user.created", "order-service", func(evt *UserCreated) error {
    fmt.Println("User created:", evt.Name)
    return nil
})

💡 详细教程:查看 📘 快速入门文档 了解完整的安装和使用步骤

🏗️ 核心特性

泛型事件订阅
// 普通订阅 - QueueSubscribe 负载均衡
natsx.Subscribe[OrderEvent](client, "order.created", "payment-service", func(evt *OrderEvent) error {
    return processPayment(evt)
})

// 广播订阅 - 所有订阅者都收到消息
natsx.SubscribeBroadcast[UserEvent](client, "user.updated", func(evt *UserEvent) error {
    return refreshCache(evt)
})

// 批量流式消费 - JetStream Pull 模式
natsx.SubscribeStreamBatch[LogEvent](client, "logs.batch", "analytics", func(evts []*LogEvent) error {
    return batchInsert(evts)
}, natsx.WithBatchSize(100), natsx.WithMaxWait(5*time.Second))
消费者池
// 全局消费者池 - 所有订阅共享
client.InitWorkerPool(10, 1000)
natsx.Subscribe[Event](client, "topic", "svc", handler, natsx.WithIntoGlobalPool())

// 局部消费者池 - 每个订阅独立
natsx.Subscribe[Event](client, "topic", "svc", handler, natsx.WithLocalPoolSize(5, 200))
JetStream 支持
client.EnableJetStream()

// 发布持久化消息
ack, _ := client.PublishJetStream(ctx, "order.created", data)

// 订阅 JetStream 消息(自动 ACK)
natsx.Subscribe[OrderEvent](client, "order.created", "svc", handler,
    natsx.WithMaxAckWait(30*time.Second),
    natsx.WithMsgMaxRetry(5),
)
重试与应答决策

handler 返回错误时,库按应答决策表自动选择 Term / NakWithDelay / Nak:

场景 应答行为
错误命中 ErrPermanent(消息体损坏、业务上无法匹配等) 立即 Term 终止,不再重投
投递次数超过 MsgMaxRetry Term 终止(WithUnlimitedDelivery 模式下永不 Term)
其他临时错误 按退避策略 NakWithDelay 重投,未配置则立即 Nak
// 永久性失败:handler 包装 ErrPermanent 哨兵,库直接 Term 终止消息
natsx.Subscribe[OrderEvent](client, "order.paid", "svc", func(ctx context.Context, evt *OrderEvent) error {
    if evt.OrderNo == "" {
        return fmt.Errorf("%w: order not found", natsx.ErrPermanent) // 重试不可修复
    }
    return processPayment(ctx, evt) // 临时错误:自动退避重投
},
    // 指数退避:1s 起步,逐次 ×2,封顶 30s,叠加随机抖动避免重投风暴同步
    natsx.WithRetryBackoff(natsx.Backoff{Base: time.Second, Max: 30 * time.Second, Factor: 2, Jitter: true}),
    natsx.WithMsgMaxRetry(5), // 最多投递 5 次后 Term
    // natsx.WithUnlimitedDelivery(), // 或:无限重投(除 ErrPermanent 外永不放弃)
)
上下文传播(ContextInjector)

每条消息处理前从消息 Header 继承跨服务上下文(如 trace_id),依赖倒置设计,库自身不感知具体实现:

// 注入器:从消息 Header 恢复 trace 上下文(桥接网关中间件的 trace 逻辑)
injector := func(ctx context.Context, msg *nats.Msg) context.Context {
    if traceID := msg.Header.Get("X-Trace-Id"); traceID != "" {
        ctx = gwMiddleware.WithTraceID(ctx, traceID)
    }
    return ctx
}

natsx.Subscribe[OrderEvent](ctx, client, "order.paid", "svc", handler,
    natsx.WithContextInjector(injector),
    natsx.WithMaxAckWait(30*time.Second), // 消息级 ctx deadline 与 AckWait 对齐,
                                           // 消除「handler 仍在跑但 JetStream 已重投」的双活窗口
)

📖 详细说明:查看 🏊 消费者池 和 🔗 JetStream 了解更多配置

🧪 测试

测试内嵌进程内 NATS 服务器(启用 JetStream),不依赖任何外部环境,直接运行:

# 运行全部测试(覆盖率 100%)
go test ./... -cover

# 连接外部服务器(可选,CI 复用常驻实例时设置)
NATS_TEST_URL=nats://127.0.0.1:4222 go test ./... -v

# 运行指定测试
go test -v -run TestSubscribe

📚 相关资源

📦 依赖

🤝 贡献

  1. Fork 本仓库
  2. 创建特性分支 (git checkout -b feature/amazing-feature)
  3. 提交更改 (git commit -m '✨ feat: Add amazing feature')
  4. 推送到分支 (git push origin feature/amazing-feature)
  5. 开启 Pull Request

📋 Git Commit Emoji 规范

点击展开 Emoji 规范表
Emoji 类型 说明
✨ feat 新功能
🐛 fix 修复 bug
📝 docs 文档更新
♻️ refactor 代码重构
⚡ perf 性能优化
✅ test 测试相关
🔧 chore 配置/构建
🚀 deploy 部署发布
🔒 security 安全修复
🔥 remove 删除代码

示例: git commit -m "✨ feat(subscribe): 新增批量流式消费"

📄 许可证

MIT License - 详见 LICENSE

👨‍💻 作者

Kamal Yang (@kamalyes)

Documentation

Overview

* @Author: kamalyes 501893067@qq.com * @Date: 2026-04-23 00:00:00 * @LastEditors: kamalyes 501893067@qq.com * @LastEditTime: 2026-04-23 00:00:00 * @FilePath: \go-toolbox\pkg\natsx\client.go * @Description: go-toolbox/pkg/natsx 客户端核心结构体 * * 纯易用性封装,不管理连接生命周期,只接受 *nats.Conn * 连接由调用方(如 go-rpc-gateway)负责创建和关闭 * * Copyright (c) 2026 by kamalyes, All Rights Reserved.

* @Author: kamalyes 501893067@qq.com * @Date: 2026-04-20 00:00:00 * @LastEditors: kamalyes 501893067@qq.com * @LastEditTime: 2026-04-20 00:00:00 * @FilePath: \go-toolbox\pkg\natsx\errors.go * @Description: go-toolbox/pkg/natsx 错误定义 * * Copyright (c) 2026 by kamalyes, All Rights Reserved.

* @Author: kamalyes 501893067@qq.com * @Date: 2026-04-23 00:00:00 * @LastEditors: kamalyes 501893067@qq.com * @LastEditTime: 2026-04-23 00:00:00 * @FilePath: \go-toolbox\pkg\natsx\logger.go * @Description: go-toolbox/pkg/natsx 日志接口,直接复用 go-toolbox/pkg/logger * * Copyright (c) 2026 by kamalyes, All Rights Reserved.

* @Author: kamalyes 501893067@qq.com * @Date: 2026-04-23 00:00:00 * @LastEditors: kamalyes 501893067@qq.com * @LastEditTime: 2026-04-23 00:00:00 * @FilePath: \go-toolbox\pkg\natsx\publish.go * @Description: go-toolbox/pkg/natsx 发布消息 * * Copyright (c) 2026 by kamalyes, All Rights Reserved.

* @Author: kamalyes 501893067@qq.com * @Date: 2026-04-23 00:00:00 * @LastEditors: kamalyes 501893067@qq.com * @LastEditTime: 2026-04-23 00:00:00 * @FilePath: \go-toolbox\pkg\natsx\subscribe.go * @Description: go-toolbox/pkg/natsx 泛型事件订阅 * * 提供三种订阅模式: * - Subscribe:普通事件订阅(QueueSubscribe 负载均衡),泛型自动反序列化 * - SubscribeBroadcast:广播事件订阅(Subscribe 模式),所有订阅者都收到消息 * - SubscribeStreamBatch:批量流式消费(JetStream Pull 模式),支持批量拉取 * * 上下文传播(ctx 分层): * - 订阅级 base ctx:随调用方传入,取消即停止消费(优雅停机) * - 消息级 ctx:每条消息从 base ctx 派生,先经 ContextInjector 注入(如 trace_id), * 再叠加与 MaxAckWait 对齐的 deadline,消除「handler 仍在跑但 JetStream 已重投」的双活窗口 * * 消费者池: * - 局部消费者池:每个订阅创建独立 WorkerPool 处理消息 * - 全局消费者池:共享 Client 级别的 WorkerPool,通过 InitWorkerPool 初始化 * * Copyright (c) 2026 by kamalyes, All Rights Reserved.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNotConnected             = errors.New("nats: not connected")
	ErrAlreadyClosed            = errors.New("nats: already closed")
	ErrInvalidSubject           = errors.New("nats: invalid subject")
	ErrInvalidMessage           = errors.New("nats: invalid message")
	ErrPublishFailed            = errors.New("nats: publish failed")
	ErrSubscribeFailed          = errors.New("nats: subscribe failed")
	ErrJetStreamFailed          = errors.New("nats: jetstream operation failed")
	ErrBucketNotFound           = errors.New("nats: kv bucket not found")
	ErrKeyNotFound              = errors.New("nats: kv key not found")
	ErrTimeout                  = errors.New("nats: operation timed out")
	ErrUnavailable              = errors.New("nats: service unavailable")
	ErrGlobalPoolNotInitialized = errors.New("nats: global consumer pool not initialized, call InitWorkerPool first")

	// ErrPermanent 永久性失败哨兵错误
	// handler 返回 errors.Is 命中此哨兵的错误时,库直接 Term 终止消息(不再重投),
	// 与 Nak 重试路径区分:格式损坏、业务上无法匹配等重试不可修复的场景
	// 用法:return fmt.Errorf("%w: order not found", natsx.ErrPermanent)
	ErrPermanent = errors.New("nats: permanent failure, message will be terminated")
)

Functions

func DeliveryCount

func DeliveryCount(msg *nats.Msg) uint64

DeliveryCount 返回消息当前投递次数(1 = 首次投递) 非 JetStream 消息(Core NATS 模式)无投递元数据,返回 0

func NewDefaultLogger

func NewDefaultLogger() logger.ILogger

NewDefaultLogger 创建默认配置的NATS日志器

func PublishEvent

func PublishEvent[T any](c *Client, eventName string, event *T) error

PublishEvent 发布泛型事件(自动 JSON 序列化)

func Subscribe

func Subscribe[T any](ctx context.Context, c *Client, eventName, subscriberName string, handleFunc func(ctx context.Context, evt *T) error, opts ...ApplySubOptsFunc) error

Subscribe 普通事件订阅(QueueSubscribe 负载均衡模式) 同一 queue 组内只有一个消费者收到某条消息,适合任务分发场景 泛型 T 自动反序列化消息体;ctx 为订阅级基础上下文,取消即停止消费(批量拉取模式下生效), 每条消息的处理 ctx 由库内派生(注入器 + AckWait deadline 对齐)

func SubscribeBroadcast

func SubscribeBroadcast[T any](ctx context.Context, c *Client, eventName string, handleFunc func(ctx context.Context, evt *T) error, opts ...ApplySubOptsFunc) error

SubscribeBroadcast 广播事件订阅 所有订阅者都收到消息,适合事件通知、状态同步场景

func SubscribeStreamBatch

func SubscribeStreamBatch[T any](ctx context.Context, c *Client, eventName, subscriberName string, handleFunc func(ctx context.Context, evts []*T) error, opts ...ApplySubOptsFunc) error

SubscribeStreamBatch 批量流式消费(JetStream Pull 模式) 基于 JetStream PullSubscribe 实现批量拉取消息;ctx 取消即停止拉取(优雅停机)

Types

type ApplySubOptsFunc

type ApplySubOptsFunc func(opt *SubscribeOptions)

ApplySubOptsFunc 订阅选项函数

func WithBatchSize

func WithBatchSize(batchSize int) ApplySubOptsFunc

WithBatchSize 设置批量消费大小

func WithConsumeFastest

func WithConsumeFastest(consumeFastest bool) ApplySubOptsFunc

WithConsumeFastest 设置是否尽快消费

func WithContextInjector

func WithContextInjector(inj ContextInjector) ApplySubOptsFunc

WithContextInjector 设置消息级上下文注入器

func WithEnableFlowControl

func WithEnableFlowControl() ApplySubOptsFunc

WithEnableFlowControl 开启流控

func WithIdleHeartbeat

func WithIdleHeartbeat(idleHeartbeat time.Duration) ApplySubOptsFunc

WithIdleHeartbeat 设置消费者心跳时间

func WithIntoGlobalPool

func WithIntoGlobalPool() ApplySubOptsFunc

WithIntoGlobalPool 设置进入全局消费者池

func WithListenBroadcast

func WithListenBroadcast() ApplySubOptsFunc

WithListenBroadcast 设置广播模式

func WithLocalPoolSize

func WithLocalPoolSize(size, queueSize int) ApplySubOptsFunc

WithLocalPoolSize 设置局部消费者池大小

func WithMaxAckWait

func WithMaxAckWait(maxAckWait time.Duration) ApplySubOptsFunc

WithMaxAckWait 设置消息最长消费时间 同时决定消息级 ctx 的 deadline:处理超时 → ctx 先取消 → handler 快速失败 → Nak 重投

func WithMaxWait

func WithMaxWait(maxWait time.Duration) ApplySubOptsFunc

WithMaxWait 设置批量消费最大等待时间

func WithMsgMaxRetry

func WithMsgMaxRetry(msgMaxRetry uint64) ApplySubOptsFunc

WithMsgMaxRetry 设置消息最大重试次数

func WithMsgRetryInterval

func WithMsgRetryInterval(msgRetryInterval time.Duration) ApplySubOptsFunc

WithMsgRetryInterval 设置消息重试间隔(固定间隔模式) 需要指数退避时改用 WithRetryBackoff,两者同时设置时退避优先

func WithRetryBackoff

func WithRetryBackoff(backoff Backoff) ApplySubOptsFunc

WithRetryBackoff 设置指数退避重试策略 延迟序列:Base, Base*Factor, Base*Factor², ... 封顶于 Max;可选抖动打散重投风暴

func WithUnlimitedDelivery

func WithUnlimitedDelivery() ApplySubOptsFunc

WithUnlimitedDelivery 无限重投模式 语义:消息消费失败后永远 Nak 重投,永不 Term(适用于资金、账务等不可丢失的消费场景)。 实现上等价于 MsgMaxRetry=0;与 WithMsgMaxRetry 互斥,后调用者生效

type Backoff

type Backoff struct {
	Base   time.Duration // 首次重试延迟
	Max    time.Duration // 延迟上限
	Factor float64       // 指数因子(每次延迟 = 上次 * Factor,<=0 时按 2.0 处理)
	Jitter bool          // 是否叠加 0~1 倍随机抖动,避免重投风暴同步
}

Backoff 重试退避策略(指数退避 + 抖动) 设置后退避计算优先于 MsgRetryInterval 固定间隔

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client NATS 易用性封装客户端

func NewClient

func NewClient(conn *nats.Conn, log ...logger.ILogger) (*Client, error)

NewClient 基于 *nats.Conn 创建客户端

func (*Client) Close

func (c *Client) Close()

Close 关闭客户端管理的所有订阅和 WorkerPool(不关闭底层连接)

func (*Client) Conn

func (c *Client) Conn() *nats.Conn

Conn 返回底层 NATS 连接

func (*Client) EnableJetStream

func (c *Client) EnableJetStream() error

EnableJetStream 启用 JetStream 上下文

func (*Client) InitWorkerPool

func (c *Client) InitWorkerPool(workers, queueSize int)

InitWorkerPool 初始化全局消费者 WorkerPool workers: worker 数量,queueSize: 任务队列大小

func (*Client) IsConnected

func (c *Client) IsConnected() bool

IsConnected 检查连接状态

func (*Client) JetStream

func (c *Client) JetStream() nats.JetStreamContext

JetStream 返回 JetStream 上下文

func (*Client) Publish

func (c *Client) Publish(ctx context.Context, subject string, data []byte) error

Publish 发布消息到指定 Subject

func (*Client) PublishJetStream

func (c *Client) PublishJetStream(ctx context.Context, subject string, data []byte, opts ...nats.PubOpt) (*nats.PubAck, error)

PublishJetStream 通过 JetStream 发布消息

func (*Client) PublishWithRetry

func (c *Client) PublishWithRetry(ctx context.Context, subject string, data []byte, attemptCount int, interval time.Duration) error

PublishWithRetry 带重试的消息发布

func (*Client) Request

func (c *Client) Request(ctx context.Context, subject string, data []byte, timeout time.Duration) (*nats.Msg, error)

Request 发送请求并等待响应

func (*Client) Stats

func (c *Client) Stats() map[string]interface{}

Stats 返回客户端统计信息

func (*Client) WorkerPool

func (c *Client) WorkerPool() *syncx.WorkerPool

WorkerPool 返回全局 WorkerPool

type ContextInjector

type ContextInjector func(ctx context.Context, msg *nats.Msg) context.Context

ContextInjector 消息级上下文注入器 每条消息分发前调用,用于从消息 Header 继承跨服务上下文(如 trace_id)或注入自定义值 依赖倒置设计:库自身不感知具体实现,由应用侧传入(如桥接网关中间件的 trace 逻辑)

type SubscribeOptions

type SubscribeOptions struct {
	IsListenBroadcast  bool            // 是否广播方式监听
	IsIntoGlobalPool   bool            // 是否进入全局消费者池中消费
	LocalPoolSize      int             // 局部消费者池大小
	LocalPoolQueueSize int             // 局部消费者池队列大小
	BatchSize          int             // 批量消费的最大数量
	MaxWait            time.Duration   // 批量消费最大等待消息时间
	ConsumeFastest     bool            // 批量消费时是否尽快消费
	MsgMaxRetry        uint64          // 消息消费失败最大重试次数(0 表示无限重投,见 WithUnlimitedDelivery)
	MsgRetryInterval   time.Duration   // 消息消费重试的时间间隔(固定间隔模式)
	RetryBackoff       *Backoff        // 指数退避模式(设置后优先于 MsgRetryInterval)
	MaxAckWait         time.Duration   // 消息最长消费时间(同时作为消息级 ctx 的 deadline)
	IdleHeartbeat      time.Duration   // 消费者心跳时间
	EnabledFlowControl bool            // 是否开启流控机制
	ContextInjector    ContextInjector // 消息级 ctx 注入器(如注入 trace_id)
}

SubscribeOptions 订阅选项

func DefaultSubscribeOptions

func DefaultSubscribeOptions() SubscribeOptions

DefaultSubscribeOptions 返回默认订阅选项

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL