← 返回题库 / 大纲

微服务架构设计

第 31 题:单体应用拆分微服务——边界与粒度 服务拆分

【真实企业业务场景】
某电商单体(Spring Boot 单工程,200 万行,200+ 开发者提交冲突频繁、一次发布 40 分钟、任何改动都要全量回归)。日订单 300 万、峰值 QPS 3 万。要求:拆成微服务,但避免「拆得太细导致分布式事务爆炸」或「拆不透继续耦合」。
【面试官问题】
    1. 怎么划服务边界?2. 粒度多细合适?3. 共享库 vs 共享库耦合怎么破?4. 拆分顺序与数据怎么迁?5. 拆完后分布式事务怎么治?
【候选人的标准回答】

第一步:分析。拆分目标不是「多服务」,而是独立部署、独立演进、故障隔离。边界按业务能力(DDD 限界上下文)划,而非技术层。

第二步:挑战。边界模糊致耦合;粒度太细→运维/事务爆炸;数据库难拆;团队协作模式要变。

第三步:架构。按领域划分:用户、商品、库存、订单、支付、营销、物流。先绞杀者模式抽边缘域(商品/营销),核心交易最后拆;数据库按域垂直拆,共享表建为独立服务(如用户中心)。

第四步:选型。Spring Cloud Alibaba(Nacos/OpenFeign/Sentinel)+ DDD 限界上下文 + 绞杀者迁移。

第五步:一致性。跨域用最终一致(事件/Outbox);强一致仅限域内。

第六步:高可用。拆分即隔离;核心域独立资源池;灰度迁移。

第七步:优化。「先宽后窄」:先粗粒度(8~12 域)跑通,再按热点细分;避免过度微服务(康威定律匹配团队)。

【架构设计】
单体 → 绞杀者 → [用户中心][商品][库存][订单][支付][营销][物流] 每域: 独立DB + 独立部署 + 事件驱动(Outbox) 解耦 网关统一入口 → 服务间 OpenFeign → 跨域最终一致
【技术方案深度解析】

为什么 DDD 限界上下文?它是业务语义边界,比技术分层更稳。粒度:一个服务应由一个 2-pizza 团队维护;常见 8~15 个域起步,避免「一个接口一个服务」。共享库陷阱:把公共实体打成 jar 共享→改一处全编译,应改为「共享服务 + 防腐层(ACL)」。

【关键技术点】
DDD限界上下文绞杀者模式康威定律防腐层事件驱动数据库垂直拆分
【Java 实现示例】
// 防腐层:订单域调用用户域,不依赖其模型
public interface UserClientACL {
    UserSnapshot get(Long uid);  // 返回本域所需最小字段
}
// 绞杀者:老单体路由逐步切到新服务(Gateway 权重)
// 迁移顺序由依赖度决定:被依赖少的先拆
【面试官可能继续追问】
【常见错误回答】
  • ❌ 按技术层拆(Controller/Service 各自服务)——仍是强耦合。正确:按业务域。
  • ❌ 一个接口一个微服务——运维/事务爆炸。正确:团队级粒度。
  • ❌ 共享 jar 实体——改一处全编译。正确:防腐层 + 服务。
【架构师评分标准】
初级 0~40
按技术层拆,无边界概念。
中级 40~60
按业务拆但粒度/迁移混乱。
高级 60~80
DDD边界+绞杀者+防腐层+逐步迁移+事务治理。
架构师 80~100
再加:①康威定律匹配组织;②粒度演进策略;③双写校验;④成本与团队效能权衡。

第 32 题:服务雪崩与舱壁/熔断治理 稳定性

【真实企业业务场景】
订单服务调用「用户服务」做鉴权,用户服务因慢 SQL RT 从 20ms 涨到 3s,订单服务线程池被打满,连带支付、库存全部超时,全站雪崩 15 分钟。要求:从架构上阻断级联失败。
【面试官问题】
    1. 雪崩是怎么形成的?2. 舱壁、熔断、限流、降级分别治什么?3. 超时怎么设?4. 熔断阈值怎么定?5. fallback 怎么写才不丢数据?
【候选人的标准回答】

第一步:分析。雪崩 = 一个慢依赖耗尽调用方资源 → 调用方变慢 → 上游资源耗尽,连锁。四件套:限流(入口)、舱壁(隔离)、熔断(快速失败)、降级(保核心)。

第二步:挑战。资源耗尽、连锁、误判、降级丢数据。

