
1. 从狂热到理性的必然转身前两年你要是说自己在做微服务架构那眼神都带着光这两年的技术分享会上越来越多的人开始坦诚地说其实我们不该一开始就拆那么碎。热度退潮之后反而能看清微服务真正适合什么场景不适合什么场景。这篇文章就围绕微服务架构选型这件事聊聊我自己一路从“拆了再说”到“能不拆就不拆、拆了要有拆的理由”的转变过程给正在纠结架构方案的团队一点参考。这个选题适合谁适合刚准备做架构升级的团队负责人适合被运维压得喘不过气的后端开发者也适合那些已经开始微服务化但是总觉得哪里不对的技术管理者。我的结论可能会让一些人大跌眼镜但我觉得这是踩过坑之后最真实的声音微服务不该是默认选项它应该是在特定条件下才值得选择的方案。而且即便决定上了微服务怎么拆、拆多大、数据怎么处理每一步都藏着学问。说完这些我想先把当年那股“拆服务热”是怎么升温的捋清楚。因为理解了狂热从哪里来才能真正理解为什么现在大家不谈微服务了反而更会做微服务了。2. 为什么人人都想冲进微服务的坑2.1 那时我们把微服务当成万能解药回忆一下大概五六年前微服务的概念在技术社区里迅速升温。当时的信息传播方式和一系列成功的公开案例把微服务的优势放大了好几倍。很多团队看到的是独立部署、故障隔离、技术栈灵活、团队自治这些词每一个都打在痛点上。那时候我们团队也面临一个典型的增长问题某个系统的代码已经膨胀到几个人的脑子装不下的程度发布要挑深夜一个小改动要拉着全局回归测试线上出问题定位靠翻日志百度和运气。于是很自然大家就把目光投向了微服务。负责架构的同事做的方案里画满了方框和箭头每次评审会都像在描述一种理想化的技术社会每个小方框各自为政中间用漂亮的协议连接起来。当时的我虽然有一些疑虑但说不清楚问题出在哪只能跟着大流往前走。事后反省这种心态就是一种典型的“锤子效应”手里拿着微服务这把锤子看什么系统都像钉子。现在回头看推动那波热潮的另外一股力量是社区讨论的圈层效应。技术群里聊的是服务拆分成多少份、用哪种服务网格、怎么搞灰度发布。你要说我们在用单体应用瞬间觉得自己落伍了一个时代。这种舆论压力很容易让技术判断变形不是从业务实际出发而是从“别人都在做”出发。2.2 热度过后的冷静信号从去年到今年业内风向开始明显变化。不是微服务本身被判了死刑而是大家开始比较“拆”和“不拆”的账。很多人意识到微服务不是免费的它把复杂性转移了而不是消灭了。原来你只有一个大程序的复杂度现在你有几十个小的复杂度外加一套让它们协作的复杂度。信号之一是很多技术团队开始讨论模块化单体就是把代码按业务模块分好目录、定好边界、用编译期或进程内隔离的方式来管理复杂度但先不拆成独立服务。信号之二是像亚马逊那样以极大规模微服务著称的公司内部其实也在反思服务拆分的粒度到底该多大。连最激进的实践者都在往回找平衡点这本身就说明问题。不过我个人特别反感那种“微服务已死”的非黑即白论调。理性的状态应该是在正确的时间、正确的地点、正确的团队条件下使用微服务其他地方老老实实待着。接下来就说说当你真正把微服务放到生产环境里跑一年之后真实的账单长什么样。3. 微服务真实成本拆解3.1 运维复杂度并不是翻倍而是指数级我认识很多团队包括我们自己刚上微服务的前半年都在一种“觉得自己很现代”的兴奋中度过。真正反应过来的时候是在一次故障处理现场。以前单体时代一个请求链路是清晰的从网关到控制器到服务到数据库最多两三个节点。拆了之后一次用户操作要穿越六七个服务每个服务都有独立的日志文件、独立的指标数据、独立的依赖版本。那一次线上告警我们排查了快三个小时。先确定是哪个入口服务报错再顺着链路一个个服务翻日志中间发现有个服务的日志级别没调好丢了一部分关键信息又回头去补日志重新复现。整个过程最让人崩溃的不是故障本身而是你连故障发生在哪个服务里都无法第一时间确定。单体时代的定位思路完全失效了你需要一套完整的链路追踪体系、集中的日志平台、统一的告警规则。说到运维成本有一个容易被忽略的细节每个服务都有自己独立的构建、部署、配置管理、依赖升级、安全补丁流程。一百个服务的升级难度不是一百倍而是一百个需要各自处理的小项目。单纯把配置中心、CI/CD、监控告警这些基础设施搭好一个五六人的团队至少得投入两个月的全职工作量。这笔账很多人拆服务之前根本没有算过。3.2 数据一致性是迟早要还的债业务系统里最难处理的其实是数据。单体的优势在于一个事务可以跨多张表保证强一致出问题可以直接回滚。拆成微服务后数据被按服务边界拆开了原来一个事务里搞定的事情现在要跨服务跨数据库去处理。最常见的订单场景就是典型例子。用户下单订单服务和库存服务分开部署库存不能超卖订单不能丢单还要保证最终一致。你得引入分布式事务框架、消息队列做补偿或者事件驱动的Saga模式。这些方案的实现难度打过交道的都懂光是处理消息重复消费和事务回滚补偿就能让一个团队忙活相当长时间。我们自己实践的教训是能用一张表解决的数据就不要拆到两个服务里能用同一个数据库解决的就不要引入分布式事务。数据边界必须紧紧跟着业务边界走但前提是这条业务边界本身是真的需要独立成服务。很多团队在领域划分上偷懒按数据表来拆服务结果服务是拆开了数据耦合还在事务问题成了甩不掉的包袱。3.3 团队沟通成本被系统性低估微服务的一大卖点是小团队自治但多数中小团队拆了服务之后人还是那几个人沟通成本反而高了。以前改一个接口内部调用改了就行现在改一个接口要协调消费方一起发版。接口版本升级必须预留兼容期这在快速迭代的项目里是一件很磨人的事。我见过一个项目因为接口契约沟通不畅A服务改了自己的返回结构B服务解析失败线上出了整整一个下午的错。本来两个人在同一个代码库里可以直接通过代码review发现这种问题拆开之后反而要靠文档、靠约定、靠反复确认。团队人数不够、协作成熟度不高的时候微服务的组织优势根本发挥不出来。反而是单体里那种高效协作模式被打散了。数据清晰之后我们来认真回答一个核心问题到底什么样的系统、什么样的团队才真正适合微服务我把自己这几年的判断标准整理了一下不一定都对但是能帮助团队少走一些弯路。4. 理性选型什么时候该拆、什么时候绝不拆4.1 做架构决策前必须先回答的三个问题第一问你的业务复杂度是真的高还是团队自己觉得高我见过不少系统功能看起来挺多但认真梳理起来核心业务链路就那么两三条。数据关系也不复杂用户、订单、商品、支付没有复杂的审批流没有跨领域的异构集成。这种系统强行拆成微服务只会把业务复杂度变成架构复杂度和运维复杂度。第二问你的系统需要独立伸缩的部分是什么微服务的好处之一是不同模块可以分别扩容。有些模块CPU密集有些模块IO密集有些模块访问量是另一些的几十倍。如果你确实存在这种明显的资源特征差异微服务是有价值的。反之如果所有模块的负载曲线长得很像拆分之后对资源利用率的帮助非常有限。第三问团队有没有建立配套能力的精力和预算我自己把微服务相关的配套能力列了一个清单链路追踪系统、集中日志平台、统一配置中心、服务注册发现、网关与鉴权、CI/CD自动化、监控告警、容器编排或云平台。这个清单里的每一项都需要投入人力去搭、去维护。如果团队只有两三个人连需求都做不完那真的不建议走这条路。4.2 一张适合保存的架构选型决策表我把这些判断标准做成了一张表每次做技术评审的时候可以直接拿出来对照打分。它的判断逻辑不是定量到精确计算而是通过几个维度的体检式检查帮团队把思路理顺。评估维度适合微服务的信号不适合微服务的信号业务复杂度多个边界清晰的业务域各自有不同的演化节奏单一业务域流程线性改动集中在同一批代码团队规模有独立团队长期维护各自服务团队间协作成熟人数少角色重叠靠非正式沟通就能解决需求资源伸缩需求不同模块负载差异大需要独立伸缩与故障隔离全系统负载均衡整体水平扩展即可满足要求数据特征数据天然按域划分跨域事务少大量跨表事务数据强一致要求高交付频率各模块发布节奏差异大按各自节奏迭代所有模块一起发布发布周期统一基础设施能力已有或愿意投入建设运维自动化有独立运维人力只有基础的服务器或云主机无人专职做平台建设这个表不是想让大家逐项打分求个总分而是希望把平时模糊的争论变成一个可以逐条对齐的讨论。我们当时做决策的时候就是把团队成员拉到一起对着表逐行讨论。很多之前说不清楚的感觉放到这些维度里之后就比较明确了。4.3 回头说说我们最终的决定现在可以回到开头的那个项目了。我们当时经过这轮审视之后做了什么决定答案是我们没有把整系统全部拆成微服务而是选择了一条中间路线。核心业务模块比如交易链路保留在模块化单体阶段只是把代码结构按业务域做清楚用接口隔离模块之间的调用关系。真正拆出去的是几个独立性强、资源消耗特殊、发布频率和其他模块差异巨大的部分比如短信通知服务、文件处理服务、报表生成服务。这个决定看起来很保守但实际运行效果让我意识到架构选型的目标不是追求最先进的方案而是追求最合适的方案。后来的迭代里每次要改动核心交易逻辑我们依然在一个仓库里完成没有跨服务的协调成本。同时短信服务出问题不会拖垮主流程文件处理服务的高CPU消耗也不影响其他模块的性能。该灵活的地方灵活了该稳定紧密的地方保持了单体这就是我理解的理性。选择好路线了接下来是很多团队都会遇到的实操问题如果确实要落地微服务应该怎么控制风险、怎么避免走上一条不归路下面这部分是我从多次实践和几次失败中攒出来的经验。5. 如果要拆怎么拆才踩坑最少5.1 从代码边界而不是数据表开始我们做服务划分踩过的第一个坑就是按照数据库表来定服务边界。订单相关的表放一起做一个服务用户相关的表放一起做一个用户服务听起来言之成理但做起来极其痛苦。因为业务操作天然是跨表的下单要读用户信息、商品信息、库存信息、优惠信息如果这些分属不同服务每个简单的操作就变成一次分布式事务编排了。更好的做法是服务边界应该跟着业务能力走先把业务用例列出来按高内聚低耦合的原则把职责归堆。这里其实可以参考领域驱动设计里的限界上下文概念不用把整个DDD体系都学完核心是理解服务和数据库表不是一对一的关系一个服务可以包含多张表也可以和别的服务共享同库里的不同表当然数据物理分离的问题后面会提。举个生活化的类比以前的老式百货商场所有商品都摆在一层楼顾客逛一圈啥都买到。后来有的商品比如需要冷链的生鲜、耐仓储的家电单独开了专卖店既方便管理也避免影响主商场的客流。这就是按业务特征而不是按商品名归类的思路。5.2 数据库与事务的边界策略这里有着很多架构师不愿意直说的坑。一旦服务拆了数据库是物理隔离还是逻辑隔离会直接影响后续的演进空间。物理分离是你每个服务独立一套数据库实例逻辑分离是大家共用一个大数据库但各自服务只管自己的表。我们的经验是不建议一上来就物理分离因为数据库物理隔离之后跨服务的查询就变成了跨数据库的查询连表要走接口层聚合性能开销和实现复杂度一下子上升很多。逻辑分离会好一些数据库还是同一个但通过表命名和权限管理确定归属权哪个服务只能读写自己的表。这种方式在一段时间内能保持一定的数据共享查询能力业务对账、报表统计还走得通。不管是哪种方式跨服务的数据一致性都绕不开。如果你已经决定跨服务的事务必须存在那么没有必须强制要求强一致的尽量用事件通知加异步补偿的模式。比如下单扣库存订单服务创建订单成功后发一个领域事件库存服务监听事件扣减库存失败了通过重试或人工介入补偿。禁止在代码里同步调用一堆服务然后期待一个分布式事务把所有都搞定那种设计在低流量低并发时看不出问题一旦流量上来就等着一个节点超时整条链路重试风暴吧。5.3 服务拆分后的可观测性必须先行很多团队是在拆了服务之后才开始想到监控怎么做。这个顺序非常危险。你可以在没有完整可观测性能力的情况下运行一个单体但绝对没有办法运行好微服务。因为单体出问题你可以用线程dump、慢查询、访问日志来做排查微服务出问题如果没有链路追踪你连请求到底去哪了都看不到。所以我的建议是拆服务之前先把下面这三样东西建设好。第一集中日志平台所有服务的日志统一收集到一个地方支持按traceId关联查询。第二链路追踪系统每个请求从入口生成唯一ID透传全程能够展示各服务调用耗时和状态。第三监控告警体系每个服务至少要有请求量、错误率、P99延迟这三个指标告警规则要针对异常而不是等到用户投诉。这三样东西搭建起来大概需要什么样的工作量如果你们有云原生成熟产品可以直接用基础建设时间会大幅缩短。如果是自建日志平台加链路追踪加监控告警一个熟练的工程师大概需要两到四周。别嫌这个投入大如果没有可观测性撑腰微服务就像蒙着眼睛在高速公路上开车只能靠后续的故障倒逼你补课。5.4 演进思维比最终架构更重要还有一点想单独说一说就是架构不应该追求一次到位。微服务已经成为一种事实上的演进式设计言下之意你完全可以从模块化单体开始随着业务复杂度真实增长再把部分模块逐步抽出来变成独立服务。这个过程比一次性拆分要平滑得多因为每个抽取动作都是针对一个已经被生产流量验证过的边界。具体落地时可以先把代码模块的边界做清楚保持进程内调用后续如果某个模块的伸缩需求、故障隔离需求、独立发布需求变得强烈再把它抽取成独立服务。抽取的时候数据库可以暂时继续共享同一个库先用服务接口隔离访问再把数据逐渐抽离。这种渐进式路线给了团队充分的学习时间和调整空间比起一步到位的大手术要稳太多。我们现在的做法就是随时保留发展成更加彻底的微服务架构的可能但不会在当前阶段把不需要的能力全部提前支付。说完方法论与边界策略我想把常见问题集中整理一下特别是那些在实际项目里反复出现的问题。这些问题有些是技术实现层面的有些是组织协作层面的但它们的共性是不提前处理后期一定会爆发。6. 微服务落地的常见问题与排查思路6.1 问题一一个“简单的”查询变得特别慢拆服务最常见的现象是本来一条SQL就能把数据查出来拆完之后要一个服务接一个服务地调用然后再把数据聚合起来。这种问题的根源往往是划分服务的时候把原本高内聚的查询逻辑撕碎了。解决方案有两个方向其一是对查询场景单独做读模型专门建立一套可以满足聚合查询的数据表或视图通过最终一致的方式同步数据这就是CQRS里读模型的思想也是比较彻底的解法。其二是在不那么追求独立伸缩的服务边界上保持一定的跨库查询能力这个前面提到过前提是数据库物理隔离没有做死。我们实际项目里就用过第一种方式。报表系统本来要跨订单服务、商品服务、用户服务三个服务拉数据每个请求耗时在几百毫秒。后来我们把这三类数据通过订阅消息同步到了一张专门的报表宽表里查询一次性走数据库索引性能回到个位数毫秒。代价是多了一个异步同步链路但只要同步失败有补偿机制问题不大。6.2 问题二发布总是互相踩脚服务之间接口不兼容是很常见的协作难题。A服务改了接口响应结构B服务没跟上线上直接报错。这个问题我们在踩过几次之后强制了三条规则。第一接口版本必须显式管理URL或Header里带版本号旧版本至少保留一个发布周期。第二每个接口的变更必须有契约单记录改动之前先发变更知会给所有消费方。第三消费方必须做兼容性测试服务提供方要提供契约测试用例自动化跑一遍。这三条规则看起来是为微服务定制的但其实模块化单体也能受益。哪怕你不拆服务只是在代码里用接口做模块隔离那接口的变更同样存在。很多团队在单体时代写代码很随意觉得反正内部调用改了不通知拆成微服务之后还带着这个习惯那就只能线上事故相见了。6.3 问题三测试环境怎么部署都不对测试环境的治理也是微服务绕不开的痛点。服务一多测试环境怎么部署就成了一道复杂的管理题。全部一起部署资源开销太大启动时间太长只部署被测服务其他服务用什么方式依赖我们尝试过几种方案最终比较有效的是按需构建的轻量测试环境。也就是说一个完整的服务集群作为基座常驻运行被测的服务单独部署新版本然后通过网关路由把测试流量打到新版本上其他请求继续走基座。这套方案配合容器化之后测试环境的问题基本解决同时整个研发团队的迭代效率提高了很多。如果你所在团队的规模已经需要用一套标准办法来治理测试环境可以往这个方向想它比本地起全套依赖有效得多。6.4 团队协作方面最容易被低估的问题最后想提一个不是技术但比技术更容易出问题的层面微服务会让组织沟通结构直接映射到系统架构上。如果你组织内部信息传递本身就拐弯抹角那么微服务那套去中心化的好处就会被内耗抵消。做架构选型时我建议先把组织的沟通成本估进去。我们后来坚持了“两披萨团队”的调整思路核心是把服务归属权明确到具体小团队避免两个部门共用一个服务却互相踢皮球。如果你们团队还很依赖集中式管理凡事要层层审批那微服务带来的自治红利基本吃不到反而会因为需要多方协作让决策更慢。架构选型真的不只是技术决策它同时是一场组织设计的调整。这一点很多文章里很少提到但实际影响非常大。我的体会是微服务本身没有过时过时的是不加思考、一拥而上的态度。热度过去了反而特别好因为终于可以安安静静地讨论架构本身而不是讨论如何在社交平台上站队。架构选型的核心永远是匹配匹配业务复杂度匹配团队规模匹配组织协作方式。希望这篇文章能把我们从狂热到理性的这条路上看到的风景、踩过的坑、得出的经验都表达清楚对正在做架构决策的你有帮助。