在 Go 语言中,结合 NATS 消息系统 和 ants 协程池 可以实现高效的消息处理与任务并发控制。以下是详细步骤和代码示例:
NATS:负责接收消息(订阅主题),将消息处理任务提交到协程池。
ants 协程池:管理并发任务,复用 goroutine,避免资源耗尽。
流程:NATS 订阅消息 → 消息触发任务 → 任务提交到 ants 池 → ants 分配 worker 处理。
bash
复制
go get github.com/nats-io/nats.go go get github.com/panjf2000/ants/v2
go
复制
package main import ( "context" "fmt" "log" "time" "github.com/nats-io/nats.go" "github.com/panjf2000/ants/v2" ) func main() { // 1. 初始化 NATS 连接 nc, err := nats.Connect(nats.DefaultURL) if err != nil { log.Fatal("NATS 连接失败:", err) } defer nc.Close() // 2. 初始化 ants 协程池(容量为 100 个协程) pool, err := ants.NewPool(100) if err != nil { log.Fatal("协程池初始化失败:", err) } defer pool.Release() // 3. 订阅 NATS 主题 subject := "tasks" _, err = nc.Subscribe(subject, func(msg *nats.Msg) { // 将消息处理任务提交到协程池 task := func() { handleMessage(msg.Data) } // 提交任务(带超时控制) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() if err := pool.SubmitWithContext(ctx, task); err != nil { log.Printf("任务提交失败: %v (消息内容: %s)", err, string(msg.Data)) } }) if err != nil { log.Fatal("订阅主题失败:", err) } // 4. 保持程序运行(实际场景中可替换为信号监听) fmt.Println("等待消息中...") select {} } // 处理消息的具体逻辑 func handleMessage(data []byte) { // 模拟耗时操作(如业务处理、数据库操作等) time.Sleep(1 * time.Second) log.Printf("处理完成: %s\n", string(data)) }
使用 nats.Connect
连接到 NATS 服务器(默认地址为 nats://localhost:4222
)。
生产环境建议配置 TLS、认证等安全参数。
ants.NewPool(100)
创建容量为 100 个协程的池。
pool.Release()
在程序退出时释放池资源。
nc.Subscribe
订阅指定主题。
在消息回调中,将 handleMessage
包装为任务函数,通过 pool.SubmitWithContext
提交到协程池。
带超时控制:防止任务队列满时无限阻塞。
handleMessage
是实际业务逻辑,处理消息内容。
若任务耗时较长,需确保协程池容量足够,避免任务堆积。
根据消息处理压力动态调整 ants 池容量:
go
复制
// 监控任务队列长度,自动扩容 go func() { ticker := time.NewTicker(10 * time.Second) defer ticker.Stop() for range ticker.C { if pool.Running() > 80 { pool.Tune(200) // 扩容到 200 } else if pool.Running() < 20 { pool.Tune(50) // 缩容到 50 } } }()
在任务中捕获错误并重试:
go
复制
task := func() { if err := handleMessageWithRetry(msg.Data, 3); err != nil { log.Printf("消息处理失败: %v (内容: %s)", err, string(msg.Data)) } } func handleMessageWithRetry(data []byte, maxRetries int) error { for i := 0; i < maxRetries; i++ { if err := doProcess(data); err == nil { return nil } time.Sleep(time.Duration(i) * time.Second) } return fmt.Errorf("重试 %d 次后失败", maxRetries) }
如果使用 NATS JetStream(持久化消息),需在处理成功后发送 Ack:
go
复制
task := func() { if err := handleMessage(msg.Data); err != nil { return // 不 Ack,等待重试 } if err := msg.Ack(); err != nil { log.Printf("Ack 发送失败: %v", err) } }
通过 ants 的统计接口监控池状态:
go
复制
fmt.Printf("运行中协程数: %d\n", pool.Running()) fmt.Printf("已完成任务数: %d\n", pool.Running())
连接管理:NATS 连接需配置重连逻辑(nats.Options
的 RetryOnFailedConnect
和 MaxReconnect
)。
任务隔离:不同优先级的任务可使用多个协程池。
流量控制:结合 NATS 的流量控制(如 nats.BufferSize
)避免内存溢出。
优雅关闭:监听退出信号,先关闭 NATS 订阅,再释放协程池:
go
复制
sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) <-sigCh nc.Drain() // 停止接收新消息 pool.Release() // 等待剩余任务完成
通过结合 NATS 和 ants,你可以在高并发消息场景下实现资源可控、高效稳定的任务处理架构。
在 Go 语言中,协程(goroutine) 是轻量级的并发执行单元,而协程池(goroutine pool) 是一种用于管理和复用协程的技术,可以有效控制并发数量、避免资源耗尽。以下是实现协程池的常见方法:
通过 channel
控制并发数,限制同时运行的协程数量。
go
复制
package main import ( "fmt" "sync" ) func worker(id int, jobs <-chan int, wg *sync.WaitGroup) { defer wg.Done() for job := range jobs { fmt.Printf("Worker %d processing job %d\n", id, job) // 模拟任务处理 // time.Sleep(time.Second) } } func main() { const numJobs = 10 // 总任务数 const numWorkers = 3 // 协程池大小 jobs := make(chan int, numJobs) var wg sync.WaitGroup // 启动协程池 for i := 1; i <= numWorkers; i++ { wg.Add(1) go worker(i, jobs, &wg) } // 分发任务 for j := 1; j <= numJobs; j++ { jobs <- j } close(jobs) // 关闭通道,通知协程退出 wg.Wait() // 等待所有协程完成 fmt.Println("All jobs done!") }
ants
)ants 是一个高性能的协程池库,支持动态调整池大小和任务提交。
go
复制
package main import ( "fmt" "sync" "time" "github.com/panjf2000/ants/v2" ) func task(i int) { fmt.Printf("Task %d started\n", i) time.Sleep(time.Second) fmt.Printf("Task %d done\n", i) } func main() { const numTasks = 10 var wg sync.WaitGroup pool, _ := ants.NewPool(3) // 池容量为3 defer pool.Release() for i := 1; i <= numTasks; i++ { wg.Add(1) _ = pool.Submit(func() { defer wg.Done() task(i) }) } wg.Wait() fmt.Println("All tasks done!") }
资源控制:避免无限制创建协程导致内存溢出。
复用协程:减少协程创建/销毁的开销。
任务队列:支持任务排队和优先级调度。
错误处理:协程内部需通过 recover()
捕获 panic,避免程序崩溃。
任务超时:结合 context
包实现任务超时控制。
资源释放:使用 defer pool.Release()
确保协程池正确关闭。
简单场景:使用 channel + WaitGroup
即可实现协程池。
生产环境:推荐使用 ants
等成熟库,功能更丰富、性能更优。
根据你的需求选择合适的方案,平衡开发效率与性能要求。