第三步:架构。①每下游独立线程池(舱壁);②调用设超时(如 300ms)+ 重试有限;③错误率超阈值熔断(Sentinel/Resilience4j),半开探测恢复;④非核心降级 fallback;⑤入口限流防过载。

第四步:选型。Sentinel(限流/熔断/降级)+ Resilience4j(舱壁/重试)+ OpenFeign 集成。

第五步:一致性。熔断/降级只影响非核心;核心数据写仍需成功(排队/异步补偿)。

第六步:高可用。隔离防扩散;熔断防雪崩;超时防挂死。

第七步:优化。超时=依赖 P99×安全系数;熔断配最小请求数防抖动误判。

【架构设计】
上游 → 限流(Sentinel) → 舱壁线程池(每依赖独立) → 超时+熔断 → 下游 ↓熔断打开 fallback(降级/排队)
【技术方案深度解析】

熔断 vs 舱壁:舱壁防「一个慢依赖拖垮整个线程池」,熔断防「下游已死还持续打」。两者互补。超时:不设=线程永久挂起=雪崩;设太短=误熔断。用 P99+余量。fallback:非核心返回默认值/缓存;核心走异步队列稍后补。

【关键技术点】
舱壁模式熔断超时降级Sentinel级联失败
【Java 实现示例】
// 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
【面试官可能继续追问】
【常见错误回答】
  • ❌ 不设超时——线程挂死。正确:短超时。
  • ❌ 全服务共用线程池——一损俱损。正确:舱壁。
  • ❌ 熔断即丢弃核心数据——资损。正确:核心异步补。
【架构师评分标准】
初级 0~40
不知雪崩成因。
中级 40~60
知熔断,缺舱壁/超时。
高级 60~80
四件套+阈值联动+fallback分级。
架构师 80~100
再加:①超时基于 P99 推导;②熔断误判防护;③核心依赖特殊策略;④演练验证。

第 33 题:API Gateway 与 BFF 设计 网关

【真实企业业务场景】
App/小程序/H5/PC 四端共用后端,前端抱怨「一个页面调 12 个接口」;同时需统一鉴权、限流、灰度。要求:设计网关与 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;避免网关成业务层。

【架构设计】
客户端 → SLB → Gateway(鉴权/限流/灰度路由) → BFF(app/web) → 聚合[用户][商品][订单]... 网关不放业务聚合;BFF 按端定制,后端域纯净
【技术方案深度解析】

网关 vs BFF:网关关注「通用横切 + 路由」,BFF 关注「前端体验聚合」。把聚合放网关会让网关变胖变慢、难维护。为什么异步 WebFlux?网关高并发 IO 密集,响应式非阻塞提升吞吐、降资源。灰度:网关按 header/用户标签路由到新版本实例。

【关键技术点】
Spring Cloud GatewayBFFWebFlux灰度路由横切能力JWT验签
【Java 实现示例】
// 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);
【面试官可能继续追问】
【常见错误回答】
  • ❌ 把业务聚合塞进网关——网关变胖瓶颈。正确:下沉 BFF。
  • ❌ 每端调后端 12 次——前端慢。正确:BFF 聚合。
  • ❌ 网关同步阻塞——吞吐低。正确:WebFlux 异步。
【架构师评分标准】
初级 0~40
网关=业务层,无 BFF。
中级 40~60
有网关,聚合放错位置。
高级 60~80
网关/BFF 职责清晰+灰度+异步+限流。
架构师 80~100
再加:①横切能力边界治理;②多端演进策略;③网关容量/高可用;④前端体验度量。

第 34 题:服务注册发现与多机房寻址 注册中心

【真实企业业务场景】
服务从 50 扩到 300 个,某次 Nacos 集群抖动,大量实例被误剔除导致调用失败率飙到 30%。要求:选对注册中心、配好健康检查与分级寻址,避免「抖动即全摘」。
【面试官问题】
    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 节点集群;消费者本地缓存兜底;多级降级。

第七步:优化。保护阈值 + 心跳间隔调优防误剔;就近路由降延迟。

【架构设计】
实例 → 心跳 → Nacos集群(AP) → 推送/拉取 → 消费者本地缓存 调用: 同zone优先 → 跨zone兜底; 注册中心挂→用本地缓存继续调
【技术方案深度解析】

