ELI5 · 说人话

怎么把一个大包
拆成一条街

一个包拆成一条街 一个仓库 · 一个包 · 一个库 各自的仓库 · 各自上线 · 各自的库

微服务不是「把代码拆小」。
是把发布这件事拆开——每支队伍不用等别人,自己就能上线。代价是:本来一次函数调用的事,现在要走网络。

拆的是组织,付的是网络

先分清楚

它和分布式是什么关系

不是并列的两个词。微服务是分布式的一种,而且是被人逼出来的那一种。

分布式系统 一件事,摊给很多台机器做 一张表分成 64 片 文件存三份的对象存储 六个节点的 Redis 集群 同一个服务部 20 个副本 ——这些都没有「微服务」 微服务 按业务边界切开 各自的库 · 各自上线 微服务一定是分布式,分布式不一定是微服务。
分布式微服务
为什么有一台机器装不下,也不敢只有一台一个发布节奏挤不下这么多人
按什么切按数据和流量切:分片、副本按业务能力切:订单、库存、支付
谁先受益机器:扛得住,死一台不要紧人:自己改自己发,不用排队
能不做吗数据量到了就没得选永远可以不做,单体也能活
代价网络不可靠,状态难对齐上面这些全都有,再加一整套运维

所以:先按分布式那套把「网络会骗人」这件事处理干净(超时、重试、幂等、最终一致),微服务只是在这之上多加了一条按业务切的规矩

解决什么

三件事,全都跟人有关

单体系统长到 200 人一起改的时候,会同时冒出这三个症状。

排队
上线要排队

一个仓库、一个包,谁改都得等下一个发布窗口。改一行文案,等一周。

连坐
一处崩,全站崩

一个导报表的功能把内存吃爆,同一个进程里的下单、支付跟着一起躺下。

陪跑
扩容只能整包扩

只有支付扛不住,却要把整个包复制 3 份。另外两块跟着白占内存。

反过来说,它不解决这三件事:代码写得烂、需求天天变、系统本来就慢。拆开之后这些只会更明显——因为现在跨了网络。

最难的一步

切错了比不切还糟

判断标准只有一条:一个需求进来,动几个服务?

按技术分层切 接口层服务 逻辑层服务 数据层服务 一个需求 = 动 3 个服务、发 3 次版 按业务能力切 订单 库存 支付 接口 · 逻辑 · 数据 都在自己肚子里 一个需求 = 只动 1 个服务

所以边界要按业务能力划:订单、库存、支付、结算。不要出现 user-service / dao-service / common-service 这种按技术切的名字——那是把一个函数调用改成了三次网络请求,纯亏。

怎么解决

企业级要先配齐八件套

这八样东西,是拆开之后凭空多出来的成本。少一样,第 5 个服务上线那天就会还回来。

一道门

API 网关。所有请求先进这道门:认身份、限速、按路牌转发。外面永远只看见一个地址。

一本花名册

注册与发现。机器会挂、会重启、会扩容,地址天天变。谁上线自己去登记,别人按名字找。

一个开关箱

配置中心。改一个超时时间不该重新发版。开关和代码分开放,改完立刻生效。

两种说话方式

要立刻拿到答案就直接调;发短信、算积分这种不急的,扔进消息队列。能异步就异步。

一排保险丝

超时、重试、熔断、降级。下游一慢就先掐断,返回兜底结果。没有超时的调用是定时炸弹。

三块仪表盘

日志、指标、链路。每个请求带同一个 traceId 走完全程。查不出来的系统等于没上线。

各自的库

一个服务只准动自己的库,别人只能走接口。跨服务的事用消息补偿,别指望一个事务。

一条流水线

20 个服务就是 20 条流水线和 20 套容器编排。手工部署撑不过第 5 个服务。

最容易死的一次

一个服务慢,全都跟着死

支付变慢 5 秒,不会只是「支付慢」。调它的人全在那儿等着,线程占满,网关也进不去了。

没有超时和熔断 网关 订单 支付 卡住 5s 整站跟着挂 连登录页都打不开 线程全堆在这儿等,不放手 配上超时 + 熔断 + 降级 80ms 就返回:支付繁忙,稍后重试 网关 订单 熔断 支付 只掉支付这一块 浏览、下单照常

三件套一起才有用:超时让等待有尽头,熔断让失败不再重复付出代价,降级让用户看到一句人话而不是白屏。再加一条舱壁——支付的线程池和查询的线程池分开,谁满谁自己受着。

出事之后

请求穿过五个服务,慢在哪一段

单体里看一份日志就够了。拆开之后,一次「下单慢」散在五台机器的五份日志里。

traceId: a7f3c1… 网关 3 ms 订单 12 ms 库存 8 ms 支付 900 ms ← 通知 5 ms 没有 traceId,五个团队会互相说「不是我」。

点一下看每一段

traceId 要在网关生成、每一跳都往下传,日志里带上它。这是第一天就要做的事,不是出事之后再补——补的时候你手里只有八个日志文件和一堆对不上的时间戳。

别踩

五条红线

十个服务连同一个数据库

这不是微服务,是分布式单体:改一张表要通知十个团队,好处一个没拿到,坏处全占了。一个服务一个库,别人只能通过接口拿数据。

三个人维护八个服务

服务数量该跟团队数量走,不跟模块数量走。一个团队能端到端负责 1~3 个服务;再多就是每天在八个仓库之间来回切窗口。

一次下单,同步调了五层

订单叫库存、库存叫支付、支付叫风控……每多一跳,可用性就乘一次 0.999,延迟就加一次。同步链路别超过 3 跳,后面的改成发消息,让下游自己去处理。

重试没有幂等

网络超时不代表对方没做,重试就会做第二遍——用户被扣两次钱。所有写接口都要带幂等号,同一个号第二次进来直接返回上次结果。

每个服务一套技术栈

「语言随便选」听着很美,代价是八套监控、八套发布脚本、八种线上问题。定一套主栈,例外要申请。

动手

四步,前两步都不动服务

任务:把一个跑了 6 年、40 个人在改的单体电商,安全地拆成微服务。
  1. 1

    先不拆在单体里把边界画出来:订单、库存、支付各成一个模块,互相只准调对方的接口,不准直接摸对方的表。在一个进程里都分不清的边界,拆出去只会更乱。

  2. 2

    先修路网关、注册中心、配置中心、链路追踪、流水线——八件套先跑通,拿单体自己当第一个用户。基建没好就拆服务,等于在泥地里盖房。

  3. 3

    只切一块挑最痛的那块先搬出去——发布最频繁的,或者最吃资源的。老代码用绞杀者模式:新流量走新服务,老路留着,稳了再删。一次只切一个。

  4. 4

    演一次事故上线前把支付服务 kill 掉,看下单还能不能走降级路径。没演练过的降级等于没有降级——真出事那天你才发现兜底逻辑三个月前就被改坏了。

最后一句实话:能不拆就别拆。单体 + 清楚的模块边界 + 一条像样的流水线,足够撑一家公司到五十个研发。等到「上线要排队」真的疼了再拆,那时候你已经知道该沿哪条缝切。

分布式,是机器装不下逼出来的;
微服务,是挤不下逼出来的。