Skip to content

微服务面试题 - 2026-06-19 17:39 ​

生成时间:2026-06-19 17:39 | 共 10 题 | 难度递进(基础→进阶→高阶)


【面试题】1. Nacos 同时支持 AP 和 CP 模式,分别对应什么注册中心的场景?什么时候该用 AP,什么时候该用 CP? ​

【标准答案】一句话:AP 模式(临时实例)用于"死了无所谓,活着继续干活"的日常微服务;CP 模式(持久实例)用于"数据绝对不能出偏差"的核心场景。架构区别:AP 模式下 Nacos 用的是自研的 Distro 协议——节点间异步复制数据,注册中心网络分区后各分区独立对外服务,分区恢复后再merge。代价是短暂不一致(一台机器挂了可能 5~10 秒其他机器还以为它活着),换来的是高可用——任何节点都能注册发现。CP 模式走 Raft 协议——必须多数节点存活且 Leader 选举成功才能提供服务,强一致性但牺牲可用性。选型口诀:API 服务用 AP——挂了切别的机器就行,慢几秒发现无所谓;数据库中间件、配置中心、支付网关的路由信息用 CP——一台机器被错误路由到会直接出生产事故。实操中 90% 的微服务都用 AP,CP 只在关键基础设施用。面试官想听到的区别不是概念而是"什么时候一致性没可用性重要"。


【面试题】2. CAP 理论中的 P(分区容错)在分布式系统中到底能不能放弃?为什么说"CAP 只能三选二"是误导? ​

【标准答案】P(分区容错)在分布式系统中不能放弃——分区就是网络故障,你没法"选择"不发生网络分区,它一定会发生。所以 CAP 的真相不是"三选二",而是"当分区发生时,你在 C(一致性)和 A(可用性)之间二选一"。分区没发生时,C 和 A 可以同时满足。以 Nacos 为例:网络正常时,注册中心既保证数据一致性又始终可对外服务。一旦 Nacos 集群出现脑裂(两个分区各自认为自己在线),AP 模式选择可用性——两个分区各自独立接请求,分区恢复后合并数据(可能出现短暂的不一致);CP 模式选择一致性——少数派分区拒绝服务,只保留 Leader 所在分区工作。面试官想听的是你理解"CAP 约束只在分区期间生效"而不是纠结 CAP 三个字母。BASE 理论就是 CAP 的工程妥协:BA(基本可用)对应舍弃 C 的强一致性,S(软状态)承认中间状态,E(最终一致性)保证最终会一致。一句话:架构师的工作不是喊 CAP,而是在"分区发生时到底要 C 还是要 A"这个决策上签字画押。


【面试题】3. 微服务拆分的第一步是什么?什么叫"先拆数据,再拆服务"?如果数据库是单体,拆服务有意义吗? ​

【标准答案】拆分第一步不是画服务边界图,是拆数据库。数据库是单体、服务拆得再细都是假拆分——A 服务 SELECT * FROM order 都不走 API 直接连同一个库 join user 表,这叫什么微服务?这叫"物理部署上的微服务,逻辑上的大单体"。正确顺序:第一步梳理数据依赖——把数据库中的表按领域归属分组,画出"哪张表被哪些业务修改"的依赖拓扑图。第二步是破圈——被多个业务同时修改的表(比如用户表的手机号既被用户中心改又被订单服务改),把它从共享表变成"归属表+API 访问":用户中心是 User 数据的唯一 Owner,订单服务要用用户手机号必须调用户中心的 API。第三步才是拆代码——数据已独立,服务拆起来只是把对应的代码迁到独立仓库+独立部署。判断拆得干不干净的标准:如果某个服务的开发能直接连另一个服务的数据库查数据——拆失败了。面试金句:先拆数据后拆服务,数据不拆就是换了个壳的单体。迁移期间可以用"双写+灰度切流",但目标永远是每个服务成为自己数据的唯一写入入口。


【面试题】4. 金丝雀发布和蓝绿部署的区别是什么?在微服务环境下,金丝雀流量怎么路由?流量染色具体怎么做? ​