为什么 AP?注册中心挂比「短暂脏列表」危害小;AP 保证注册中心故障时服务仍可互调。保护阈值:当健康实例 < 阈值(如 80%),不再剔除,宁可负载高也不让流量无路可走。临时 vs 持久:临时实例靠心跳(进程死即删),持久实例手动(如 DB、配置)。

【关键技术点】
NacosAP/CP健康检查保护阈值本地缓存就近路由
【Java 实现示例】
// Nacos 保护阈值:健康比例过低保留全部实例
// application.yml
spring.cloud.nacos.discovery: health-check-interval: 5  # 秒
# 控制台设 protect-threshold: 0.8
// 消费者:启用本地缓存+就近(同 cluster 优先)
// NacosDiscoveryClient 默认带本地缓存,注册中心挂仍可读取
【面试官可能继续追问】
【常见错误回答】
  • ❌ 心跳 1s 超时 1 次就剔——抖动误剔。正确:多层超时+保护阈值。
  • ❌ 注册中心挂调用全失败——无本地缓存。正确:消费者缓存兜底。
  • ❌ 所有调跨越机房——延迟高。正确:就近路由。
【架构师评分标准】
初级 0~40
不知 CAP 取舍。
中级 40~60
会用 Nacos,不懂保护阈值。
高级 60~80
AP/CP+健康检查+缓存+就近+保护阈值。
架构师 80~100
再加:①注册中心高可用部署;②多机房寻址策略;③与配置中心隔离;④抖动自愈度量。

第 35 题:灰度发布与全链路灰度 发布

【真实企业业务场景】
新订单服务上线一次,因未灰度,bug 影响 100% 用户,资损 + 客诉 2 小时。要求:建立从网关到 DB 变更的全链路灰度,新版本先放 1%→10%→50%→100%,且出问题 1 分钟回滚。
【面试官问题】
    1. 蓝绿/滚动/金丝雀区别?2. 全链路灰度怎么保证同用户打到同一版本?3. 灰度流量怎么打标传递?4. DB 变更怎么灰度(不兼容怎么办)?5. 回滚怎么做快?
【候选人的标准回答】

第一步:分析。灰度=用小流量验证新版本,错误半径可控。难点在全链路:一个请求经过多服务,必须「打标→透传→路由」一致。

第二步:挑战。多服务版本一致性;标传递;DB 不兼容变更;快速回滚。

第三步:架构。①网关按用户标签(uid%100)路由到灰实例;②灰度标写入请求上下文透传(OpenTelemetry baggage / 自定义 header);③各服务按标路由到同版本;④DB 变更:先加字段(兼容)→ 双写 → 迁读 → 删旧(扩展式迁移);⑤回滚:保留旧版本实例 + 路由权重一键回 0。

第四步:选型。Nacos(权重)+ Spring Cloud Gateway(路由)+ OTel(标透传)+ K8s(多版本 Deployment)。

第五步:一致性。灰度标全链路透传保证「用户 x 全程同版本」;DB 兼容式变更保证新旧并存。

第六步:高可用。灰度故障仅影响小流量;监控对比新旧;自动熔断异常版本。

第七步:优化。基于指标(错误率/RT)自动推进/回滚;影子流量(复制真实流量到新版本比对)。

【架构设计】
用户 → Gateway(按uid%100打标) → [订单v2灰] → [库存v2灰] → [支付v2灰] 标透传(baggage) 保证同用户全链路同版本 DB: 加字段→双写→迁读→删旧(兼容式)
【技术方案深度解析】

蓝绿 vs 金丝雀:蓝绿是整量切换(快但错误半径大),金丝雀是逐步放量(安全但慢)。全链路灰度必须金丝雀+标透传。DB 不兼容变更:绝不能「改列类型一步到位」,要扩展式三阶段,保证新旧版本同时可读写。

【关键技术点】
金丝雀全链路灰度标透传兼容式迁移影子流量一键回滚
【Java 实现示例】
// 灰度标透传:ThreadLocal + Feign 拦截器
public class GrayInterceptor implements RequestInterceptor {
  public void apply(RequestTemplate t) {
    t.header("x-gray", GrayContext.get()); // 透传标
  }
}
// DB 兼容式:先加新字段,旧逻辑照常;新版本读新字段,逐步切换
【面试官可能继续追问】
【常见错误回答】
  • ❌ 直接全量发布——错误半径 100%。正确:金丝雀逐步。
  • ❌ DB 一步不兼容变更——新旧冲突。正确:兼容式三阶段。
  • ❌ 灰度标不传递——链路版本错乱。正确:全链路透传。
