当前位置: 首页 > news >正文

事件驱动架构详解

序言

大家好,我是程序员木然, 这一章我将会带大家做一个新的轮子项目: 基于信号的事件驱动框架。
接下来,先介绍一些基础概念吧:

事件驱动架构(EDA)
事件驱动架构是一种软件设计架构,应用程序的行为由各种事件的发生和处理决定。在这种架构下,系统通过监听和响应事件来驱动执行流程。事件可以是用户的操作、外部系统的输入或定时器的触发等。

事件驱动架构的关键特性在于“解耦”,即事件的发起者和事件的处理者是独立的,彼此之间并不直接通信,而是通过一个中央机制来调度事件和响应。这种解耦使得系统能够更灵活地扩展和维护。

啊~ 差不多就是就是这样,相信不少的同学都会很懵 ———— 这讲的啥啊?啊?啥啊?

OK啊,深呼吸,懵逼是正常的。 这个概念对于没有接触过的同学来说,直接看概念是很抽象的,那么为了大家能够更好的理解,我来给大家讲一下这个架构的演变过程吧。

要谈这个架构,我们就得先说说他的初始核心 ———— 观察者模式 (很多的架构或者玩法都是由设计模式一步一步演变过来的, 不愧是前辈们总结的经验)

那么我们就先聊聊观察者模式吧(之前没有学过观察者模式的跪求不要划走,我下面马上让大家零基础入门观察者模式)。

简单的来说:

观察者模式: 是设计模式中的一种行为型模式,主要用于定义对象之间的一种 一对多依赖关系。当一个对象(被观察者/主题)发生状态变化时,它会自动通知所有依赖它的对象(观察者),让它们能够及时更新,从而保持数据或行为的一致性。

复杂的来说:

你有没有试过在群聊里说了句:“我失恋了”,然后几十个朋友瞬间弹出:“怎么回事?”、“别伤心啦~”、“要不喝点?”——
那么!这就叫:观察者模式!

给大家一个形象的比喻吧:
概念现实类比Java 对应
被观察者 Subject八卦群管理员Order
Subject
观察者 Observer吃瓜群众InventoryManager
事件 Event八卦内容通知、方法调用

相信大家有一定的理解了,那么我们接下来还是直接上代码

核心思想

  • 被观察者(Subject/Observable):维护一组观察者,当自身状态发生变化时,负责通知这些观察者。

  • 观察者(Observer):订阅被观察者的变化,一旦接收到通知,就会执行相应的操作。

  • 解耦:被观察者只需维护观察者的列表,并在状态变化时通知它们,而不需要关心观察者的具体实现逻辑。

代码示例

被观察者 :

/*** 被观察者: 重活一世的舔狗*/
public class Subject {
}

抽象观察者:

/*** 抽象观察者*/
public interface Observer {void receiveMessage(String message);
}

观察者1号 :

/*** 观察者: 好兄弟*/
public class BroObserver implements Observer{@Overridepublic void receiveMessage(String message) {System.out.println("兄弟说: 哟哟哟,薄冰哥~");}
}

观察者2号:

/*** 观察者: 坏女孩*/
public class BadGirl implements Observer{@Overridepublic void receiveMessage(String message) {System.out.println("女神: 这舔狗怎么回事");}
}

观察者3号:

/*** 观察者: 好女孩*/
public class GoodGirl implements Observer {@Overridepublic void receiveMessage(String message) {System.out.println("好女孩: 他咋啦啊");}
}

小舔狗:发个朋友圈看看


/*** 被观察者: 重活一世的舔狗*/
public class Subject {static List<Observer> observers = new ArrayList<>();public static void main(String[] args) {// 好兄弟开始观察小舔狗observers.add(new BroObserver());// 好女孩开始观察小舔狗observers.add(new GoodGirl());// 坏女孩开始观察小舔狗observers.add(new BadGirl());String message = "放下了,我这一生,如履薄冰";System.out.println("小舔狗发送了一条朋友圈: " + message );for (Observer observer : observers) {observer.receiveMessage(message);}}
}

运行结果:

小舔狗发送了一条朋友圈: 放下了,我这一生,如履薄冰
兄弟,你想开了,出去喝一杯
他终于醒悟了
欲擒故纵吗?有点意思

那么,这个案例应该很明显的展示观察者模式的玩法了。

从观察者模式到事件驱动架构

通过上面的例子,我们知道 观察者模式 的核心就是:

  • 被观察者负责发消息

  • 观察者负责接收并响应

这种设计的好处是解耦:小舔狗发朋友圈的时候,并不需要关心“谁”在看、“看了之后会做什么”,只需要把信息广播出去就好。

但是,随着系统越来越复杂,单纯的观察者模式就显得 捉襟见肘 了。

观察者模式的局限性
  1. 通知逻辑写死
    在上面的例子里,小舔狗必须手动维护 observers 列表并遍历通知,这种方式比较硬编码
    如果观察者数量很多、或者不同事件对应不同的观察者,就会很难管理。