【标准答案】蓝绿部署:准备两套完整环境(蓝=当前版本、绿=新版本),切换时流量全部从蓝切到绿,出问题一键切回蓝。优点是回滚快到一秒,缺点是要双倍机器。金丝雀(灰度):新版本只部署少量实例,先分一小部分流量(比如 5%)给金丝雀实例,跑稳了逐步扩大比例直到 100%。金丝雀的核心优势是不需要双倍机器,但对流量路由机制要求高。流量路由两类方案:网关层和注册中心层。网关层——Spring Cloud Gateway 的 Weight Route Predicate 按权重把 5% 请求路由到金丝雀服务的 ServiceId。流量染色——请求进网关时在 Header 注入 X-Canary: true,网关匹配这个标签路由到金丝雀实例;更好的方案是用 X-Canary-UserIds 指定让特定用户走金丝雀(内部员工先试毒)。注册中心层——Nacos 和金丝雀实例注册时打 metadata 标签 canary=true,消费端通过 Ribbon/LoadBalancer 按标签过滤只调金丝雀实例。两种方案常叠加:网关染色 + 注册中心标签路由,双重保障。最关键的设计:金丝雀实例出了问题不是"悄无声息地死掉",而是要有独立的监控大盘和告警规则——灰度期间的错误率对比基线如果高出 30% 立刻自动回滚。面试官真正想听的不是概念,是"灰度流量从哪个组件、以什么规则、如何监控、出问题时怎么回滚"的全链路。


【面试题】5. SkyWalking 的 traceId 是怎么在十几个微服务之间传递的?跨线程和跨 MQ 消息时 traceId 会断吗?怎么不断? ​

【标准答案】traceId 传递靠三段:入口生成→RPC 传播→业务代码透传。入口生成:请求到达第一个微服务时,SkyWalking Agent(Java Agent 字节码增强)自动拦截生成 traceId + segmentId + spanId,绑定到当前线程的 ThreadLocal。RPC 传播:Agent 拦截 HTTP 请求的 HttpClient.execute() 或 Dubbo 的 RpcContext,在请求 Header 中自动塞入 sw8: traceId-segmentId-spanId-...(SkyWalking v8 传播协议),下游 Agent 从 Header 里解析出来继续追踪。跨线程会断——你用 new Thread() 开了个异步任务,新线程的 ThreadLocal 是空的,链路就断了。解法:TraceRunnable.wrap() 或 @TraceCrossThread 把父线程的 trace 上下文传给子线程——Spring 的 @Async 配合 SkyWalking 的跨线程插件自动包裹。跨 MQ 也会断——生产者在发消息时 Agent 把 trace 上下文注入消息 Header(Kafka 的 Headers、RocketMQ 的 UserProperty),消费者收到消息后 Agent 从 Header 里提取上下文重新创建 Span。如果 MQ 消息没有自带 Header 机制(比如老版本的 Redis 队列),就只能手动:生产者把 traceId 放进消息体,消费者拿出来 TraceContext.createEntrySpan(traceId)。核心一句:"能不能不断取决于传播点的 Agent 有没有埋点"——Dubbo/Feign/Kafka Agent 自动埋了,裸 new Thread 和裸 Redis 没有,需要手动或换封装好的工具类。


【面试题】6. ZK 的临时顺序节点、Redis 的 setnx + Lua、etcd 的 Lease + 事务,三种分布式锁实现有什么本质区别?选型优先级怎么看? ​

【标准答案】三者核心差异在"锁的安全性模型"而不是 API 形式。ZK 的临时顺序节点:加锁时在 /lock 下创建 EPHEMERAL_SEQUENTIAL 节点,序号最小的拿到锁;解锁删节点。两个天然优势:一是客户端宕机 ZK 会话超时后临时节点自动删除——自带死锁防御,不用 Watch Dog;二是顺序节点天然公平——FIFO 等待队列,不会抢占式竞争。代价:ZK 依赖 Zab 协议,写锁操作要过半节点确认,吞吐低,不适合高频加锁(比如每秒上万次扣库存)。Redis setnx + Lua:单节点 Redis 性能极高,吞吐远超 ZK,但锁的安全性靠"红锁算法"(RedLock)的多数派确认来补——Redisson 的实现比裸 setnx 可靠但复杂度和 Redis 节点数正相关。核心缺陷:Redis 的主从异步复制可能导致锁丢失——主节点加了锁还没来得及同步给从节点就挂了,从节点升级为新主后不知道这把锁的存在。etcd Lease + 事务:和 ZK 一样走 Raft 强一致性协议,加锁操作用 Txn 事务保证"Key 不存在+创建 Lease +绑定 Lease"三者原子性。etcd 比 ZK 的优势是 HTTP API 更容易运维,且社区更活跃。选型优先级:低频高可靠(发奖金批处理、定时任务互斥)→ ZK/etcd;高频中可靠(扣库存、秒杀)→ Redis + Redisson + RedLock;超高可靠必须 100% 正确(金融对账)→ etcd。面试核心:不是一个"哪个更牛逼",而是"你的业务场景下,用可用性换一致性划不划算"。


