事件驱动架构正在重复微服务的老路
打开技术论坛,满屏都是Kafka、RabbitMQ、Saga模式、事件溯源这些词。随便点开一个架构设计分享,八成在讲怎么用事件流解耦系统。这场景是不是特别眼熟?回想一下2014年左右,微服务也是这么火起来的。那时候随便一个CRUD应用,不说自己用了微服务都不好意思见人。现在轮到事件驱动架构了,好像不往系统里塞个消息队列就落伍了。
有个网友说得好,他在LinkedIn上喊了几个月“事件热就是新的微服务热”,结果没人听,大家跟旅鼠似的往悬崖下跳。这话听着扎心,但确实戳中了痛点。技术圈总是这样,先造神再毁神,微服务被喷得体无完肤之后,总要找个新偶像来崇拜。事件驱动架构正好撞枪口上了。
但仔细想想,微服务和事件驱动本来就不是对立关系。很多系统是微服务架构,内部用事件来通信。问题是很多人把手段当目的,为了用事件而用事件,就像当年为了拆微服务而拆微服务。一个数据库事务加后台worker就能搞定的事情,非要引入Kafka集群、搞事件溯源、处理最终一致性,这不是自己给自己加戏吗?
事件驱动架构的真正价值在哪里
说到底,事件驱动架构解决的是特定场景下的问题。什么场景?多个独立系统需要协作,而且这些系统不能共享数据库,不能同步调用对方接口。比如电商下单后要通知库存系统减库存、积分系统加积分、物流系统生成运单。要是用同步RPC调用,任何一个系统挂了,下单就失败。用事件驱动,订单系统发个“订单已创建”事件就完事,其他系统各自消费,互不影响。
有30年经验的架构师指出,共享数据库集成模式太脆弱了,批处理和ETL架构问题一堆,事件驱动在集成异构系统时韧性明显更强。这话在理。数据库不是API,把数据库当成系统间的集成点,相当于给所有系统开了一个后门,谁都能直接读写别人的数据表,耦合得死死的。事件驱动通过事件契约来通信,系统之间只依赖事件结构,不依赖具体实现。
还有人提到,事件驱动在处理复杂工作流时特别有用。比如视频处理应用,一个视频要经过AI生成、字幕制作、配音、转码、特效合成等多个阶段,每个阶段可能失败、可能需要重试、可能耗时不一样。用数据库事务同步处理?早超时了。用事件驱动,每个阶段独立消费事件,失败了还能重试,效率高得多。
最终一致性是个被过度妖魔化的概念
谈到事件驱动,很多人第一反应就是“最终一致性”太可怕了。数据不一致怎么办?用户看到旧数据怎么办?好像最终一致性是什么洪水猛兽。但仔细想想,很多业务场景天生就是最终一致的。你在电商平台下单,订单状态变成“已支付”之后,积分到账可能需要几秒钟,物流信息更新可能需要几分钟。用户会因为这几十秒的延迟崩溃吗?不会。
有开发者直言,最终一致性被我们妖魔化了,大多数情况下只是数据延迟一两秒而已。真正该担心的是领域边界划分、事件粒度设计、事件版本管理和并行处理这些硬骨头。这些才是事件驱动架构的真正的龙。
当然,有些场景确实不能接受最终一致性。比如银行转账,用户账户扣了钱但对方没收到,这不行。但这是业务特性决定的,而不是事件驱动的问题。交易系统需要强一致性,那就用数据库事务,别硬上事件驱动。架构选型要尊重业务规律,不是所有问题都是钉子,也不是所有工具都是锤子。
操作复杂性和调试难题是绕不过的坎
事件驱动架构最大的代价是认知负担。以前看代码就能理解业务流程,现在得追踪事件流。一个事件从这里发出去,经过消息队列,被多个消费者处理,每个消费者可能又产生新事件。要想调试一个问题,得把整个事件链路串起来,日志要关联、链路要追踪、时序要对齐。
这不是不能解决,而是需要额外投入。有团队分享经验说,他们用事件溯源加事件驱动,效果还不错,但必须重金投入可观测性建设。链路追踪、分布式日志、监控告警,这些基础设施缺一不可。如果公司连基本的监控都没有,就直接上Kafka,那出了问题就是抓瞎。
另一个被低估的痛点是幂等性。消息可能重复消费,这是消息队列的基本特性。消费者必须自己保证幂等,即同一个事件处理多次和一次效果一样。这听起来简单,做起来全是细节。用数据库唯一键去重、用Redis记录处理过的消息ID、用版本号做乐观锁,哪种方案都有坑。
很多人高估了自己的需求
有个评论特别犀利:大部分工程师连大学都没上过,没学过系统设计课程,不知道LeetCode是什么,他们就是在工作中边干边学。这种情况下,CTO说“为什么我们不能建X?你们不能强制执行一个能用的架构吗?”现实是,有能力的人能搞定,否则就是不行。
这话虽然扎心,但点出了一个事实:架构的复杂度必须和团队能力匹配。一个五人小团队,做的是内部管理系统,日活几百人,搞什么事件驱动?单体加数据库事务,轻松愉快,出了问题好排查,新人来了好上手。硬上事件驱动,光是Kafka集群维护就能占掉一个人力,更别说事件版本升级、消费者偏移量管理这些破事了。
还有个反直觉的观点:微服务和事件驱动往往是被组织架构逼出来的。公司并购了不同团队的产品,系统之间需要集成,但又不能大动干戈改代码,只好用事件来解耦。这是合理的。但如果从零开始搞新产品,一开始就搞事件驱动,很可能是过度设计。大多数情况下,从单体开始,等真正需要拆分时再拆分,比一开始就分布式要明智得多。
分布式系统从来都是最后的选择
有经验的架构师都明白一个道理:能不分布式就别分布式。单体在绝大多数情况下技术上都完全够用,驱动拆分的理由主要是组织层面的,比如团队太大需要并行开发、不同模块需要独立部署。但这些组织问题不一定非要用技术手段解决。
有人反驳说,从没见过哪个像样的企业级单体不需要和上下游系统集成。但集成和拆分是两码事。一个单体系统可以通过事件或API和外部系统通信,但它内部依然可以是一个完整的应用。把内部逻辑拆成多个微服务,每个微服务又用事件通信,这不叫集成,这叫自我分裂。
更扎心的是,分布式系统永远是最难调试、最难做性能、最难迭代的。只有在已经解决了领域问题、明确知道需要分布式的情况下才应该上。别因为觉得“可能以后需要”就提前分布式,那样开发速度会直接归零。先跑起来,再优化,这是软件工程的铁律。
架构决策要回归业务价值
有个评论说得很到位:没有哪种架构模式自带道德优越性。架构本质上是识别风险、设计缓解措施、权衡利弊。如果选了事件驱动引入的风险比解决的问题还多,那这就是个糟糕的架构决策。
判断的标准很简单:能不能把技术决策追溯到业务价值?如果引入Kafka是为了支撑订单峰值100倍的流量增长,这是有业务价值的。如果只是为了简历上多一行“精通Kafka”,那还是省省吧。同样,如果拆分微服务是为了让两个团队独立迭代、互不阻塞,这是有业务价值的。如果只是为了跟风,那等于是主动给自己找麻烦。
好架构是演进出出来的,不是设计出来的。先做个能跑的单体,监控哪里是瓶颈,再把瓶颈拆出来。先看看同步调用是否真的慢,再考虑上消息队列。先确认是否有多个团队需要独立发版,再讨论要不要微服务。顺序反了,步步都错。
总结
事件驱动架构不是新微服务,它比微服务更古老,存在了二十多年。两者是不同维度的概念,可以共存也可以独立使用。把事件驱动当成银弹,和当年把微服务当成银弹一样荒谬。技术选型要尊重业务规律、尊重团队能力、尊重客观需求。先跑通、再优化、最后才分布式,这个顺序永远不过时。
极客一语道破
事件驱动最大的问题是引入发送和接受两方;而微服务就是一方,可以被动调用,可以主动调用。
被动的好处是轻便携带,微服务可以实现在任何地方;而EDA一旦实现就强制约束,EDA这个词本身将双方强行捆绑在一起,当然,你可以说将事件发给总线就可以了,发送方和总线也是耦合了两个,在编写代码时就耦合了两个点,两点成一直线,两个点就组成系统,就组成上下文。所以使用EDA,实际就是在一个新场景中使用一个上下文模式,很重,很难,Context+Context双重导致思考过度。虽然EDA在执行时是异步,但是这种异步代价太大。这就是EDA无法流行起来的根本原因。