  2. 缺少事件抽象
    我们的朋友圈消息本质上就是一个事件,但在观察者模式里,它就是个字符串,没有更强的语义和约束。

  3. 同步阻塞
    目前的通知方式是同步的:小舔狗一发朋友圈,就必须等所有观察者都回复完才能继续执行。现实世界往往需要 异步解耦(比如发朋友圈后,你不会等朋友一个个回复完才能去睡觉, 当然除了等待女神点赞的各位🤡)。

事件驱动架构的引入

为了解决以上问题,事件驱动架构(EDA) 作为观察者模式的自然演进出现了。它在“发布-订阅”的基础上,加入了 事件总线(Event Bus) 或 信号机制(Signal),从而让系统具备更强的解耦性和扩展性。

  • 事件(Event/Signal)

更加明确的抽象,不只是“字符串”,而是一个携带上下文的数据对象。

  • 事件总线(Event Bus / Dispatcher)

被观察者不用维护一堆观察者列表,而是把“事件”丢到总线上,由总线统一分发给感兴趣的观察者。

  • 异步机制

事件触发后,观察者可以异步处理,不会阻塞被观察者。

类比理解

如果说 观察者模式 就像“小舔狗建了个微信群,大家都在群里看他的动态”,
那么 事件驱动架构 就像“朋友圈系统”:

  • 小舔狗只管发动态(事件),

  • 微信后台(事件总线)会根据规则推送给相关好友(订阅者),

  • 好友什么时候看、怎么看、要不要回复,完全解耦。

过渡总结

所以,我们可以认为:

  • 观察者模式 是最简单的事件驱动思想实现。

  • 事件驱动架构 是它的 进阶和扩展,解决了通知逻辑混乱、同步阻塞、缺乏抽象等问题。

接下来,我们就要一步一步从观察者模式的代码,逐渐演化出一个 基于信号的事件驱动框架。

接下来,升级为事件驱动!
1 事件与监听器定义
// 事件标记接口
public interface Event { }// 事件监听器(泛型约束具体事件类型)
public interface EventListener<E extends Event> {void onEvent(E event);
}
import java.time.LocalDateTime;// 具体事件:发朋友圈
public class MomentPosted implements Event {private final String author;private final String message;private final LocalDateTime time;public MomentPosted(String author, String message) {this.author = author;this.message = message;this.time = LocalDateTime.now();}public String getAuthor() { return author; }public String getMessage() { return message; }public LocalDateTime getTime() { return time; }
}
2 事件总线(支持同步/异步分发)
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;public class EventBus {private final Map<Class<? extends Event>, CopyOnWriteArrayList<EventListener<? extends Event>>> listeners = new ConcurrentHashMap<>();private final AtomicBoolean async = new AtomicBoolean(false);private ExecutorService executor;public EventBus() { }// 开启/关闭异步分发public void setAsync(boolean enable) {async.set(enable);if (enable && executor == null) executor = Executors.newCachedThreadPool();if (!enable && executor != null) {executor.shutdown();executor = null;}}// 订阅public <E extends Event> void subscribe(Class<E> type, EventListener<E> listener) {listeners.computeIfAbsent(type, k -> new CopyOnWriteArrayList<>()).add(listener);}// 取消订阅public <E extends Event> void unsubscribe(Class<E> type, EventListener<E> listener) {List<? extends EventListener<? extends Event>> list = listeners.get(type);if (list != null) list.remove(listener);}// 发布事件@SuppressWarnings("unchecked")public <E extends Event> void publish(E event) {List<EventListener<? extends Event>> list = listeners.getOrDefault(event.getClass(), new CopyOnWriteArrayList<>());if (list.isEmpty()) return;for (EventListener<? extends Event> l : list) {EventListener<E> listener = (EventListener<E>) l;if (async.get() && executor != null) {executor.submit(() -> listener.onEvent(event));} else {listener.onEvent(event);}}}
}
3 三位“好友”成为订阅者(观察者)
public class BroObserver implements EventListener<MomentPosted> {@Overridepublic void onEvent(MomentPosted event) {System.out.println("好兄弟:" + event.getAuthor() + " 想开了?今晚我请!");}
}public class BadGirl implements EventListener<MomentPosted> {@Overridepublic void onEvent(MomentPosted event) {System.out.println("坏女孩:这波是欲擒故纵吗?有点意思~");}
}public class GoodGirl implements EventListener<MomentPosted> {@Overridepublic void onEvent(MomentPosted event) {System.out.println("好女孩:别难过,我在呢~");}
}
4. 小舔狗只负责“发动态”(发布事件)
public class MomentsDemo {public static void main(String[] args) throws InterruptedException {EventBus bus = new EventBus();// 切换成异步分发试试(不需要就注释掉)bus.setAsync(true);// 订阅“发朋友圈”事件bus.subscribe(MomentPosted.class, new BroObserver());bus.subscribe(MomentPosted.class, new GoodGirl());bus.subscribe(MomentPosted.class, new BadGirl());// 小舔狗发布动态String msg = "放下了,我这一生,如履薄冰";System.out.println("小舔狗发布朋友圈:「" + msg + "」");bus.publish(new MomentPosted("薄冰哥", msg));// 演示异步时,给线程池一点时间输出(同步模式可去掉)Thread.sleep(200);}
}
可能输出(异步下顺序不定,同步下按订阅顺序):
小舔狗发布朋友圈:「放下了,我这一生,如履薄冰」
好女孩:别难过,我在呢~
坏女孩:这波是欲擒故纵吗?有点意思~
好兄弟:薄冰哥 想开了?今晚我请!
小结一下吧
特性观察者模式事件驱动架构
通知机制主动维护观察者列表交给事件总线分发
事件模型通常是方法调用/字符串语义化的事件对象
执行方式同步阻塞支持异步解耦
适用场景小型、单进程内交互大型、跨服务、异步系统
EDA典型应用场景
  • 电商系统
    下单 → 触发 OrderCreated 事件 → 库存服务扣减库存、支付服务生成支付单、消息服务推送通知。
    → 好处:下单服务只负责发事件,不关心“库存、支付、消息”具体怎么处理。

