目录

多实例单体部署真的靠谱吗?

现在可不是所有人都觉得微服务架构是首选的。有些人觉得没必要什么业务都搞微服务,大单体仍然是有意义的。

但是我认为凡事并不是那么绝对。
在实践中,有些人似乎把贯彻大单体的思路执行歪了。由于各种原因,系统开发采用大单体架构,最终落实到部署时采用的却是大单体应用多开的部署方案。

这种多实例单体部署真的靠谱吗?

在前几年,诸如分布式、微服务这样的词语伴随云服务的概念一起流行起来,很多项目被开发或者改造为了微服务架构。
在微服务架构流行之前,服务的部署以单体为主。
所谓单体,是指一个业务被封装为了一个单独的整体,部署的时候对这个整体直接运行进行部署。

我的老师曾经就对我说过,现在的服务运维部署已经跟他们当时的情况截然不同了。当时启动一个服务,是配置好所需的环境以后,将服务端程序直接双击打开就能启动了。
拿Java应用举例,一开始的时候流行的是Tomcat,相当于有一个应用池,一个应用可以被封装为war包,放入应用池中即可载入这个服务端应用了,此时的服务端应用就像是Tomcat的一个插件,能插能拔。于此思想类似的还有IIS,在一台Windows Server的服务器上要部署一个.Net Framework服务端程序的话,也是在IIS里创建一个站点,配置好应用,此时IIS也相当于一个应用池,很多应用运行在这个应用池中。
后来流行起来了“真正”的大单体应用,它极大地简化了服务端本体的部署难度。比如Java生态中,流行起来了Spring框架,后来又基于Spring发展而来SpringBoot,可以将服务端本体与Tomcat一起打包为一个jar文件,运维只需要在配置完毕后,通过java -jar server.jar这样的启动指令就能完成本体部署了。

但是在实践中,大家渐渐发现了大单体架构存在一些问题。

  • **对于用户而言,面对用户的大量请求时,大单体架构难以控制风险范围。**具体而言,如果某一个部分的工作出现了问题,在大单体架构上难以通过将这一部分逻辑“屏蔽”掉的方式阻止风险蔓延。大单体往往是一个地方崩,整个服务端一起崩
  • **对于开发而言,不能灵活安排开发任务。**大单体里肯定有各个不同的业务模块,这些模块最终都要合并到一个单一的大单体中,不同人员的协调工作就成了问题,例如代码的合并的冲突问题,不同的业务需要彼此之间配合对方的开发排期。不同的业务也不能按照各自的业务特点灵活选择更适合这个业务的开发语言。

面对这些问题,自然就有了一些人提出这样的想法:能不能根据业务的不同,直接把业务拆成多个服务端,一个业务单独一个服务端,需要共享数据就相互请求一下,平时各个业务自己处理自己的

我想,这个看起来比较天马行空的想法,赶上了个好时候。
当时恰好流行起了云计算,云计算的一大噱头就是动态伸缩
如果你不采用云主机,你开展业务就必须得组装对应的主机,哪怕是租用VPS也只能根据业务需要选固定的型号。但是云计算允许你需要多少自己去开对应配置的实例,业务需求大的时候就多开点,业务需求小的时候就开小点。
于是微服务的诞生在这个时代机遇下就多了一个理所应当的理由:为了适应云计算的动态调整需求,单体服务一定得根据业务拆散,让其能够根据业务需求进行动态调整。比如此时此刻的用户评论相关业务压力大,就多开一些云实例来跑用户评论业务,要是订单业务压力比较大了,就再多开一点订单业务的云实例。云实例用完以后可以及时释放掉。

可以说,为了更好处理用户请求,增加系统吞吐量,也为了协调多部门的开发工作,同时赶上了云计算流行的时代机遇,微服务这个东西像是含着金汤勺的公主,天生骄傲,理所当然地诞生,也理所当然就流行起来了。

既然微服务体系是一个基于现状、开发需求和时代背景的必然结果,那为什么又有些人开始说要整起来大单体了呢?

首先是有很大一部分清醒的人点破了这层窗户纸,业务的处理能力应该极大程度上依赖于开发的设计能力,而不是架构的盲目改变
互联网早期本来就是大单体的。有很多百万级用户、高峰几十万并发甚至更高的业务其实在之前的单体服务下跑起来是没啥问题的,否则微服务流行之前互联网还不存在了吗?

再加上是近些年里一些大厂的实践分享带动的风气。
例如在2023年,Amazon的Prime Video团队发布了一个案例研究,说他们将一个监控系统从微服务架构迁移到了单体架构,然后还尽可能去掉了类似云函数这样的云服务,这样做让他们减少了系统中的不必要的复杂性,反向操作居然实现了降本增效。
这个风气也带动了一些营销号跟风推崇,毕竟大厂都这么干了,我们这等垃圾为什么不立刻跟上?

先抛开开发工作组织的问题,单纯论技术实现的方面的话,之前的大单体能承载住用户流量嘛?
答案显然是可以的,否则我们也没办法解释淘宝这样的购物网站是怎么跑起来的。

