微服务与分布式系统面试题
生成时间:2026-06-19 16:39 | 共 10 题 | 难度递进:基础 → 进阶 → 高阶
【面试题】1. DDD 战略设计里事件风暴怎么搞?限界上下文怎么从事件风暴中识别出来?
【标准答案】事件风暴(Event Storming)不是画 UML,是拉着业务专家、产品、开发蹲在白板前,用便利贴把业务上"发生了什么"全抖出来。做法三步:第一步,橙色的"领域事件"铺开——"订单已提交"、"库存已扣减"、"支付已完成",不管顺序先全贴出来;第二步,按时间线串起来形成事件流,这时候会发现有些事件天然扎堆,比如订单相关的一组事件和物流相关的一组事件之间有明显的断裂点;第三步,把扎堆事件圈出来,翻译成"限界上下文"——一个圈里的事件归一个上下文管。判断上下文边界的三条铁律:事件归属统一(该事件只在一个上下文里产生)、该上下文内的模型术语一致("订单"在这上下文里的含义唯一)、数据主权清晰(谁生成谁负责)。面试官真正想听的是:你没把 DDD 当画画工具,而是把它当团队对齐业务认知的手段。
【面试题】2. Nacos 2.x 相比 1.x 做了哪些核心架构升级?为什么升级后性能提升那么大?
【标准答案】Nacos 2.x 最核心的变化是把 1.x 的 HTTP 轮询/长轮询全换成 gRPC 长连接。1.x 的痛点:客户端定期短连接请求 + 长轮询 hold 住连接,海量实例时 HTTP 连接数爆炸——1000 个服务实例,每个每 5 秒发心跳,服务端维持的短连接压力不说了。2.x 用 gRPC 双向流,每个客户端和服务端只维持一条长连接,心跳和配置变更全走这个通道,连接数从 O(n²) 掉到 O(n)。第二个升级是事件驱动模型:1.x 配置变更要等长轮询超时或下次轮询才拉,2.x Server 主动 push 增量和批量事件通过 gRPC Stream 推给客户端,延迟从秒级降到毫秒级。第三个是分层架构:内核层(一致性协议)+ 插件层(服务发现/配置管理)拆开,性能瓶颈清晰可治。一句话总结:1.x 是"客户端不停敲门问有没有变化",2.x 是"服务端有变化直接打电话告诉你"。
【面试题】3. Gateway 的过滤器链是怎么走的?网关层怎么拦截恶意请求?
【标准答案】Gateway 核心是 Route + Predicate + Filter 三件套。一个请求进来→先走 Predicate 匹配路由(比如 /order/** 匹配到订单服务路由)→然后过滤器链按顺序执行。过滤器分两级:GlobalFilter 对所有路由生效,GatewayFilter 只对特定路由生效。执行顺序靠 Order 值控制,NettyWriteResponseFilter 排最后(回写响应),RemoveCachedBodyFilter 排最前。安全防护实战做三层:第一层 IP 黑白名单——在 GlobalFilter 里取请求来源 IP,匹配黑名单直接 403;第二层参数校验和 XSS/SQL 注入过滤——读取请求体做正则校验,命中危险关键字直接拒绝;第三层防重放攻击——要求请求头带时间戳+签名,服务端验时间戳在 5 分钟内且签名正确。实现时注意:Gateway 基于 WebFlux,不能像 Zuul 那样同步阻塞读取 body 多次,要用 cacheRequestBody 装饰器先缓存再校验,否则 RequestBody 读了就没了。
【面试题】4. 配置中心的敏感信息(数据库密码、API Key)如何安全存储?配置灰度发布怎么落地?
【标准答案】敏感信息保护三层:第一层加密存储——不要在配置中心明文贴密码,Nacos 支持配置加密插件(如 Jasypt),配置存的是密文,客户端启动时用本地密钥解密。第二层访问控制——生产环境的敏感配置 Namespace 只对核心服务可见,开发和测试环境看不到生产密钥。第三层审计——谁改了敏感配置、什么时候改的,必须有操作日志可追溯。配置灰度发布的核心是"别一刀切让所有实例同时刷新"。Apollo/Nacos 的做法:先把新配置标记为灰度状态,指定灰度规则(比如 IP 段或实例标签),只有匹配的实例拉到这个配置,其他实例还跑旧配置。观察一段时间(监控、业务指标无异常),再全量发布。如果翻车,一键回滚到上一个版本,不需要重新构建部署改代码。面试加分:灰度回滚比代码回滚快 100 倍,这恰恰是配置中心最值钱的能力,也最能打动面试官。
【面试题】5. Raft 协议解决的核心问题是什么?Nacos 里 Raft 扮演什么角色?
【标准答案】Raft 干一件事:让一堆机器在没有"上帝视角"的情况下,对"当前数据是什么"达成一致。核心分两阶段——Leader 选举和日志复制。选举:节点有三种身份(Leader、Follower、Candidate),每个节点启动是 Follower,超时没收到 Leader 心跳就变 Candidate 发起投票,票数过半当选 Leader。日志复制:Leader 接写入→写本地日志→并行发 AppendEntries 给 Follower→过半确认就提交→返回客户端成功。Nacos 里 Raft 两个用处:一是集群元数据同步(有哪些服务注册了),二是持久化服务模式(选 CP 时用)。但注意 Nacos 默认 AP 模式,服务注册走 Distro 协议异步同步,只有切到 CP 或用持久化实例时才会走 Raft。面试官想确认:你知道 Raft 不是默认开着的,也知道"强一致是需要代价的"。
【面试题】6. RocketMQ 事务消息怎么保证"本地事务和消息发送"同时成功?半消息机制是核心吗?
【标准答案】对,半消息(Half Message)是 RocketMQ 事务消息的灵魂。流程:第一步,生产者发一条半消息给 Broker——这条消息对消费者不可见,Broker 记下来并返回成功。第二步,生产者执行本地事务(比如扣库存),执行成功就告诉 Broker Commit,失败就 Rollback。第三步,Broker 根据 Commit/Rollback 决定消息扔给消费者还是丢弃。核心命门:万一第二步之后网络断了,Broker 一直不知道 Commit 还是 Rollback 怎么办?Broker 启动回查机制——定期去问生产者"你那笔本地事务到底成了没?",生产者必须实现 checkLocalTransaction 方法返回事务状态。这样就做到:本地事务成功→消息必投递,本地事务失败→消息必回滚,中间态靠回查补齐。半消息不是 trick,是二阶段提交思想在消息领域的工程化改造。落地时记得:回查接口要幂等,不能用"查个数据库就返回"这么简单——要能处理线程中断、超时等各种异常。
【面试题】7. 加权轮询和最小活跃数负载均衡算法分别怎么实现?各自适合什么场景?
【标准答案】加权轮询(Weighted Round Robin)核心是"权重高的多分配,但不能连续分配"。平滑加权轮询(Nginx、Dubbo 在用)做法:每个实例维护一个 currentWeight,初始为 0,每次选择时 currentWeight += weight,选出最大的那个实例,然后把它的 currentWeight 减去所有权重之和。这样权重为 3,1,1 的三个实例分配序列是第一个实例出现 3 次但在 5 次总请求里均匀撒开,不会 3 次连续砸给同一个。最小活跃数算法是"谁现在最闲给谁"——每个实例维护一个 activeCount(正在处理的请求数),选择 activeCount 最低的那个,同等低时按权重选。适合"请求耗时差异大"的场景——有的接口快有的慢,不能按请求数均摊,要看谁手上活少就给谁。比如同时有查配置(1ms)和生成报表(500ms),轮询会卡死生成报表的实例。一招记住:轮询是"请求层面公平",最小活跃数是"实例负载公平",前者适合同质请求,后者适合异质请求。
【面试题】8. Sentinel 集群流控是怎么实现的?Token Server 挂了怎么办?
【标准答案】集群流控对付的场景是:你想限制某个服务的总 QPS 为 1000,不是每个实例各限 1000,而是所有实例加起来 1000。Sentinel 的方案是引入一个 Token Server,所有客户端去 Token Server 申请令牌。分两种模式:独立模式——部署一个独立的 Token Server 进程,所有应用实例远程调用它,适合跨应用统一限流;嵌入模式——Token Server 跑在集群的某个实例上,其他实例 RPC 调用它,同集群内限流。Token Server 挂了怎么兜底?Sentinel 加了一个"退化到单机限流"的机制——检测 Token Server 不可用超时后,自动切换到单机流控模式,用预配置的单机阈值(或集群阈值/实例数)继续限流。Token Server 恢复后自动切回去。还有一个细节:Token Server 本身可以做集群部署,基于一致性哈希分桶减少单点压力。面试亮点:集群限流比单机限流多了一层 Token Server 依赖,架构复杂度上升,不到万不得已不上——绝大多数场景 QPS 阈值拆到各实例就够用了。
【面试题】9. etcd 分布式锁和 Redis、ZK 有什么本质区别?三选一你会怎么决策?
【标准答案】本质区别在实现机制和一致性模型。etcd 锁基于 Lease(租约)+ Revision(全局递增版本号)+ Watch 机制:每个抢锁者创建绑定 Lease 的 key,etcd 保证 key 创建顺序基于全局 Revision 严格递增,Revision 最小的拿锁,其他 Watch 前一个 Revision 的 key 等通知。这和 ZK 的临时顺序节点很像,但 etcd 用 Raft 日志实现比 ZK 的 ZAB 更简洁。核心差异表:Redis 是 AP——性能最高(万级 TPS),但极端情况下可能丢锁(主从切换);ZK 是 CP——强一致不会丢锁,但性能低(百级 TPS),集群运维重;etcd 也是 CP——强一致,但设计更现代、运维更轻,性能稍好于 ZK。决策规则:高频抢购秒杀→Redis/Redisson(容忍极低概率的重复);核心资金交易→etcd(如果能用 K8s 生态天然集成)或 ZK(如果已有 ZK 基建);中小项目不引入新中间件→Redis 锁够用。一句总结:选型不是比"谁最牛",是比"谁最合适"——etcd 再好,没有运维经验就是定时炸弹。
【面试题】10. 线上一个接口突然 P99 延迟从 50ms 飙到 3s,但 CPU、内存、线程数都正常,你怎么排查?
【标准答案】CPU 正常但响应飙高,问题不在计算,在"等待"。排查思路五步:第一步,看接口的链路追踪(SkyWalking/Trace),找到具体是哪个下游调用变慢了——可能是 Redis、MySQL、别的微服务;第二步,如果是 MySQL 慢,打开慢查询日志或 show full processlist,看是锁等待还是全表扫描,大概率索引失效或优化器选了错误执行计划(analyze table 更新统计信息可能解决);第三步,如果是 Redis 慢,slowlog get 100 看是不是有大 key 操作(HGETALL 一个几十万字段的 hash),或者 key 集中在某一台导致热点;第四步,如果是下游服务慢,看下游的线程池是不是打满了——线程池满的表现不是 CPU 高而是所有请求排队等线程;第五步,上面都正常就看网络——tcpdump 抓包看有没有大量重传,ping 一下延迟,大概率是跨机房调用或者某台交换机故障。一层层剥洋葱,别上来就重启,丢了现场第二天还会复现。Arthas 的 trace 命令从入口方法往下追踪每一步耗时,是排查黑盒远程调用最直接的工具。