【架构师评分标准】
初级 0~40
全量发布,无灰度。
中级 40~60
网关灰度,无全链路透传。
高级 60~80
全链路标透传+兼容式迁移+快速回滚。
架构师 80~100
再加:①影子流量验证;②指标驱动自动推进/回滚;③MQ/DB 灰度闭环;④错误半径与 SLO 关联。

第 36 题:服务调用治理与超时重试 调用治理

【真实企业业务场景】
一次大促,订单→库存→优惠三跳调用,因「库存超时 2s + 优惠超时 2s」叠加,订单整体 RT 飙到 8s,线程池满。排查发现超时设了无脑 5s,重试放大 3 倍流量。要求:建立超时/重试/降级的治理规范。
【面试官问题】
    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 限重试流量。

【架构设计】
订单(预算500ms) ├─库存 ≤150ms (重试1,幂等) 强依赖 ├─优惠 ≤100ms (降级默认) 弱依赖 └─用户 ≤80ms (降级缓存) 弱依赖 超时级联传递; 重试指数退避+上限
【技术方案深度解析】

为什么级联超时?上游超时到了,下游还在跑=浪费+可能重复。用 deadline 让下游提前取消。重试风暴:每层重试 N 次,链路 M 层 → N^M 倍请求。必须限制总重试且退避。幂等:非幂等接口重试会重复扣款,必须带幂等键。

【关键技术点】
超时预算级联超时退避重试幂等依赖分级重试风暴
【Java 实现示例】
// 重试装饰:仅超时/未知异常,退避,上限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)
【面试官可能继续追问】
【常见错误回答】
  • ❌ 超时无脑 5s——RT 叠加雪崩。正确:预算分配级联。
  • ❌ 对所有错重试——业务错重复副作用。正确:仅可重试+幂等。
  • ❌ 重试无上限——风暴。正确:退避+上限。
【架构师评分标准】
初级 0~40
无超时/无脑重试。
中级 40~60
有超时,重试无幂等/无限。
高级 60~80
超时预算+安全重试+依赖分级+熔断。
架构师 80~100
再加:①级联 deadline 实现;②重试流量隔离限流;③全链 RT 大盘;④规范沉淀为框架。

第 37 题:微服务数据冗余与查询聚合 数据

【真实企业业务场景】
订单详情页要展示「用户昵称+商品标题+优惠券+物流」来自 4 个库,一次查询跨 4 服务 JOIN 不可行,RT 1.5s 且一服务挂全挂。要求:解耦查询、降 RT、提升可用。
【面试官问题】
    1. 微服务为什么不能跨库 JOIN?2. 冗余数据怎么保证不脏?3. CQRS 怎么落地?4. 聚合查询一致性怎么妥协?5. 冗余存储成本?
【候选人的标准回答】

第一步:分析。微服务「数据私有」,跨域查询需冗余/物化视图CQRS:写走领域,读走聚合视图。

第二步:挑战。跨库 JOIN、冗余一致性、聚合可用性。

第三步:架构。数据冗余:订单域通过事件(用户改名→UserUpdatedEvent)更新本地冗余用户昵称,读时无需跨服务;②CQRS:写模型(规范化的领域库)+ 读模型(宽表/ES 聚合视图),通过 CDC/事件同步;③聚合服务:BFF 并行调各域拼装,单域挂用缓存/默认值兜底。

第四步:选型。CDC(Canal/Debezium)→ MQ → 读模型(ES/宽表);事件驱动冗余。

第五步:一致性。读模型最终一致(秒级);关键字段(金额)以领域库为准,冗余仅展示。

第六步:高可用。冗余解耦,单域挂读视图仍可用;聚合降级。

第七步:优化。宽表减少 JOIN;缓存热点聚合;冗余字段最小化。

【架构设计】
[用户库]→CDC→MQ→[订单读模型(冗余昵称)] [商品库]→CDC→MQ→[订单读模型(冗余标题)] 写: 领域库(规范) 读: 宽表/ES(聚合) → 订单详情RT从1.5s→80ms
【技术方案深度解析】

为什么冗余?跨服务实时 JOIN 既破坏封装又耦合可用性。冗余以空间换解耦+性能。CQRS:读写模型分离,写侧重规范一致,读侧重性能与聚合。一致性妥协:展示字段允许秒级延迟(昵称改了订单页稍后更新),金额等强一致字段不走冗余。