但是单体如果加上单机部署,应该是很难承载大量的并发的,并且也比较难保证单体应用的高可用,所以多实例的单机部署变得很常见了。
多实例单体业务体系下,同一个单体应用被打包为多个副本,通过某些负载均衡手段分发请求。
在最早的时候,负载均衡直接依靠DNS来完成分流。可以把一个单体应用部署在一个服务器主机上,然后部署多个服务器主机,每个主机都有一个IP地址,DNS解析的时候给同一个域名添加多个主机的解析,用户解析DNS地址的过程形成了自然分流。
后来衍生了各种负载均衡服务,通过Nginx或云厂商的LoadBalance服务分发请求。

说到底,多实例大单体并不是一个彻底的新东西,其实就是早期互联网本来的主流。

我觉得,多实例大单体的本质只解决了高可用的表层问题,实际上是没有触及单体架构的核心缺陷的,这反而可能放大运维与扩展的隐形成本。

在大单体模式下的开发工作中,各个模块集成在同一个应用里。对于小公司来说的话,开发可能本来就没几个人,搞微服务说是在让每个人各司其职,其实是中看不中用的,不适合小公司的开发。

但是反过来说的话,单体应用的所有模块都打包在一个包里,修改任何一个小功能都需要进行全量测试、打包和部署。
这样做对于一个开发早期的项目而言可能是无伤大雅的,因为每个模块都在快速迭代,很有可能某几个服务的变更较大,带着整个服务都发生了较大的变动。这样来看,大单体可能更利于小团队开发早期的快速迭代。
但是项目正式上线运营后,大单体就有点不方便运维了。**多实例部署大单体,所有副本为了避免版本不一致导致的业务异常,必须同步更新,难做到针对业务模块的灰度发布、蓝绿部署、金丝雀发布。**如果硬要做到,那必须得让开发与运维共同配合实现复杂的路由控制逻辑,有点得不偿失。
但是这些问题在微服务体系下是不怎么严重的问题,开发完全可以修改对应的服务模块,运维直接安排上线就行了,部署风险被隔离在了单个模块里。

单体的所有模块共享服务器资源,但是不同模块的负载特征差异可能很大,有些服务可能是CPU密集型的,而有些可能是IO密集型的。
在多实例大单体需要扩展时,只能进行整体扩容,没办法针对高负载模块单独扩展,那低负载的模块可能会存在资源闲置(某些模块明明不是热点,被迫跟着被扩容了)。

大单体模式下的数据库维护也是有挑战性的。
多实例大单体可能很难拆分数据库,因为要保证各个单体实例都要有一致的数据对外服务,所以每一个单体实例连接的数据库里有系统的总的全量数据。这样做会增加数据库的压力也有待讨论。

单体代码量随业务增长持续膨胀,模块间依赖混乱,假如后面出了bug要修改,也可能引入新问题。
多实例单体只能用一套技术栈,但不同模块的技术需求可能完全不同,而微服务可针对每个模块独立选择技术栈。

多实例单体需要维护多个完全相同的应用实例与统一的数据库集群,一旦出现问题(比如发生了内存泄漏),所有实例都会受影响,排查时需同步检查多个实例的日志,定位效率低。
但是微服务是可以通过熔断和服务降级来隔离故障的。

刚才在讨论大单体模式的运行资源问题的时候就谈到了数据库,主要是提到了性能层面。
但是细想起来还有运维层面的问题。

多实例大单体的数据库无非是两种情况,要么是所有服务都连了同一个数据库,要么就是整出来了一个数据库集群。并且多实例单体的数据库通常是单库单表或者主从复制的。
当业务增长到一定阶段,也会出现表级锁竞争(多个单体同时操作一个表)、索引优化处理(不同模块的查询需求冲突)、备份/恢复操作的风险(比如全库备份时间长,恢复影响所有模块,备份恢复过程可能会有锁影响别的业务等)等问题。
某个业务记录了时序数据,这类数据往往需要涉及到分库分表,如果数据都混在一起的话,万一运维在做冷数据归档下线操作的时候做了错误操作,也可能会导致整个数据库出现问题。

在我看来的话,业务增长期、团队规模扩大、模块负载差异显著的场景,大单体是没什么优势的。
但是既然有这样的声音,那一定有这样的需求。讨论多实例大单体部署什么时候靠谱也是有一定意义的。

  • 初创阶段或者小型业务:业务如果核心诉求是快速验证,整体低成本的话,不设计微服务拆分、服务注册发现、分布式事务等复杂机制。此时只需维护Nginx负载均衡+几个应用实例+数据库主从,无需搭建微服务生态,能让业务快速上线。多实例部署确实也缓解了单实例宕机导致服务不可用,满足基础可用性需求。
  • 很简单或者联系很紧密的业务:一个内部办公系统(如考勤、审批等等),模块间依赖紧密,无明显负载差异的话,可能上微服务的话更像是“为拆而拆”了,额外增加复杂度,说不定单体成本更低。