【面试题】7. 跨服务的数据一致性除了分布式事务,还有哪些"非事务"手段?CDC + 最终一致性是怎么做到"异步但不丢数据"的? ​

【标准答案】分布式事务(2PC/Seata AT)本质是用"同步协调 + 锁定资源"强推一致性——代价是锁等待、吞吐低、耦合重。非事务手段有两类主流方案:事件驱动和 CDC。事件驱动(应用层发消息):本地事务完成 + 发领域事件 → 下游消费事件 → 更新自己的数据。这就是"本地事务表 + 消息队列"模式——业务操作和写 outbox 表在同一个本地事务,定时任务扫 outbox 发 MQ,"发了但没收到 ACK"的消息可重发。CDC(Change Data Capture):不靠应用代码发消息,直接监听数据库 binlog。Debezium 连上 MySQL 的 binlog,orders 表 insert 一条,binlog 里就有一条 insert 事件,Debezium 实时捕获 → 投递 Kafka → 下游订阅消费。好处:业务代码零侵入、消息一定不会丢(binlog 是数据库的事务日志,commit 了才有 binlog)、顺序严格保证。代价:依赖 binlog 解析格式,DDL 变更要维护,不支持业务逻辑转换(binlog 里只是 column_a=1,不包含"订单金额超过 1000 触发大额风控")。CDC + CQRS 的组合最常见:CDC 把订单表的变更事件流入 Kafka → 查询服务消费事件 → 维护一份"订单读模型"在 Elasticsearch 里,查询接口直接走 ES,读写完全分离。一句话:CDC 把"谁改了数据"和"数据改完之后要通知谁"解耦了,数据库只管 commit,剩下的交给 binlog 流水线。


【面试题】8. 线上一个服务拖死整个集群——什么叫"服务雪崩"?你用 Sentinel 怎么配规则才能让一个服务炸了不影响其他服务? ​

【标准答案】服务雪崩:A 调 B,B 调 C,C 的 DB 挂了导致 C 响应变慢→B 的线程都在等 C 响应→B 的资源被耗尽→A 调 B 也超时→A 也崩溃→整个调用链像多米诺骨牌全倒。核心机制是"慢调用传染"——下游慢导致上游线程堆积,不是下游直接报错而是"等着等着把自己的线程等没了"。Sentinel 三道防火墙。第一道:熔断——给 B 的调用配"慢调用比例熔断",统计窗口 10 秒内有 50% 的调用超过 500ms 就熔断,熔断期间请求直接走 fallback 返回兜底数据,不给 C 机会继续拖累。关键参数:比例熔断比异常比例熔断更早发现——线程在等没报错但已经慢成狗了,异常比例看不到问题。第二道:线程池隔离——B 调 C 的线程池独立配置,10 个线程全满后新请求直接拒绝(快速失败),不让 B 的 Tomcat 线程被 C 拖住。这个设计最关键——哪怕 C 死透了,B 的其他接口完全正常。第三道:RT 自适应限流——不设固定 QPS 阈值,设"系统负载保护":单机 load1 超过 CPU 核数自动限流,管你是不是正常流量先自保。三道一叠加,效果是:C 挂了 → B 熔断 C → 失败快速返回 → B 线程池不堆积 → A 调用 B 正常(除了调 C 的那个接口降级)→ 雪崩链打断。面试一句定乾坤:熔断保下游、隔离保线程、系统保护保命——三道防线缺一道都是在赌运气。