  • 游戏开发
    玩家升级 → 发布 LevelUp 事件 → 背包系统加奖励、排行榜更新分数、成就系统点亮徽章。

  • 前端/客户端
    JS 的 addEventListener、Android 的 BroadcastReceiver,其实都是事件驱动的变体。

技术总结

那么,大家开发中实现事件驱动的技术有哪些呢?
本地级别:Guava EventBus、Spring ApplicationEvent。
分布式级别:Kafka、RabbitMQ、Pulsar、RocketMQ。
云原生级别:Knative Eventing、AWS EventBridge。

经过这一章,大家应该能理解到事件驱动的操作了,那么这一章圆满结束。

课后思考:

    1. 幂等性:事件可能重复消费,要保证处理一次和处理多次结果一致
    1. 有的场景必须保证先后,比如“先扣库存,再发货”。
    1. 事件如果丢了怎么办?要不要补偿机制?

更多资料,请关注Code百分百

http://www.dtcms.com/a/352432.html

相关文章:

  • AI Agent安全的“阿喀琉斯之踵”:深度解析MCP核心风险与纵深防御架构
  • Python爬虫: 分布式爬虫架构讲解及实现
  • mysql是怎样运行的(梳理)
  • Java基础第二课:hello word
  • 传统联邦 VS 联邦+大模型
  • freeModbus TCP收发数据一段时间后,出现掉线情况(time out问题)
  • 依托边缘计算方案,移动云全面化解算力、效率、安全平衡难题
  • Wireshark捕获数据的四种层次
  • 【Python数据分析】商品数据可视化大屏项目
  • YggJS RButton 按钮组件 v1.0.0 使用教程
  • 亚马逊运营效能提升:广告策略优化与自配送售后管理的协同路径
  • Makefile构建优化:提升编译效率的关键
  • 打卡day49
  • RocketMq程序动态创建Topic
  • 在 Ubuntu 下遇到 <string>头文件找不到的问题
  • 运筹优化(OR)-在机器学习(ML)浪潮中何去何从?
  • 独孤思维:无限分发,无成本赚钱的副业
  • JVM分层编译深度解析:完整机制与实践指南
  • 面向世界模型构建的跨模态认知网络工程
  • the scientist and engineer‘s guide to DSP:1 The Breadth and Depth of DSP 引言
  • CSS实现内凹圆角边框技巧(高频)
  • 【C++】用哈希表封装unordered_XX
  • 西游记24-26:万寿山,五庄观,镇元子;猴子偷果,猪八戒吃人参果——食而不知其味;逃跑被抓回,替师傅受罚;到处求仙,最终观音菩萨救树
  • Qt数据结构与编码技巧全解析
  • LeetCode 2140. 解决智力问题
  • 力扣(滑动窗口最大值)
  • LeetCode 刷题【53. 最大子数组和】
  • 一篇文章拆解Java主流垃圾回收器及其调优方法。
  • 详解 torch.distributed.all_gather_into_tensor
  • 15.examples\01-Micropython-Basics\demo_yield_task.py 加强版