
有时候真的觉得,微服务这东西吧,香是挺香的,但踩坑也是真的狠。我这几年做分布式,最怕的不是业务复杂、也不是写代码写到手酸,而是凌晨两点被电话叫醒:“链路 RT 飙了,首页 504,你看下是哪个服务在慢?”
每次听到这个我心里就知道——又有人在链路里加了一层调用,或者哪个下游又“梦游”了。
这篇算是我这些年踩坑之后的一些碎碎念,不是教科书,不保证完美,但每一句都是实打实的血泪经验。
我几年前接手过一个老项目,最离谱的一次链路是这样的:
网关 → A → B → C → D → E → F
六跳。每一跳都 80~120ms,最后 RT 是 600ms 起步。用户点个按钮,像是点到异地机房。
那时候我特别不理解:为啥一个查订单的接口,要查到 F 服务?后来才搞明白——大家做需求的时候都觉得“反正我这个接口需要那个数据,就直接调一下它嘛”,于是越积越厚,链路越拖越长。
问题的本质其实就一句话:大家都觉得自己的那一次调用没事,但所有调用叠在一起就出事了。
链路长,就像高速公路上加了好多收费站,这点过路费不是大问题,但排队排成长龙,一辆接一辆慢吞吞,就会把整条高速拖死。
简单说,就是一个服务挂了,导致十几个、几十个一起挂,然后网关直接 502 一片。
之前遇到过一次最典型的,当时我们的库存服务突然 RT 飙升,平时 20ms,那次变成了 2s;订单服务每个请求都要查库存,上游就跟着变慢;再往上用户下单就卡住;最后所有下单相关的业务全挂。
当时是晚上 10 点,业务方直接跑过来问:“怎么下个单能要这么久?”
我也只能苦笑:“不是我们想慢,是下游在那里深呼吸。”
我后来把问题总结成三个字:等、堵、炸。
有时候你看日志会发现:线程池 200 个线程,一个都没空,因为大家都在等待下游的 2s 超时。
这就是最可怕的:不是因为处理慢,而是因为一直在等,导致大家都没法工作。
很多团队(包括我们当年)都会犯同一个错:
默认超时值不改,库里多少就是多少。
然后就等着凌晨线上爆炸。
这件事我反复讲,讲到有点烦了,但真的太多人不重视。
我自己踩过一个坑:有一次一个接口因为访问第三方支付,超时时间设置成了 10 秒(对,就是我设的)。平时没感觉,但双十一那天因为对方偶尔卡一下,整个链路都跟着卡。
后来我总结了几条很简单但非常救命的原则:
很多人只知道“失败多了会熔断,但具体怎么判定的?”
我当年第一次用 Hystrix 时也只知道简单配置,直到某天线上被淹没,才研究得特别细。
核心逻辑其实就三个:
我第一次真正体会熔断的威力,是线上 C 服务突然 RT 飙到 5s,所有服务都在等它。
后来我们给 B→C 加了 Resilience4j,一旦失败率达到 50%,马上熔断,直接走降级逻辑。
结果非常明显:链路不再被拖垮,页面从 504 变成兜底数据,至少不至于挂死。
讲人话就是:我不能因为你一个服务在拖延,把我的线程池全拖死。
Hystrix、Resilience4j 都有线程池隔离,重点就是——
每个下游调用自己的线程池,不许抢资源。
我们之前没有隔离时,库存服务 RT 2s 导致调用它的线程数占满了整个应用的线程池,其他接口完全没办法服务用户。
加了“舱壁”之后,库存服务爱慢不慢,慢死了也只是把自己的池子占满,不影响订单、商品详情那些正常流量。
这就是舱壁机制的本质。
我自己的体感是:
因为能允许突发流量,比如突然来了 200 QPS,你不至于直接拒绝。
因为输出稳定,RT 可控。
当年我写过一个支付回调接口,用的是令牌桶,结果双十一凌晨,突发流量把桶打空了,直接拒绝了一堆回调。后来换成漏桶,流量下来了,但更稳。
对于内部系统,我一般建议使用令牌桶。因为微服务链路,不能太死板,总是要吃一点突发。
降级这事,大家都懂,但实际写出来的往往非常“羞羞的”。
比如我见过一个最离谱的降级:
catch (Exception e) {
return null;
}然后就造成了 NPE 满天飞。
我自己的做法:
降级是让服务“趴着也能活”,不是让它“躺平”。
以前没有 tracing 的时侯,排查问题就像摸黑找猫。加了 SkyWalking/Zipkin 后,真是一下清醒了。
有一次我们查一个慢接口,开发说“不可能是我这边,逻辑很简单的”。
链路追踪一打开:
A → B 100ms,B → C 1.4s
然后大家都沉默了。
追踪里看得最清楚的就是:
这东西平时没感觉,但线上爆炸的时候,是救命稻草。
说实话,我以前以为 SLA 是业务方的事情,后来才明白:
没有清晰 SLA 的架构,是无法扩容、无法限流、无法保护的。
我的经验是:
为什么要定 SLA?因为:
没有 SLA 的系统,就像没有体检报告的身体,看着好像还行,但随时可能倒下。
级联失败这个东西,戏剧性很强,轻则 504 一片,重则直接报警电话响一晚上。
我自己的经验:
我当年踩得最痛的坑是:
我们有个服务启动后,缓存要预热 2 分钟。结果部署后还没预热好,就有请求进来,导致大量数据库查询,DB 压力飙升,整个集群挂了。
那次之后我深刻意识到:
启动预热也是链路稳定性的一部分。
这是一个我印象特别深的线上事故。
某年 618 晚上 20:00,秒杀开始,我们系统平时 QPS 600,峰值能冲到 3000 左右,也不算很夸张。
结果就那一天库存服务突然 RT 变成了 800ms,我们完全没料到。原因是某个同事改了库存的查询逻辑,加了个“统计未支付订单数量”的 join,而且没走索引。
这 800ms 就像砸在水面上的石头:
整条链路像一串多米诺骨牌,全倒了。
当时我看着监控,整整愣了十秒。
最后解决是这样:
那次之后我们团队开会讨论了三个小时,最后的结论非常现实:
没有熔断、没有隔离、没有限流的微服务架构,就是站在楼顶吹风。看似凉快,其实随时可能掉下去。
其实微服务并没那么复杂,大部分问题都是因为我们没尊重“延迟”这个敌人。
CPU、内存、带宽这些都很直观,但“延迟”最容易被忽略,它悄悄堆积,等你意识到时,系统已经跪了。
链路长不可怕,可怕的是没有保护。
雪崩不可怕,可怕的是没有兜底。
问题不可怕,可怕的是没人愿意承认“我们之前的设计不够稳”。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。