从这个角度思考的话,其实与其说微服务和大单体谁好谁坏,本质上是一个关于拆分粒度的架构设计问题。
拿出一个完美的架构设计确实是一个吃经验又吃能力的狠活,我感觉架构讨论不是看几篇营销号文章就能琢磨透的问题。
如果不琢磨明白背后的道理,这样的讨论没什么实在的意义。

我觉得在架构讨论中最容易陷入的误区,是将单体与微服务置于非此即彼的对立位置,纠结于谁更先进,而不是纠结谁更适配。

事实上,所有架构决策的核心都围绕拆分粒度展开,从粗到细的粒度选择,本质是业务复杂度、团队能力与增长预期三者动态匹配的过程,不存在一成不变的最优解,只有随阶段调整的合理选择。

站在现在的眼光来看,粗粒度架构(多实例单体)的价值,从来不是作为微服务的替代品,而是在业务初期或者小业务提供低成本启动的最优路径。
对于尚未验证市场的业务或资源有限的团队,过度追求细粒度拆分反而会陷入架构先行、业务滞后的问题。
多实例单体无需投入精力设计服务边界、分布式事务或服务生态,将所有模块整合为一个应用,通过少量实例部署与数据库主从复制实现基础高可用,既能快速上线验证需求,又能将开发与运维成本控制在最低。这种选择并非妥协,而是对业务优先级的清醒认知。做开发和做架构偶尔还是得算一下经济账,权衡时间、人力与实际需要的成本。

而当业务进入增长期,粗粒度架构的隐性矛盾会逐渐显性化,此时拆分粒度的精细化便成为必然了。这主要是因为:

  • 随着用户规模扩大、功能模块增多,单体应用的耦合缺陷会不断放大:一个模块的微小修改需要全量部署,高负载模块的资源需求会被低负载模块拖累,代码库的膨胀导致维护效率下降
  • 多实例单体带来的这些问题无法通过增加实例数量解决,因为其根源在于架构层面的模块绑定,而非部署层面的实例不足 此时,微服务的细粒度拆分本质是解耦与弹性的双重实现:
  • 按业务域拆分后的独立服务,可根据负载特征单独扩容,按迭代节奏独立部署,甚至根据模块特性选择适配的技术栈,让资源配置与业务需求精准匹配
  • 这种拆分不是对单体架构的否定,而是架构随业务复杂度升级的自然演进,就像城市发展到一定规模后,需要将交通、商业、居住功能分区规划,才能支撑更高效的运转。

正如刚才所说的那样,讨论微服务与大单体的时候压根就不应该以静态的眼光来看待问题。
没有一步就能做成的业务,无论是多大的业务都是需要一步一步来做的。哪怕是起手就是微服务架构,各个模块根据实际需要拆分服务仍然是一个类似“微服务和大单体矛盾”的问题。

另外,架构演进的关键在于 “渐进式拆分”,而非 “一刀切” 的彻底重构。
我一直觉得,成熟的架构设计必然包含未来演进的预留空间的:

  • 理想情况下,在单体阶段就需梳理模块间的依赖关系,避免形成无法拆分的紧耦合
  • 当拆分时机到来时,优先选择高负载、高变更频率的模块作为突破口,而非一次性拆分所有功能,比如先将计算密集型的核心业务剥离为独立服务,通过消息队列实现与单体系统的异步通信,数据层面采用双写策略保障一致性,待新服务稳定运行后,再逐步拆分其他模块。
    小步快跑的演进模式,或许能规避一次性重构的风险,又能让团队在实践中积累微服务经验,避免因能力不足导致的架构失控。
    同时,拆分粒度也需把握适度,毕竟过度拆分会导致服务调用链冗长、分布式事务复杂,反而降低系统稳定性,最终形成分布式单体的更糟局面。

回到题目,多实例单体部署真的靠谱吗?
我觉得这是一个需要和稀泥的问题——大家都靠谱,盲目往哪个方向一把梭都是不对的。
因为说到底,架构选型的本质是一门动态适配的艺术。

现如今,粗粒度的多实例单体适合业务初期的快速验证,适合小项目的快速成长;而细粒度的微服务适合业务增长期的高效支撑,无论是对于运维还是开发,都提供了更灵活的解决方案,两者之间不存在绝对的优劣,而是架构演进的不同阶段。

我觉得,优秀的架构设计从不执着于某种固定模式,而是根据业务当前的核心诉求,选择最适合的拆分粒度,并为未来的演进预留灵活调整的空间
它不是静态图,而是随业务成长不断优化的动态系统,最终目标是让架构成为业务增长的助推器,而不应该在这个问题上天天逼逼赖赖,让他成为束缚业务的枷锁。
其实我也一直不明白,为什么大家总在一个问题上搞非黑即白的对立,需要啥就用啥呗,每个人都有每个人的需求,我们应该让每一次架构调整都精准匹配当前阶段的真实需求。