【面试题】9. 可观测性三支柱——Logging、Metrics、Tracing 各自能回答什么问题?三者怎么关联打通实现"一个 traceId 串起日志+指标+链路"? ​

【标准答案】三支柱各回答不同维度的问题。Logging(日志):回答"在这个时刻发生了什么"。查 ERROR 日志定位具体报错堆栈,适合排查已知请求的具体错误。Metrics(指标):回答"系统整体怎么样"。CPU 使用率、QPS、P99 延迟——适合告警和容量规划,告诉你"有问题了"但不告诉你"为什么"。Tracing(链路):回答"一次请求经过了什么"。A→B→C 的调用链每个 Span 的耗时,适合排查"为什么这个接口慢"。三者孤立的痛点:告警说 P99 延迟飙升→你打开 Tracing 看哪个 Span 慢→发现是 C 的 DB 查询慢→你打开 Logging 想看 C 的具体日志→但 traceId 和日志里没有任何关联,你要去 C 的日志里按时间 grep 大海捞针。打通的核心是 MDC(Mapped Diagnostic Context):SkyWalking Agent 把 traceId 注入当前线程的 MDC,Logback/Log4j2 的 Pattern 里配 %X{traceId},每行日志自动带 traceId。关联流程:Grafana 大盘里 P99 飙升 → 点进去看慢 Trace → 选中慢 Span 跳转到 SkyWalking RocketBot → 看到 Span 的 traceId → 拿这个 traceId 去 ELK/Kibana 里搜日志 → 所有该请求的日志一字排开 → 定位到慢 SQL。三支柱关联再加一层:把 Metrics 和 Tracing 的关联建好——SkyWalking 的 Span 里有 duration,可以被 Prometheus 抓取为 Histogram 指标,这样你在大盘上看到 P99 突增,直接 drill down 到具体 trace,不用切工具。一句总结:Logging 定位微观错误,Metrics 发现宏观异常,Tracing 串联调用路径,MDC 的 traceId 是把三者拼成一个闭环的那根线。


【面试题】10. 从 0 到一个日均 100 万 PV 的微服务系统,你会怎么规划技术选型、里程碑和风险兜底方案? ​

【标准答案】分五阶段,每阶段有明确的停止信号——不能没完没了地做完美架构。第一阶段(1~2 周):搭骨架。选 Spring Cloud Alibaba 全家桶——Nacos 注册配置一体、Gateway 网关、Sentinel 流控,不要引入太多组件,选型原则是"会用的人多 + 中文社区活跃 + 阿里自己也在用"。数据库 MySQL 主从+Redis Cluster+一个 MQ(Kafka 或 RocketMQ)。代码上先写一个"核心链路"跑通——用户注册→下单→支付,就这一条链路,验证技术栈能跑。第二阶段(1 个月):拆出核心服务。用户服务、订单服务、商品服务、支付服务——只拆这四个,其余的继续留在单体里。拆的标准是"当这个服务挂了会不会影响下单"——会就优先拆、做好容错。第三阶段(1 个月):补基础设施。SkyWalking 链路追踪、ELK 日志中心、Prometheus + Grafana 监控、CI/CD 流水线、配置中心灰度发布方案。这一阶段不做业务功能,全部补运维能力——上线前把"出问题后 5 分钟能定位到哪个服务"作为验收标准。第四阶段(MVP 上线+灰度):先内部 100 个用户跑一周→扩大到 1000 个→全量。灰度期重点观察三个指标:错误率、P99 延迟、数据库慢查询(这三个才是用户体感,QPS 高不等于好用)。第五阶段(上线后持续):数据库读写分离+分库分表(ShardingSphere)、引入 CDC 做数据同步、业务中台化抽取公共能力。风险兜底方案:每个阶段都有"退路设计"。注册中心挂了→RPC 客户端缓存本地服务列表;Redis 全挂→接口直接穿透到 DB 并立刻限流保护;MQ 积压→消费者动态扩容+积压超阈值临时切同步调用。最关键的一句话:架构师不是在搭一个不会出问题的系统,而是在搭一个"任何组件挂了都能以优雅姿势降级而不是闪崩"的系统。"优雅降级"比"永不宕机"更值钱。