架构设计方法:别再迷信微服务了,先看看这几种坑
最近在群里看到一个消息,某大厂把微服务改造给回退了,所有服务重新合并成一个单体应用。这事挺有意思的,去年他们还在高调宣扬微服务改造成功,今年就悄悄回滚了,连个公告都没发。其实类似的事这几年见多了,每次看到都忍不住想说,架构设计根本不是选择题,而是一道应用题。你选的不是最潮的方案,而是最适合当前团队和业务的方案。
说到架构设计方法,很多人的第一反应就是微服务、分布式、容器化这些时髦词。但说真的,我见过太多团队被这些词带偏了。一个二十人的团队,非要去搞几十个微服务,结果运维排班都得排到半夜,监控告警吵得人焦虑。架构设计这件事,本质上是在约束下找最优解。约束是什么?是团队人数、技术水平、资金成本、业务复杂度、时间窗口。你得先承认这些约束,然后再谈方法。
好的架构设计,是让团队睡得着觉的设计
我记得有个朋友跟我吐槽,说他们公司引入了一个微服务框架,然后花了两周时间配置服务发现、负载均衡、熔断降级,真正写业务代码的时间不到一天。后来上线第一天,服务间调用超时,导致整个系统雪崩。他们架构师在群里说,你们要优雅降级啊!问题是,业务方根本没有降级的预案,因为没人跟他们讲过这个微服务架构会带来什么新问题。
好的架构设计不是画一堆漂亮的架构图,也不是用了多少中间件,而是让团队每个人都清楚系统是怎么运行的,出了问题知道去哪里看,改了一行代码能预判影响范围。用大白话说,就是系统别老捅娄子,万一捅了,你也能快速修好。这个听起来简单,做到却很难。很多架构设计在纸面上无懈可击,但一遇到真实的流量冲击就原形毕露。关键在于,设计时要考虑到人的因素——团队的认知负荷、沟通成本、技能短板。你让一个只会写SQL的团队去维护一个消息队列,那是灾难;你让一个没有DBA的团队去搞分库分表,那也是自找麻烦。
微服务架构拆分演进示意图
架构设计方法三板斧:业务域划分、技术选型、演进规划
根据我这些年的观察,真正有效的架构设计方法不是什么玄学,就是踏踏实实做好三件事:第一,划分业务域;第二,选择合适的技术;第三,规划演进路径。
业务域划分这事,很多团队喜欢按功能模块来分,比如用户模块、订单模块、支付模块。但实际业务往往是跨模块的,比如一个下单流程要涉及营销、库存、物流、积分。如果你按功能模块去拆分微服务,那写一次下单逻辑可能要调用五个服务,分布式事务还没法搞,最后只能弄个最终一致性,然后发现业务上根本没法接受。我比较推崇的做法是,先画业务事件图,看看核心业务在哪些地方产生了关键事件,比如订单已支付、库存已锁定、物流已发货。围绕这些事件去划分领域边界,每个服务负责一个完整的事件链,这样数据的一致性边界就清晰了。
技术选型这块,我的原则就是:能用成熟稳定的,就别用花里胡哨的。比如你用个MySQL就能解决问题的,别非得引入NoSQL,除非你真的很清楚自己的数据访问模式是哪种。用个HTTP接口能搞定的,别上来就上RPC框架,除非你真的达到了上万次调用的并发。架构设计不是技术狂欢,而是成本控制。这里说的成本不只指钱,还包括团队的学习成本和维护成本。举个例子,有个创业公司,为了“未来可扩展”,一开始就上了Kafka,结果整个团队没人懂Kafka的消费模型的调优,经常消息积压,后来换成了RabbitMQ,问题立刻少了一多半。
演进规划,其实就是承认架构是会变的,然后你提前留好变动的余地,而不是一开始就设计一个终极形态。我见过最极端的一个例子,有个团队花了半年时间设计了一个“完美”的微服务架构,等到开发的时候,业务已经改了N轮了,之前的架构设计基本作废。所以,架构设计应该是在一个简单的骨架下,快速做出原型,然后通过实际业务的反馈不停迭代。就像建房子,你先打好地基,架好柱子,然后房间的隔断可以慢慢调整,而不是一开始就把所有墙体都砌好。
事件驱动架构消息流示意图
实战中常见的坑,你都踩过几个?
实战中常见的坑,你都踩过几个?
先说说分布式事务的坑。很多团队一上微服务,就面临着跨服务的数据一致性问题,然后想方设法去搞分布式事务,什么两阶段提交、TCC、Saga。说实话,这些技术不是没用,但复杂度极高,我见过不少团队为了搞定一个分布式事务,引入了三四个中间件,最后生产环境还是一堆问题。实际上,很多业务根本不需要强一致性。比如用户下单后,只要保证最终的库存扣减和订单状态一致就行了,中间有几个秒级的延迟完全可以接受。这时候用事件驱动加本地消息表,就比一上来就搞TCC框架要靠谱得多。
再一个坑是过度设计。比如为了“高并发”,一开始就上Redis缓存,结果业务量每天只有几百次请求,缓存带来的数据一致性麻烦却让开发效率低了一大截。还有为了“高可用”,每个服务都搞了三个副本,结果资源浪费严重,费用翻了几倍。架构设计要给未来的增长留空间,但这个空间需要适度。我的经验是,先根据当前业务量的一倍到三倍来设计系统的容量,当业务真的到了瓶颈再扩展也不迟。毕竟,腾讯、阿里的架构也不是一开始就是那样的,都是被用户流量逼着进化出来的。
另外,很多团队在架构设计时忽略了“人”的因素。比如某个老系统是PHP写的,团队里都是PHP高手,结果为了追赶潮流,非要用Java微服务重写,那就要考虑前期投入的学习成本和风险。有时候继续沿用现有的技术栈,在原来的基础上做模块化拆分,反而是一个更稳妥的架构设计方法。我能理解大家对新技术的渴望,但请理性的评估一下团队的现状。
回到本质:用简单方法解决复杂问题
回到本质:用简单方法解决复杂问题
最近这两年,业界开始流行一个词叫“模块化单体”。就是说,把单体应用按照业务域来划分若干个模块,每个模块内部高内聚,模块之间通过明确的接口通信,但最终打包成一个应用部署。这样既能享受单体应用的简单性,又能避免微服务带来的分布式复杂性。我觉得这是一个非常务实的架构设计方法,特别适合中小团队。你可以先把整个系统的边界划清楚,然后随着业务和团队的成长,再把其中的某些模块单独拆出去成为独立的服务,这是一种平滑演进的方式。
事件驱动架构也是一个值得关注的方向。它和传统的请求响应模型很不一样,核心是让每个服务关注自己的领域事件,通过异步消息来解耦服务之间的依赖。我最近读过一篇文章,讲的是某个金融公司通过引入事件风暴来驱动架构重构,把原来乱成一锅粥的系统梳理得清清楚楚。具体做法是先召集业务方和技术方,一起讨论核心的业务事件,比如“贷款申请已受理”、“银行卡已绑定”、“月供已扣”。把这些事件画成一张大图,然后看哪些事件是跨部门的,哪些是同一个服务内部的。这个过程中,你会自然发现很多服务之间的耦合关系,然后你就知道应该如何划分模块了。这个方法不依赖什么高深理论,就是一个很好的架构设计分析方法。
最后说一句,架构设计方法没有标准答案,但有底线思维。底线就是:快、能改、死不了。快的是开发效率要高,能改的是要适应业务变化,死不了是指系统要能在故障中恢复。你用的方法是否高大上其实不重要,重要的是你是否一直在这三个底线上做文章。我见过不少团队,把时间花在争论“用Istio还是Linkerd”上,却忽略了最基础的业务日志分析,结果系统一卡顿根本无从查起。与其追求那些花架子,不如回归基本功,把你的服务分解策略、数据流走向、监控告警设计做好。这才是架构设计的关键。
写这篇文章,不是说微服务不好,也不是说架构设计可以随意敷衍,而是想劝大家,在每次纠结用什么架构方案之前,先深呼吸,想想你的业务现状、团队能力和未来的不确定性。架构设计是产品探索的一部分,不是纯技术输出。你做出的每一个设计决策,都会影响未来一年甚至几年的开发和运营成本。所以,别为了简历上写几行炫技的文字,就去做草率的设计。踏踏实实,从业务本质出发,用最直接的方法解决问题,这才是架构设计最该有的样子。