连载中 14/20

资源隔离:舱壁模式

2026-07-14 · 3861 阅读 · 0 评论 · 0 赞

一堵舱壁的沉船史

第 1 篇雪崩链的第一环还记得吗:会员服务所有线程都阻塞在等订单服务上,连健康检查、无关接口一起陪葬。根因是所有调用共用同一个 Tomcat 线程池——一堵舱壁,一处漏水全船沉。泰坦尼克号最大的教训不是冰山,是水密舱没隔到底;微服务里的解法叫舱壁模式(Bulkhead):把资源切成隔舱,一个舱进水,船照开。

线程池隔离与信号量隔离

两种切舱方式。线程池隔离:每个下游调用配一个独立线程池,调用订单走订单池、调用会员走会员池——订单病了只会耗光自己的 20 个线程,会员池照常干活。代价是线程切换有开销、线程数会膨胀(10 个下游 × 20 线程 = 200 个,还没算 Tomcat 自己的)。信号量隔离:不换线程,只用计数器限制并发——同时最多 20 个请求在调下游,超了直接拒绝。轻量、零切换开销,但调用线程自己会阻塞在下游上,没有独立的超时中断能力。

// 每个下游一个独立线程池,互不挤占
ExecutorService orderPool = new ThreadPoolExecutor(20, 20, 60, SECONDS,
        new ArrayBlockingQueue<>(50), named("order-pool"));
ExecutorService memberPool = new ThreadPoolExecutor(20, 20, 60, SECONDS,
        new ArrayBlockingQueue<>(50), named("member-pool"));

Future<Order> f = orderPool.submit(() -> orderClient.query(id));
Order order = f.get(500, MILLISECONDS);   // 只等 500ms,超时也不拖累别的池
维度线程池隔离信号量隔离
切换开销有(上下文切换)
超时控制强(可中断等待)弱(调用线程自己阻塞)
资源占用高(每舱一组线程)低(只加计数器)
适合场景慢下游、外部网络调用快调用、内部高频接口

隔离的粒度

太粗没意义:按团队分舱,隔壁团队的坑照样把你的舱灌满;太细会爆炸:几百个下游几百个池,线程数失控。经验法则:核心下游独立成舱,边缘下游共享舱 + 熔断兜底。除了调用线程池,连接池同样要分:DB 连接池、Redis 连接池、HTTP 连接池各自独立,别让报表查询和交易抢同一个 DB 池。

舱壁不只是线程池

把「隔离」往上抬一层,是同一套哲学在不同维度的应用:机器级——核心服务独立集群,别和边缘服务混部,报表把 CPU 吃满时交易还在同一台机器上就是埋雷;机房级——异地多活,机房级故障的爆炸半径被限制在一个机房;数据级——核心库与边缘库分实例,慢查询拖不垮核心交易;账号级——限流篇讲过的按用户维度隔离配额,大客户刷爆自己的额度,不影响别人。

隔离的通用公式一句话:把故障的爆炸半径,限制在你愿意承受的范围内。切得越细半径越小,运维成本越高——按业务重要性切,不按强迫症切。

舱壁保证病不传染,但传染性最强的源头还没堵上——「没有超时的等待」。所有线程被拖死的起点,往往就是那个忘了设超时的 HTTP 调用。下篇把全链路的时间预算算清楚。

503

10 年全栈工程师 · 503咖啡馆主理人

#舱壁模式#资源隔离#线程池隔离#信号量隔离#爆炸半径

评论 (0)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 5 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