【关键技术点】
CQRS数据冗余CDC物化视图最终一致聚合降级
【Java 实现示例】
// 监听用户改名事件,更新订单读模型冗余字段
@RocketMQMessageListener(topic = "user-updated")
public void on(UserUpdatedEvent e) {
  orderReadMapper.updateNickname(e.uid(), e.nick()); // 幂等按 eventId
}
// 读:直接查宽表,不跨服务
OrderDetail d = orderReadMapper.selectWide(orderId);
【面试官可能继续追问】
【常见错误回答】
  • ❌ 跨服务实时 JOIN——耦合+慢。正确:冗余/CQRS。
  • ❌ 冗余全量字段——膨胀。正确:最小必要字段。
  • ❌ 金额也冗余——脏数据资损。正确:金额走领域库。
【架构师评分标准】
初级 0~40
跨库 JOIN 思维。
中级 40~60
知冗余,无 CQRS/一致性分级。
高级 60~80
CQRS+CDC冗余+最终一致+聚合降级。
架构师 80~100
再加:①读写模型边界;②冗余字段最小集与成本;③CDC 可靠性;④强/弱一致字段分级。

第 38 题:服务契约与版本兼容 契约

【真实企业业务场景】
订单服务升级把「金额字段从 int 改 long 并改名 amountCent→amount」,未通知 30 个调用方,当晚 8 个服务解析失败报警。要求:建立接口契约管理与兼容性保障。
【面试官问题】
    1. 怎么保证接口变更不破坏调用方?2. 兼容性原则?3. 契约测试怎么做?4. 版本怎么管(URL/Header)?5. 破坏性变更怎么平滑?
【候选人的标准回答】

第一步:分析。微服务接口是契约,变更需向后兼容或显式版本演进,靠规范 + 契约测试兜住。

第二步:挑战。隐式变更、多调用方、破坏性升级。

第三步:架构。兼容性规则:只增字段(不删不改名不缩类型),老调用方忽略新字段;②契约测试:Pact 等 consumer-driven,CI 中校验 provider 满足 consumer 期望;③版本:优先「加字段兼容」,必须破坏性变更才升主版本(/v2 或 header),双版本并存过渡;④变更评审:接口变更走评审+影响面扫描。

第四步:选型。Pact(契约测试)+ OpenAPI(规范)+ CI 卡点 + 网关版本路由。

第五步:一致性。契约即接口事实标准;测试保证 provider/consumer 对齐。

第六步:高可用。兼容变更零停机;破坏性变更双版本平滑。

第七步:优化。接口文档自动化(Swagger);影响面工具扫描调用方。

【架构设计】
Consumer(测试期望) → Pact Broker ← Provider(校验满足) 发布门禁: 契约不匹配=CI 失败, 禁止发布 变更: 加字段(兼容) / 升主版本(破坏性,双版本并存)
【技术方案深度解析】

为什么 consumer-driven?provider 不知道所有 consumer 怎么用,让 consumer 定义期望,provider 在 CI 校验,才能防「改了不知谁挂」。兼容性:JSON/Protobuf 对「加 optional 字段」天然兼容;改名/改类型是破坏性的,必须版本化。

【关键技术点】
契约测试Pact向后兼容版本演进OpenAPI发布门禁
【Java 实现示例】
// 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 校验满足该契约,否则发布失败
【面试官可能继续追问】
【常见错误回答】
  • ❌ 直接改字段名——调用方挂。正确:加字段兼容/版本化。
  • ❌ 无契约测试——改完不知谁崩。正确:consumer-driven。
  • ❌ 破坏性变更不并存——硬切。正确:双版本过渡。
【架构师评分标准】
初级 0~40
随意改接口。
中级 40~60
有文档,无自动契约校验。
高级 60~80
兼容性规则+契约测试+版本化+评审。
架构师 80~100
再加:①影响面扫描;②破坏性变更平滑策略;③文档自动化;④契约纳入发布门禁。

第 39 题:多租户 SaaS 架构 SaaS

【真实企业业务场景】
SaaS CRM 平台服务 5000 家企业租户,小租户 50 人、大租户 5 万人。要求:隔离(A 租户看不到 B 数据)、成本可控(不能一租户一库烧钱)、大租户可独占、小租户共享。
【面试官问题】
    1. 三种隔离模式怎么选?2. 共享库怎么防越权?3. 大租户独占怎么切?4. 租户路由怎么做?5. 成本怎么平衡?
【候选人的标准回答】

