0

0

Go语言观察者模式如何实现_Go观察者模式事件通知机制

P粉602998670

P粉602998670

发布时间:2026-02-20 12:38:46

|

595人浏览过

|

来源于php中文网

原创

go需自行实现观察者模式,核心是带sync.rwmutex保护的eventbus(map[string][]func(interface{})),事件名用string、回调参数为interface{},注册需去重(推荐uintptr(unsafe.pointer(&fn))),支持同步/异步通知及defer式注销。

go语言观察者模式如何实现_go观察者模式事件通知机制

Go里没有内置Observer接口,得自己搭结构

Go语言不提供像Java那样的Observer抽象类或C#的event关键字,观察者模式必须靠组合+函数类型+并发安全容器来实现。核心是定义一个事件分发器(EventBus),内部维护map[string][]func(interface{}),键为事件名,值为回调切片。

常见错误是直接用map并发读写——没加锁会导致panic: fatal error: concurrent map read and map write。必须用sync.RWMutexsync.Map(后者适合读多写少,但不支持遍历全部监听器)。

实操建议:

ithy
ithy

融合多种AI模型的AI搜索平台

下载
  • sync.RWMutex保护注册/注销/通知全过程,比sync.Map更可控
  • 事件名用string而非常量枚举,方便动态扩展,但需文档约定命名规范(如"user.created"
  • 回调函数参数统一为interface{},由观察者自行断言,避免泛型在早期Go版本中带来的复杂度

注册和注销观察者要支持重复注册防护

多个相同回调反复Subscribe("order.paid", handler)会导致同一事件触发多次执行,这是高频bug。不能只靠用户自觉,得在Subscribe里做去重。

立即学习go语言免费学习笔记(深入)”;

典型做法是把回调包装成带唯一ID的结构体:type observer struct { id string; fn func(interface{}) },注册时检查id是否已存在;或者用func指针地址做key(仅限闭包外的具名函数,匿名函数地址不可靠)。

实操建议:

  • 推荐用uintptr(unsafe.Pointer(&fn))获取函数地址做去重依据,适用于所有函数值(包括闭包)
  • Unsubscribe必须支持按事件名+回调双条件删除,否则无法精准清理
  • 注册返回一个unsubscribe()函数,方便defer调用,比手动Unsubscribe更不易遗漏

通知过程要区分同步 vs 异步,别卡住主流程

默认同步通知(逐个调用回调)看似简单,但某个观察者panic或阻塞会拖垮整个事件流。比如支付成功后发短信、写日志、更新缓存三个观察者,其中一个HTTP超时,其余两个就等死。

异步通知用go e.notifyAsync(event, data)能解耦,但引入新问题:观察者执行顺序不确定、错误无法回传、生命周期难管理。

实操建议:

  • 默认走同步,仅对明确标记async:true的事件才启用goroutine
  • 异步通知中用recover()捕获panic,避免goroutine泄露
  • 不要在通知里传指针给观察者——原对象可能已被回收,应传深拷贝或只传不可变数据(如struct{ID int; Time time.Time}

用channel实现轻量级事件总线更贴近Go惯用法

比起维护map+锁,用chan Event做中心队列更符合Go“通过通信共享内存”的哲学。每个观察者起一个goroutine从channel收事件,自行过滤关心的类型。

缺点是无法动态增删监听器(channel关闭后新加的收不到),且广播需遍历所有监听channel,效率不如map查找。但它天然规避了并发写map问题,也更容易做背压控制(如带缓冲的channel)。

实操建议:

  • select配合default防止阻塞:select { case ch
  • 监听器goroutine退出前务必close(ch)并从全局监听列表移除,否则内存泄漏
  • 如果需要事件确认机制(如Kafka的ack),就得在channel消息里附带done chan struct{},由观察者通知完成

真正麻烦的是跨服务事件(比如订单服务发事件,库存服务监听),这时候Go原生机制就不够用了——得上消息队列。本地观察者模式再怎么优化,也只是单进程内的协作契约。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
golang如何定义变量
golang如何定义变量

golang定义变量的方法:1、声明变量并赋予初始值“var age int =值”;2、声明变量但不赋初始值“var age int”;3、使用短变量声明“age :=值”等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

207

2024.02.23

golang有哪些数据转换方法
golang有哪些数据转换方法

golang数据转换方法:1、类型转换操作符;2、类型断言;3、字符串和数字之间的转换;4、JSON序列化和反序列化;5、使用标准库进行数据转换;6、使用第三方库进行数据转换;7、自定义数据转换函数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

239

2024.02.23

golang常用库有哪些
golang常用库有哪些

golang常用库有:1、标准库;2、字符串处理库;3、网络库;4、加密库;5、压缩库;6、xml和json解析库;7、日期和时间库;8、数据库操作库;9、文件操作库;10、图像处理库。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

348

2024.02.23

golang和python的区别是什么
golang和python的区别是什么

golang和python的区别是:1、golang是一种编译型语言,而python是一种解释型语言;2、golang天生支持并发编程,而python对并发与并行的支持相对较弱等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

212

2024.03.05

golang是免费的吗
golang是免费的吗

golang是免费的。golang是google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的开源编程语言,采用bsd开源协议。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

403

2024.05.21

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

344

2025.06.09

golang相关判断方法
golang相关判断方法

本专题整合了golang相关判断方法,想了解更详细的相关内容,请阅读下面的文章。

197

2025.06.10

golang数组使用方法
golang数组使用方法

本专题整合了golang数组用法,想了解更多的相关内容,请阅读专题下面的文章。

990

2025.06.17

pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

776

2026.02.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 5.3万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号