微服务架构设计
第 31 题:单体应用拆分微服务——边界与粒度 服务拆分
- 1. 怎么划服务边界?2. 粒度多细合适?3. 共享库 vs 共享库耦合怎么破?4. 拆分顺序与数据怎么迁?5. 拆完后分布式事务怎么治?
第一步:分析。拆分目标不是「多服务」,而是独立部署、独立演进、故障隔离。边界按业务能力(DDD 限界上下文)划,而非技术层。
第二步:挑战。边界模糊致耦合;粒度太细→运维/事务爆炸;数据库难拆;团队协作模式要变。
第三步:架构。按领域划分:用户、商品、库存、订单、支付、营销、物流。先绞杀者模式抽边缘域(商品/营销),核心交易最后拆;数据库按域垂直拆,共享表建为独立服务(如用户中心)。
第四步:选型。Spring Cloud Alibaba(Nacos/OpenFeign/Sentinel)+ DDD 限界上下文 + 绞杀者迁移。
第五步:一致性。跨域用最终一致(事件/Outbox);强一致仅限域内。
第六步:高可用。拆分即隔离;核心域独立资源池;灰度迁移。
第七步:优化。「先宽后窄」:先粗粒度(8~12 域)跑通,再按热点细分;避免过度微服务(康威定律匹配团队)。
为什么 DDD 限界上下文?它是业务语义边界,比技术分层更稳。粒度:一个服务应由一个 2-pizza 团队维护;常见 8~15 个域起步,避免「一个接口一个服务」。共享库陷阱:把公共实体打成 jar 共享→改一处全编译,应改为「共享服务 + 防腐层(ACL)」。
// 防腐层:订单域调用用户域,不依赖其模型 public interface UserClientACL { UserSnapshot get(Long uid); // 返回本域所需最小字段 } // 绞杀者:老单体路由逐步切到新服务(Gateway 权重) // 迁移顺序由依赖度决定:被依赖少的先拆
追问 1:拆完调用链变长 RT 升?异步解耦 + 并行编排(CompletableFuture)+ 本地缓存常用快照。
追问 2:跨域强一致怎么办?尽量域内;必须跨域用 Saga/TCC,避免 2PC(锁资源久)。
追问 3:团队不会 DDD?先按业务模块物理分目录 + 独立库,再渐进引入上下文映射。
追问 4:拆分期双写怎么保证一致?影子双写 + 比对校验 + 切读流量灰度,确认无误再停老写。
追问 5:服务太多注册中心压力?按域分注册命名空间/集群;控制粒度;元数据路由。
- ❌ 按技术层拆(Controller/Service 各自服务)——仍是强耦合。正确:按业务域。
- ❌ 一个接口一个微服务——运维/事务爆炸。正确:团队级粒度。
- ❌ 共享 jar 实体——改一处全编译。正确:防腐层 + 服务。
第 32 题:服务雪崩与舱壁/熔断治理 稳定性
- 1. 雪崩是怎么形成的?2. 舱壁、熔断、限流、降级分别治什么?3. 超时怎么设?4. 熔断阈值怎么定?5. fallback 怎么写才不丢数据?
第一步:分析。雪崩 = 一个慢依赖耗尽调用方资源 → 调用方变慢 → 上游资源耗尽,连锁。四件套:限流(入口)、舱壁(隔离)、熔断(快速失败)、降级(保核心)。
第二步:挑战。资源耗尽、连锁、误判、降级丢数据。
第三步:架构。①每下游独立线程池(舱壁);②调用设超时(如 300ms)+ 重试有限;③错误率超阈值熔断(Sentinel/Resilience4j),半开探测恢复;④非核心降级 fallback;⑤入口限流防过载。
第四步:选型。Sentinel(限流/熔断/降级)+ Resilience4j(舱壁/重试)+ OpenFeign 集成。
第五步:一致性。熔断/降级只影响非核心;核心数据写仍需成功(排队/异步补偿)。
第六步:高可用。隔离防扩散;熔断防雪崩;超时防挂死。
第七步:优化。超时=依赖 P99×安全系数;熔断配最小请求数防抖动误判。
熔断 vs 舱壁:舱壁防「一个慢依赖拖垮整个线程池」,熔断防「下游已死还持续打」。两者互补。超时:不设=线程永久挂起=雪崩;设太短=误熔断。用 P99+余量。fallback:非核心返回默认值/缓存;核心走异步队列稍后补。
// OpenFeign + Sentinel 熔断降级 @FeignClient(name = "user-svc", fallback = UserFallback.class) public interface UserClient { @GetMapping("/auth") AuthResult auth(Long uid); } // 独立线程池(舱壁) ThreadPoolExecutor userPool = new ThreadPoolExecutor(20, 20, 60s, queue, reject→ fallback); // 超时:RestTemplate/WebClient 设 connect/read timeout = 300ms
追问 1:熔断后请求全失败?半开探测:放少量请求探测恢复,成功则闭合。
追问 2:重试放大流量?重试配退避+上限;仅对幂等/超时(非 5xx 业务错)重试。
追问 3:舱壁线程数怎么定?按依赖 RT 与并发需求:线程数 ≈ QPS×RT;留余量防queue堆积。
追问 4:核心依赖熔断了?核心依赖熔断=严重故障,应 fail-fast 返回友好错误而非假成功;并告警+扩容。
追问 5:Hystrix 停更了?用 Sentinel/Resilience4j;Resilience4j 更轻、函数式装饰。
- ❌ 不设超时——线程挂死。正确:短超时。
- ❌ 全服务共用线程池——一损俱损。正确:舱壁。
- ❌ 熔断即丢弃核心数据——资损。正确:核心异步补。
第 33 题:API Gateway 与 BFF 设计 网关
- 1. 网关和 BFF 区别?2. 网关放哪些能力、不放哪些?3. 聚合接口放哪?4. 网关成为瓶颈/单点怎么办?5. 灰度在网关怎么做?
第一步:分析。网关=横切(鉴权/限流/路由/灰度);BFF=面向前端的聚合编排层,按端定制。
第二步:挑战。能力边界;聚合位置;网关瓶颈;灰度。
第三步:架构。①Gateway(Spring Cloud Gateway):鉴权、限流、路由、灰度路由、日志;②BFF 层(按端:app-bff/web-bff)聚合多个后端域,减少前端往返;③后端域只暴露领域 API,不感知端。
第四步:选型。Spring Cloud Gateway(WebFlux 异步)+ Nacos(路由/灰度配置)+ BFF 独立服务。
第五步:一致性。网关不持有业务状态;鉴权结果透传(JWT/上下文);灰度靠路由权重。
第六步:高可用。网关多实例 + LVS/SLB;无状态;限流防过载;BFF 按端独立部署互不影响。
第七步:优化。网关只做轻量(鉴权用本地公钥验签);重聚合下沉 BFF;避免网关成业务层。
网关 vs BFF:网关关注「通用横切 + 路由」,BFF 关注「前端体验聚合」。把聚合放网关会让网关变胖变慢、难维护。为什么异步 WebFlux?网关高并发 IO 密集,响应式非阻塞提升吞吐、降资源。灰度:网关按 header/用户标签路由到新版本实例。
// Gateway 灰度路由:按 header 版本路由 @Bean public RouteLocator routes(RouteLocatorBuilder b) { return b.routes() .route("order-v2", r -> r.header("x-version", "v2") .uri("lb://order-svc-v2")) .route("order", r -> r.alwaysTrue().uri("lb://order-svc")) .build(); } // BFF 并行聚合(CompletableFuture) CompletableFuture<User> u = userClient.get(uid); CompletableFuture<List<Order>> o = orderClient.list(uid);
追问 1:网关 RT 高?网关只做轻量;验签用本地公钥;避免远程调用鉴权(改本地缓存 token 元数据)。
追问 2:BFF 变另一个单体?BFF 按端独立但仍是聚合层,不过度塞业务;复杂逻辑下沉领域服务。
追问 3:灰度错了怎么回?路由配置热更(Nacos);一键切回 100% 旧版;监控对比。
追问 4:网关限流和 Sentinel 关系?网关做入口粗限流(用户/IP),服务内 Sentinel 做细粒度(接口/热点)。
追问 5:多网关还是单网关?入口统一网关;内部可按域再加边缘网关;避免网关过多致路由混乱。
- ❌ 把业务聚合塞进网关——网关变胖瓶颈。正确:下沉 BFF。
- ❌ 每端调后端 12 次——前端慢。正确:BFF 聚合。
- ❌ 网关同步阻塞——吞吐低。正确:WebFlux 异步。
第 34 题:服务注册发现与多机房寻址 注册中心
- 1. Nacos/Eureka/ZK/Consul 怎么选?2. 健康检查怎么做才不误剔?3. 注册中心挂了服务还能调吗?4. 多机房怎么就近调用?5. 临时实例 vs 持久实例?
第一步:分析。注册中心是「系统的电话簿」,核心矛盾是 一致性 vs 可用性(CAP):ZK/CP 强一致但分区时不可用;Nacos(可配 AP/CP)/Eureka(AP)优先可用性。
第二步:挑战。误剔、注册中心挂、跨机房延迟、一致性选择。
第三步:架构。①选 Nacos(AP 模式)做注册发现(优先可用,容忍短暂不一致);②健康检查:客户端心跳 + 服务端探测,设合理超时(如 15s×3)防抖动误剔;③本地缓存:消费者缓存实例列表,注册中心挂仍可调用;④就近路由:按 zone/region 优先同机房;⑤保护阈值:健康实例比例过低时保留全部,防雪崩剔除。
第四步:选型。Nacos(AP 注册 + 配置);Eureka(纯 AP 备选);ZK 用于强一致协调(如选主)。
第五步:一致性。AP 模式允许短暂脏列表,靠客户端重试+健康检查自愈;强一致场景用 CP/ZK。
第六步:高可用。注册中心 3/5 节点集群;消费者本地缓存兜底;多级降级。
第七步:优化。保护阈值 + 心跳间隔调优防误剔;就近路由降延迟。
为什么 AP?注册中心挂比「短暂脏列表」危害小;AP 保证注册中心故障时服务仍可互调。保护阈值:当健康实例 < 阈值(如 80%),不再剔除,宁可负载高也不让流量无路可走。临时 vs 持久:临时实例靠心跳(进程死即删),持久实例手动(如 DB、配置)。
// Nacos 保护阈值:健康比例过低保留全部实例 // application.yml spring.cloud.nacos.discovery: health-check-interval: 5 # 秒 # 控制台设 protect-threshold: 0.8 // 消费者:启用本地缓存+就近(同 cluster 优先) // NacosDiscoveryClient 默认带本地缓存,注册中心挂仍可读取
追问 1:Nacos 和 Eureka 区别?Nacos 同时支持 AP/CP、配置中心一体、推送更实时;Eureka 纯 AP、已停更。
追问 2:注册中心脑裂?AP 模式脑裂下各分区独立可用(不阻塞),合并后列表收敛;比 CP 拒绝服务更优。
追问 3:跨机房怎么防跨调用?zone/cluster 元数据路由;跨机房走专线+熔断;单元化下强制单元内闭环。
追问 4:实例上下线抖动?延迟剔除(degrade 期)+ 客户端重试;优雅下线先摘流量再停进程。
追问 5:配置和注册混用风险?注册中心挂不影响配置(反之亦然)需分离部署或独立集群,防一损俱损。
- ❌ 心跳 1s 超时 1 次就剔——抖动误剔。正确:多层超时+保护阈值。
- ❌ 注册中心挂调用全失败——无本地缓存。正确:消费者缓存兜底。
- ❌ 所有调跨越机房——延迟高。正确:就近路由。
第 35 题:灰度发布与全链路灰度 发布
- 1. 蓝绿/滚动/金丝雀区别?2. 全链路灰度怎么保证同用户打到同一版本?3. 灰度流量怎么打标传递?4. DB 变更怎么灰度(不兼容怎么办)?5. 回滚怎么做快?
第一步:分析。灰度=用小流量验证新版本,错误半径可控。难点在全链路:一个请求经过多服务,必须「打标→透传→路由」一致。
第二步:挑战。多服务版本一致性;标传递;DB 不兼容变更;快速回滚。
第三步:架构。①网关按用户标签(uid%100)路由到灰实例;②灰度标写入请求上下文透传(OpenTelemetry baggage / 自定义 header);③各服务按标路由到同版本;④DB 变更:先加字段(兼容)→ 双写 → 迁读 → 删旧(扩展式迁移);⑤回滚:保留旧版本实例 + 路由权重一键回 0。
第四步:选型。Nacos(权重)+ Spring Cloud Gateway(路由)+ OTel(标透传)+ K8s(多版本 Deployment)。
第五步:一致性。灰度标全链路透传保证「用户 x 全程同版本」;DB 兼容式变更保证新旧并存。
第六步:高可用。灰度故障仅影响小流量;监控对比新旧;自动熔断异常版本。
第七步:优化。基于指标(错误率/RT)自动推进/回滚;影子流量(复制真实流量到新版本比对)。
蓝绿 vs 金丝雀:蓝绿是整量切换(快但错误半径大),金丝雀是逐步放量(安全但慢)。全链路灰度必须金丝雀+标透传。DB 不兼容变更:绝不能「改列类型一步到位」,要扩展式三阶段,保证新旧版本同时可读写。
// 灰度标透传:ThreadLocal + Feign 拦截器 public class GrayInterceptor implements RequestInterceptor { public void apply(RequestTemplate t) { t.header("x-gray", GrayContext.get()); // 透传标 } } // DB 兼容式:先加新字段,旧逻辑照常;新版本读新字段,逐步切换
追问 1:MQ 消费者怎么灰度?灰实例消费灰队列(按标路由消息);或消费全量但按标过滤处理,避免重复消费。
追问 2:灰度用户状态不一致?标基于稳定 uid;状态读写走同一版本实例,避免新旧写冲突(双写兜底)。
追问 3:回滚丢灰度期数据?DB 兼容式变更保证新旧都能读;回滚后旧版本继续处理,数据不丢。
追问 4:影子流量怎么比?复制真实请求到新版本(不落库/打标隔离),比对响应差异,上线前验证。
追问 5:灰度指标怎么自动推进?监控错误率/RT,优于基线自动放量,异常自动回滚(Argo Rollouts 类)。
- ❌ 直接全量发布——错误半径 100%。正确:金丝雀逐步。
- ❌ DB 一步不兼容变更——新旧冲突。正确:兼容式三阶段。
- ❌ 灰度标不传递——链路版本错乱。正确:全链路透传。
第 36 题:服务调用治理与超时重试 调用治理
- 1. 超时怎么设才合理?2. 重试为什么危险、怎么安全重试?3. 调用链 RT 怎么预算?4. 下游依赖怎么分级?5. 重试幂等怎么保证?
第一步:分析。调用治理=「超时预算 + 安全重试 + 分级降级」,目标是错误不放大、延迟可预期。
第二步:挑战。超时叠加、重试风暴、非幂等重试致脏数据。
第三步:架构。①超时预算:订单总 RT 预算 500ms,按调用链分配(库存 150ms、优惠 100ms、用户 80ms),用 Context 级联超时(如 gRPC deadline);②重试:仅对超时/5xx(非业务 4xx),指数退避+抖动+上限(≤2 次),且必须幂等;③分级:强依赖(支付)fail-fast,弱依赖(推荐)降级;④熔断配合。
第四步:选型。OpenFeign + Sentinel(超时/熔断)+ Resilience4j(重试/退避)+ 上下文超时传递。
第五步:一致性。重试只针对可重试错误;幂等键防重复副作用。
第六步:高可用。超时防挂死;重试上限防风暴;降级保核心。
第七步:优化。超时基于各依赖 P99 实测;重试配 token bucket 限重试流量。
为什么级联超时?上游超时到了,下游还在跑=浪费+可能重复。用 deadline 让下游提前取消。重试风暴:每层重试 N 次,链路 M 层 → N^M 倍请求。必须限制总重试且退避。幂等:非幂等接口重试会重复扣款,必须带幂等键。
// 重试装饰:仅超时/未知异常,退避,上限2,要求幂等 RetryConfig rc = RetryConfig.custom() .maxAttempts(2).waitDuration(Duration.ofMillis(100)) .retryOnException(e -> e instanceof TimeoutException || e instanceof UnknownError).build(); Supplier<R> s = Retry.decorateSupplier(Retry.of("inv", rc), () -> client.call(req.withIdempotKey())); // 调用链路减法超时(父剩 200ms 则子最多 200ms)
追问 1:重试还是切熔断?瞬时抖动重试(退避);持续性失败直接熔断,不重试。
追问 2:下游没做幂等怎么办?调用方带幂等键(请求唯一 ID),下游用唯一索引/去重表;或改为查询补偿而非重试写。
追问 3:超时太短误杀?基于 P99×1.5 实测校准;配最小样本防抖动。
追问 4:重试流量冲垮下游?重试流量单独限流(如重试占总 10%);下游熔断优先。
追问 5:弱依赖降级值怎么定?用缓存/默认值/上一版本快照;保证主流程可走完。
- ❌ 超时无脑 5s——RT 叠加雪崩。正确:预算分配级联。
- ❌ 对所有错重试——业务错重复副作用。正确:仅可重试+幂等。
- ❌ 重试无上限——风暴。正确:退避+上限。
第 37 题:微服务数据冗余与查询聚合 数据
- 1. 微服务为什么不能跨库 JOIN?2. 冗余数据怎么保证不脏?3. CQRS 怎么落地?4. 聚合查询一致性怎么妥协?5. 冗余存储成本?
第一步:分析。微服务「数据私有」,跨域查询需冗余/物化视图或CQRS:写走领域,读走聚合视图。
第二步:挑战。跨库 JOIN、冗余一致性、聚合可用性。
第三步:架构。①数据冗余:订单域通过事件(用户改名→UserUpdatedEvent)更新本地冗余用户昵称,读时无需跨服务;②CQRS:写模型(规范化的领域库)+ 读模型(宽表/ES 聚合视图),通过 CDC/事件同步;③聚合服务:BFF 并行调各域拼装,单域挂用缓存/默认值兜底。
第四步:选型。CDC(Canal/Debezium)→ MQ → 读模型(ES/宽表);事件驱动冗余。
第五步:一致性。读模型最终一致(秒级);关键字段(金额)以领域库为准,冗余仅展示。
第六步:高可用。冗余解耦,单域挂读视图仍可用;聚合降级。
第七步:优化。宽表减少 JOIN;缓存热点聚合;冗余字段最小化。
为什么冗余?跨服务实时 JOIN 既破坏封装又耦合可用性。冗余以空间换解耦+性能。CQRS:读写模型分离,写侧重规范一致,读侧重性能与聚合。一致性妥协:展示字段允许秒级延迟(昵称改了订单页稍后更新),金额等强一致字段不走冗余。
// 监听用户改名事件,更新订单读模型冗余字段 @RocketMQMessageListener(topic = "user-updated") public void on(UserUpdatedEvent e) { orderReadMapper.updateNickname(e.uid(), e.nick()); // 幂等按 eventId } // 读:直接查宽表,不跨服务 OrderDetail d = orderReadMapper.selectWide(orderId);
追问 1:冗余字段更新延迟用户投诉?关键字段(昵称)秒级可接受;或写时双更(领域+冗余)保证即时,异步校验修复。
追问 2:冗余数据膨胀?只冗余展示必需字段;冷热分离;定期清理。
追问 3:CDC 断了冗余脏?CDC 断有监控+积压告警;重放修复;读模型带版本号便于校正。
追问 4:强一致查询怎么办?极少场景(如对账)直连领域库实时查,不走冗余。
追问 5:聚合服务挂?BFF 并行+超时+降级默认值;核心字段走缓存。
- ❌ 跨服务实时 JOIN——耦合+慢。正确:冗余/CQRS。
- ❌ 冗余全量字段——膨胀。正确:最小必要字段。
- ❌ 金额也冗余——脏数据资损。正确:金额走领域库。
第 38 题:服务契约与版本兼容 契约
- 1. 怎么保证接口变更不破坏调用方?2. 兼容性原则?3. 契约测试怎么做?4. 版本怎么管(URL/Header)?5. 破坏性变更怎么平滑?
第一步:分析。微服务接口是契约,变更需向后兼容或显式版本演进,靠规范 + 契约测试兜住。
第二步:挑战。隐式变更、多调用方、破坏性升级。
第三步:架构。①兼容性规则:只增字段(不删不改名不缩类型),老调用方忽略新字段;②契约测试:Pact 等 consumer-driven,CI 中校验 provider 满足 consumer 期望;③版本:优先「加字段兼容」,必须破坏性变更才升主版本(/v2 或 header),双版本并存过渡;④变更评审:接口变更走评审+影响面扫描。
第四步:选型。Pact(契约测试)+ OpenAPI(规范)+ CI 卡点 + 网关版本路由。
第五步:一致性。契约即接口事实标准;测试保证 provider/consumer 对齐。
第六步:高可用。兼容变更零停机;破坏性变更双版本平滑。
第七步:优化。接口文档自动化(Swagger);影响面工具扫描调用方。
为什么 consumer-driven?provider 不知道所有 consumer 怎么用,让 consumer 定义期望,provider 在 CI 校验,才能防「改了不知谁挂」。兼容性:JSON/Protobuf 对「加 optional 字段」天然兼容;改名/改类型是破坏性的,必须版本化。
// Consumer 定义契约期望(Pact JVM) @Pact(consumer = "order-web") RequestResponsePact p(PactDslWithProvider b){ return b.given("order exists").uponReceiving("get") .path("/orders/1").willRespondWith().status(200) .body("{\"id\":1,\"amount\":100}").toPact(); // amount 为新增兼容字段 } // CI: provider 校验满足该契约,否则发布失败
追问 1:字段必须改名?加新名+保留旧名(双写)过渡 N 个版本,再删旧;或升 /v2 并存。
追问 2:契约测试拖慢 CI?只跑变更服务的契约;并行+缓存;关键链路必跑。
追问 3:老版本consumer多久下线?监控老版本调用量→低于阈值发通知→设 deadline 下线。
追问 4:网关怎么路由版本?header/URL 版本路由到对应 provider 实例,双版本并存期。
追问 5:Protobuf 兼容性?protobuf 对增 field/改 optional 兼容;改 required/类型/编号破坏性,需版本。
- ❌ 直接改字段名——调用方挂。正确:加字段兼容/版本化。
- ❌ 无契约测试——改完不知谁崩。正确:consumer-driven。
- ❌ 破坏性变更不并存——硬切。正确:双版本过渡。
第 39 题:多租户 SaaS 架构 SaaS
- 1. 三种隔离模式怎么选?2. 共享库怎么防越权?3. 大租户独占怎么切?4. 租户路由怎么做?5. 成本怎么平衡?
第一步:分析。多租户核心矛盾:隔离性 vs 成本。三种模式:独立库(隔离最强最贵)、共享库+租户ID(最省但易串)、混合(大独占小共享)。
第二步:挑战。越权、成本、弹性、路由。
第三步:架构。①混合隔离:小租户共享库(行级 tenant_id 隔离+强制过滤),大租户独立 schema/库;②租户路由:登录后 tenant_id 写入上下文,MyBatis 拦截器自动拼 tenant_id(防漏写);③资源配额:按套餐限流/配额;④大租户迁移:达到阈值自动迁独立库。
第四步:选型。共享 MySQL+tenant_id 行隔离;独立库按租户;MyBatis 拦截器强制租户过滤;连接池按租户分组。
第五步:一致性。租户上下文全程透传;所有查询强制带 tenant_id,防越权。
第六步:高可用。大租户独立资源防「吵闹邻居」;共享池配额隔离。
第七步:优化。按租户数据量自动升降级隔离级别;冷热分离降本。
三种模式权衡:独立库运维/成本高但合规强;共享库省但靠纪律(一行漏写 tenant_id 就串数据);混合最务实。防越权关键:别指望业务代码每次写对,用拦截器在 SQL 层强制注入 tenant_id,业务无感知且不可绕过。
// MyBatis 拦截器:所有查询自动注入 tenant_id(防漏写越权) @Intercepts({@Signature(type=Executor.class, method="query", args={...})}) public class TenantInterceptor implements Interceptor { public Object intercept(Invocation i){ BoundSql sql = ...; String newSql = appendTenant(sql, TenantContext.get()); return i.proceed(); // 用 newSql 替换 } }
追问 1:共享库单表太大?按租户 hash 分表;大租户独立表/库;归档冷数据。
追问 2:租户误删数据跨租户?拦截器强制 tenant_id + 软删除 + 回收站按租户隔离。
追问 3:自定义字段怎么存?扩展表(tenant_id, entity_id, key, value)或 JSON 列,按租户配置。
追问 4:计费/配额怎么落地?API 网关按租户限流;用量采集定时汇总对账。
追问 5:合规要求数据不出境?租户级区域路由,独立库落指定 Region;混合隔离支持合规。
- ❌ 全独立库——小租户烧钱。正确:混合隔离。
- ❌ 靠业务拼 tenant_id——漏写串数据。正确:拦截器强制。
- ❌ 大租户共享池——吵闹邻居。正确:独占升级。
第 40 题:Service Mesh 落地评估 Service Mesh
- 1. Mesh 解决什么问题、代价是什么?2. Sidecar 性能损耗?3. 什么时候不该用 Mesh?4. 与 SDK 治理怎么过渡?5. 多语言收益怎么算?
第一步:分析。Mesh 把治理从 SDK 下沉到 Sidecar(Envoy),统一多语言治理、独立演进。代价是复杂度+延迟+资源。
第二步:挑战。性能损耗、运维复杂度、迁移成本、价值论证。
第三步:架构。①控制面(Istio)下发规则,数据面(Envoy Sidecar)透明拦截流量做熔断/限流/mTLS/追踪;②渐进落地:先非核心域,用 SDK+Mesh 双模共存,治理规则统一抽象;③性能:Sidecar 增 ~1-3ms RT、每实例多 0.5-1 vCPU,需容量评估。
第四步:选型。Istio(K8s 原生)+ Envoy;或轻量(如 MOSN/自研 sidecar)。单语言纯 Java 可考虑 SDK(Spring Cloud)更省。
第五步:一致性。治理规则统一下发;mTLS 保证服务间零信任。
第六步:高可用。Sidecar 故障本地降级(passthrough);控制面 HA。
第七步:优化。Sidecar 资源限额;协议优化(HTTP/2、gRPC);只注入需要的服务。
为什么上 Mesh?多语言+治理统一演进是核心收益;纯 Java 单栈用 Spring Cloud SDK 更轻。代价:Sidecar 每实例常驻资源、RT +1~3ms、运维门槛高(需 K8s 熟练)。过渡:双模,规则抽象层兼容 SDK 与 Mesh,逐步切。
// 治理规则统一抽象(SDK/Mesh 皆可驱动) interface TrafficPolicy { // 限流/熔断规则,Mesh 与 SDK 共用语义 void apply(CircuitBreaker cb, RateLimit rl); } // K8s VirtualService 示例(Istio): // apiVersion: networking.istio.io/v1beta1 kind: VirtualService // 限流/熔断在控制面配置,应用无感知
追问 1:Sidecar 资源浪费?只给需要治理的服务注入;资源限额;用 eBPF 类方案减少 sidecar(如 Cilium)。
追问 2:Mesh 故障连锁?Sidecar passthrough 降级;控制面 HA 多副本;关键域保留 SDK 兜底。
追问 3:纯 Java 栈?若全 Java,Spring Cloud SDK 治理更轻、延迟更低,Mesh 收益有限。
追问 4:迁移风险?先非核心域试点;双模并存;监控 RT/资源对比再推广。
追问 5:可观测怎么接?Mesh 原生生成访问日志/指标(Prometheus)+ 接入 OTel,统一追踪。
- ❌ 为时髦硬上 Mesh——复杂度爆炸。正确:看多语言/治理痛点。
- ❌ 全量一刀切——风险高。正确:双模渐进。
- ❌ 忽略 Sidecar 损耗——容量不足。正确:容量评估。