Golang发布订阅模式的基础实现思路
发布时间 - 2026-01-11 00:00:00 点击率:次最简发布订阅用map+chan实现,以主题为key、带缓冲通道为value,配RWMutex保护并发;发布时快照通道列表并select非阻塞发送;订阅需返回取消函数清理map和通道,主题应语义化而非UUID。
用 map + chan 实现最简发布订阅
核心就是维护一个主题(string)到订阅者通道(chan interface{})的映射,发布时遍历所有对应通道发送消息。注意:不能直接在发布时向 chan 写入而不做保护——如果某个订阅者没及时读,写操作会阻塞整个发布流程。
实操建议:
- 每个订阅者应启动独立 goroutine 消费自己的
chan,避免阻塞发布者 - 使用
sync.RWMutex保护map的并发读写,尤其在动态增删订阅时 - 通道建议带缓冲(如
make(chan interface{}, 1)),防止单个慢消费者拖垮全局 - 不推荐用无缓冲通道做订阅通道,极易因消费滞后导致发布卡死
sync.Map 能替代普通 map 吗?
可以,但不推荐作为首选。虽然 sync.Map 免去了显式加锁,但它不支持遍历——而发布动作必须“找到所有监听该主题的通道”,这就必须能枚举键值对。sync.Map 的 Range 是快照式遍历,期间新增/删除订阅可能被跳过;且无法保证遍历与写入的严格一致性。
更稳妥的做法仍是 map 配 sync.RWMutex,并在读取前加读锁、写入时加写锁:
mu.RLock()
chans := make([]chan interface{}, 0, len(m[topic]))
for _, ch := range m[topic] {
chans = append(chans, ch)
}
mu.RUnlock()
for _, ch := range chans {
select {
case ch <- msg:
default: // 避免阻塞,丢弃或记录
}
}
如何安全关闭订阅通道并清理资源
单纯关闭 chan 不足以清理,因为已关闭的通道仍可读(返回零值),且 map 中残留的 nil 或已关闭通道会导致后续发布 panic 或逻辑错乱。
关键动作是「移出 map」+「通知消费者退出」:
- 不要在发布逻辑里检查
cap(ch) == 0或ch == nil来跳过,这不可靠 - 订阅函数应返回一个
func()取消函数,内部完成:从map删除通道 + 关闭通道 + 唤醒消费者 goroutine - 消费者 goroutine 应用
for msg := range ch模式,通道关闭后自动退出 - 若需等待消费者真正退出,可用
sync.WaitGroup计数,但注意别在持有锁时wg.Wait()
为什么不用第三方库如 github.com/google/uuid 生成主题名
主题名本质是业务语义标识(如 "user.created"、"order.paid"),不是为了唯一性。用 UUID 当主题名反而让调试和监控变困难——你没法一眼看出事件类型,日志里全是随机字符串,Prometheus 标签也难以聚合。
真正需要唯一性的场景是「临时请求响应匹配」(比如 RPC 回调),那属于请求
-响应模式,不是发布订阅。发布订阅的主题应是稳定、可读、可预测的字符串常量或拼接结果。
容易被忽略的一点:主题层级设计会影响扩展性。例如用 "payment.usd.success" 而非 "payment_usd_success",后续就能支持通配符订阅(如 "payment.*.success"),但 Go 标准库不内置通配匹配,得自己实现或引入轻量库如 github.com/robfig/pat 做前缀匹配。
# git
# go
# github
# golang
# app
# ai
# google
# 键值对
# 标准库
# 字符串常量
# 为什么
# 有锁
# String
# 常量
# for
# select
# 字符串
# Interface
# cap
# nil
# map
# 并发
# 事件
# rpc
# prometheus
# 遍历
# 而非
# 跳过
# 自己的
# 去了
# 就能
# 并在
# 这就
# 仍是
# 不做
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
如何快速完成中国万网建站详细流程?
Laravel怎么清理缓存_Laravel optimize clear命令详解
laravel服务容器和依赖注入怎么理解_laravel服务容器与依赖注入解析
网站建设保证美观性,需要考虑的几点问题!
使用PHP下载CSS文件中的所有图片【几行代码即可实现】
Laravel模型关联查询教程_Laravel Eloquent一对多关联写法
php嵌入式断网后怎么恢复_php检测网络重连并恢复硬件控制【操作】
如何在IIS中新建站点并配置端口与IP地址?
Laravel怎么创建控制器Controller_Laravel路由绑定与控制器逻辑编写【指南】
Java解压缩zip - 解压缩多个文件或文件夹实例
Windows10电脑怎么查看硬盘通电时间_Win10使用工具检测磁盘健康
清除minerd进程的简单方法
Laravel如何处理JSON字段_Eloquent原生JSON字段类型操作教程
高端建站三要素:定制模板、企业官网与响应式设计优化
免费视频制作网站,更新又快又好的免费电影网站?
如何续费美橙建站之星域名及服务?
Laravel如何发送邮件_Laravel Mailables构建与发送邮件的简明教程
为什么php本地部署后css不生效_静态资源加载失败修复技巧【技巧】
Laravel如何实现API版本控制_Laravel API版本化路由设计策略
Laravel Octane如何提升性能_使用Laravel Octane加速你的应用
Laravel如何配置.env文件管理环境变量_Laravel环境变量使用与安全管理
胶州企业网站制作公司,青岛石头网络科技有限公司怎么样?
Laravel如何使用集合(Collections)进行数据处理_Laravel Collection常用方法与技巧
佐糖AI抠图怎样调整抠图精度_佐糖AI精度调整与放大细化操作【攻略】
奇安信“盘古石”团队突破 iOS 26.1 提权
如何用ChatGPT准备面试 模拟面试问答与职场话术练习教程
如何挑选高效建站主机与优质域名?
智能起名网站制作软件有哪些,制作logo的软件?
微信小程序 闭包写法详细介绍
网页设计与网站制作内容,怎样注册网站?
javascript如何操作浏览器历史记录_怎样实现无刷新导航
微博html5版本怎么弄发语音微博_语音录制入口及时长限制操作【教程】
网站制作软件有哪些,制图软件有哪些?
php在windows下怎么调试_phpwindows环境调试操作说明【操作】
详解Android图表 MPAndroidChart折线图
EditPlus中的正则表达式 实战(1)
Laravel如何生成PDF或Excel文件_Laravel文档导出工具与使用教程
Laravel Asset编译怎么配置_Laravel Vite前端构建工具使用
Windows驱动无法加载错误解决方法_驱动签名验证失败处理步骤
Laravel如何实现一对一模型关联?(Eloquent示例)
长沙企业网站制作哪家好,长沙水业集团官方网站?
JS经典正则表达式笔试题汇总
邀请函制作网站有哪些,有没有做年会邀请函的网站啊?在线制作,模板很多的那种?
香港服务器租用每月最低只需15元?
Laravel如何实现文件上传和存储?(本地与S3配置)
Firefox Developer Edition开发者版本入口
java获取注册ip实例
Windows Hello人脸识别突然无法使用
Laravel如何正确地在控制器和模型之间分配逻辑_Laravel代码职责分离与架构建议
Laravel如何实现API版本控制_Laravel版本化API设计方案