第一步:分析。多租户核心矛盾:隔离性 vs 成本。三种模式:独立库(隔离最强最贵)、共享库+租户ID(最省但易串)、混合(大独占小共享)。

第二步:挑战。越权、成本、弹性、路由。

第三步:架构。混合隔离:小租户共享库(行级 tenant_id 隔离+强制过滤),大租户独立 schema/库;②租户路由:登录后 tenant_id 写入上下文,MyBatis 拦截器自动拼 tenant_id(防漏写);③资源配额:按套餐限流/配额;④大租户迁移:达到阈值自动迁独立库。

第四步:选型。共享 MySQL+tenant_id 行隔离;独立库按租户;MyBatis 拦截器强制租户过滤;连接池按租户分组。

第五步:一致性。租户上下文全程透传;所有查询强制带 tenant_id,防越权。

第六步:高可用。大租户独立资源防「吵闹邻居」;共享池配额隔离。

第七步:优化。按租户数据量自动升降级隔离级别;冷热分离降本。

【架构设计】
租户登录 → tenant_id 上下文 → [路由] → 大租户:独立库 / 小租户:共享库(tenant_id过滤) MyBatis拦截器强制拼 tenant_id; 配额/限流按套餐
【技术方案深度解析】

三种模式权衡:独立库运维/成本高但合规强;共享库省但靠纪律(一行漏写 tenant_id 就串数据);混合最务实。防越权关键:别指望业务代码每次写对,用拦截器在 SQL 层强制注入 tenant_id,业务无感知且不可绕过。

【关键技术点】
多租户隔离行级隔离tenant_id路由MyBatis拦截器配额混合隔离
【Java 实现示例】
// 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 替换
  }
}
【面试官可能继续追问】
【常见错误回答】
  • ❌ 全独立库——小租户烧钱。正确:混合隔离。
  • ❌ 靠业务拼 tenant_id——漏写串数据。正确:拦截器强制。
  • ❌ 大租户共享池——吵闹邻居。正确:独占升级。
【架构师评分标准】
初级 0~40
无隔离概念。
中级 40~60
单模式,无强制隔离。
高级 60~80
混合隔离+拦截器强制+路由+配额。
架构师 80~100
再加:①自动升降级隔离;②成本模型;③合规与区域;④大租户迁移工具。

第 40 题:Service Mesh 落地评估 Service Mesh

【真实企业业务场景】
公司 300 服务、多语言(Java/Go/Python),治理逻辑(限流/熔断/追踪)散落在各 SDK,升级要改 300 个服务。CTO 想引入 Istio。要求:评估是否值得上 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);只注入需要的服务。

【架构设计】
App → Envoy(Sidecar) → [熔断/限流/mTLS/追踪] → Envoy → App 控制面 Istio 下发规则; 多语言统一治理, SDK 解耦
【技术方案深度解析】

为什么上 Mesh?多语言+治理统一演进是核心收益;纯 Java 单栈用 Spring Cloud SDK 更轻。代价:Sidecar 每实例常驻资源、RT +1~3ms、运维门槛高(需 K8s 熟练)。过渡:双模,规则抽象层兼容 SDK 与 Mesh,逐步切。

【关键技术点】
IstioEnvoySidecar多语言治理mTLS双模过渡
【Java 实现示例】
// 治理规则统一抽象(SDK/Mesh 皆可驱动)
interface TrafficPolicy { // 限流/熔断规则,Mesh 与 SDK 共用语义
  void apply(CircuitBreaker cb, RateLimit rl);
}
// K8s VirtualService 示例(Istio):
// apiVersion: networking.istio.io/v1beta1  kind: VirtualService
// 限流/熔断在控制面配置,应用无感知
【面试官可能继续追问】
【常见错误回答】
  • ❌ 为时髦硬上 Mesh——复杂度爆炸。正确:看多语言/治理痛点。
  • ❌ 全量一刀切——风险高。正确:双模渐进。
  • ❌ 忽略 Sidecar 损耗——容量不足。正确:容量评估。
【架构师评分标准】
初级 0~40
不知 Mesh 代价。
中级 40~60
知概念,无权衡。
高级 60~80
收益/代价权衡+渐进落地+性能评估。
架构师 80~100
再加:①单语言栈对比(SDK 更优);②双模过渡设计;③Sidecar 资源与 RT 容量模型;④可观测整合。
第三部分 · 微服务架构设计(第 31~40 题) · 返回大纲