Skip to content

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

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


【面试题】1. DDD 里聚合的设计原则是什么?聚合太大或太小分别会导致什么问题? ​

【标准答案】聚合是 DDD 的核心战术模式——一组"必须保持一致性的领域对象集合",聚合根是外部访问的唯一入口。设计原则四条:一、小聚合优先——一个聚合一个事务边界,聚合太大事务锁范围也大,并发下容易死锁;二、通过 ID 引用而非对象引用——聚合之间用唯一 ID 关联,避免跨聚合懒加载和跨聚合事务的诱惑;三、聚合内强一致性(ACID),聚合间最终一致性(事件/消息)——不要在单个事务里跨两个聚合修改数据;四、聚合根是业务不变量的唯一守门人(比如"订单总额=商品价格×数量+运费")。聚合太大→一个请求锁住整张表→并发能力暴跌→用户投诉卡顿。聚合太小→每个实体一个聚合→本该原子操作的业务逻辑被拆成多个异步事件→分布式事务满天飞→数据不一致线上事故。面试一句:聚合大小取决于你的业务不变量——一致性必须在边界内的进聚合,能接受短暂不一致的跨聚合走异步。


【面试题】2. 客户端服务发现和服务端服务发现架构有什么区别?各有什么适用场景? ​

【标准答案】客户端发现就是 Spring Cloud+Nacos 模式——服务实例启动时向注册中心注册地址和元数据,调用方从注册中心拉取服务列表,在本地通过负载均衡算法选实例发起调用。服务端发现是 K8s Service 模式——调用方打到 Service 固定 VIP,由 kube-proxy 根据 iptables/IPVS 规则转发到 Pod。客户端发现:优势是调用方完全掌控负载均衡策略(同机房优先、灰度路由),不依赖底层基础设施;劣势是每个语言技术栈都要配注册中心客户端,异构技术栈运维成本高。服务端发现:优势是调用方零侵入——一个 HTTP 请求就完事,什么语言都能调;劣势是负载均衡只能做四层(IP 级别),七层路由(按 header、按参数)做不了。生产上两者混用——内部微服务 RPC 调用走客户端发现获取全部路由能力,外部流量和简单 HTTP 服务走 K8s Service 省心。面试一句:客户端发现是"自己拿地图自己导航",服务端发现是"告诉调度员目的地然后等你送我过去"——灵活但多学工具 vs 省心但路线有限。


【面试题】3. 微服务网关层怎么统一处理跨域 CORS?为什么不能直接配 Access-Control-Allow-Origin: *? ​

【标准答案】网关统一处理跨域是标准做法——不让每个微服务各自配跨域,集中管理避免配置漂移。但直接配通配符在生产上两宗罪:一、和 Access-Control-Allow-Credentials: true 不兼容——浏览器规范禁止同时使用通配符和携带 Cookie,你的鉴权 Token 如果靠 Cookie 传递会直接失效;二、暴露给任意域名——任何网站都能跨域调用你的 API,CSRF 风险城门大开。正确做法:在网关 CorsWebFilter 配白名单——allowedOrigins 列出你的前端域名,allowedMethods 只开 GET/POST/PUT/DELETE 够用的几个,allowedHeaders 同理。关键安全意识:跨域不是安全机制,浏览器同源策略只是保护"不能跨域读取响应",不能替代鉴权——关了 CORS 的 API,curl 和 postman 依然能直接调。网关真正的安全防线是认证鉴权 Filter:JWT 验签、IP 白名单、防重放。面试一句:跨域是浏览器的门锁——只防君子不防小人;你的鉴权 Filter 才是网关保险柜——各司其职,一个不能省。


【面试题】4. 生产环境的数据库密码、API Key 等敏感配置怎么管理?直接放 Nacos 里有哪些风险?有哪些分层方案? ​

【标准答案】敏感配置的生命周期管理是配置中心绕不过的安全坎。Nacos/Apollo 有权限控制(命名空间隔离、RBAC),但配置值是明文存储——运维能看到、日志打出来也能看到、配置快照里也躺着。安全等级分三档。第一档对称加密:配置值存密文(AES 加密),应用启动时用本地密钥解密。简单但密钥管理麻烦——密钥泄露全部兜底。第二档 Jasypt 集成:密码字段用 ENC(xxx) 包起来,Spring Boot 无缝集成,主密钥通过环境变量/K8s Secret 注入不入仓库不打日志,够用且不过度设计。第三档 Vault/KMS:配置中心只存指向路径,应用启动时动态从 Vault 或云厂商 KMS 拉取密钥,密钥定期自动轮转(30 天一换),泄露窗口缩短到分钟级。生产上中厂 Jasypt 方案足够——敏感性不是靠"配置中心有多安全",而是假设被攻破了攻击者拿到的全是密文干瞪眼。面试一句:敏感配置安全不是赌配置中心不被攻破,而是攻破了你也只能看到一串加密后的乱码。


【面试题】5. 分布式系统的 Quorum(法定人数)机制是什么原理?为什么 ZooKeeper 和 etcd 写数据必须过半数节点确认? ​

【标准答案】Quorum 机制解决"节点会挂、网络会裂时,怎么保证读到最新写入的数据"。核心公式 R + W > N——N 是总副本数,W 是写入需要确认的节点数,R 是读取需要查询的节点数。只要写入和读取的节点集合有重叠(两边加起来超过 N),读就一定能撞上最新数据。ZooKeeper 和 etcd 的过半写是特殊形式 W=N/2+1, R=1。读只有一个节点,怎么保证最新?靠 Raft 的 Leader 机制——只有 Leader 能写,读发给 Follower 如果日志没追上就拒绝或转发给 Leader。过半写的数学保证:脑裂时旧 Leader 在少数派分区收不到过半确认→拒绝写入→不会双主。多数派选举新 Leader 后正常服务。其他经典组合:W=N, R=1 写全量读任意(写慢读快,适合读多写少);W=1, R=N 写任意读全量(写快读慢,适合写多读少)。面试一句:Quorum 是分布式系统的投票箱——超过半数人投票说"我存好了",才能告诉你"这事成了"。


【面试题】6. Seata 的 TC(Transaction Coordinator)自身怎么保证高可用?TC 宕机后正在进行的全局事务会丢吗? ​

【标准答案】Seata TC 的高可用分三层保障。第一层——TC 集群部署:TC 通过注册中心(Nacos)注册多个实例,TM/RM 通过注册中心发现 TC,一个 TC 挂掉长连接断开后客户端自动重连到另一个 TC。第二层——全局事务会话持久化:TC 内存里的全局事务状态(xid→分支事务状态映射)靠共享存储层保证不丢。Seata 支持把会话存到 Redis 集群、数据库或文件。TC A 宕机后,TC B 接管同一个 xid 时直接从 Redis 加载状态继续协调。第三层——定时事务恢复:Seata 有定时任务扫描超时的全局事务(分支超时未回应),自动触发回滚,不被个别 RM 宕机拖死。生产建议:TC 3 节点集群 + Nacos 注册 + Redis 共享存储,任何一个 TC 节点挂掉秒级切换。额外注意:TC 和 RM 之间的 RPC 是全局事务关键路径,跨机房部署时延迟每多 10ms 就拖慢上千个事务。面试一句:TC 高可用不是"一台不够加两台"——内存状态靠 Redis 共享存储转移,角色切换靠注册中心,宕机扫描靠定时任务,三者合一才是活的 TC 集群。


【面试题】7. 新服务刚启动时,负载均衡直接把全量流量分给它会发生什么?Dubbo 和 Spring Cloud 怎么设计服务预热机制? ​

【标准答案】新服务刚启动直接接收全量流量会导致"冷启动雪崩"——JVM 还在 JIT 预热、连接池没建立、缓存全是空的、类还没加载完。典型现象:服务启动后前 30 秒 RT 飙升→被流量打死→重启→又被打死,死循环。预热机制核心是"新实例先以低权重接少量流量,时间线逐步增加"。Dubbo 预热是内置的——warmup 参数默认 10 分钟,权重从 0 线性增长到目标值:第 0 秒权重=0,第 5 分钟一半,第 10 分钟 100%。Spring Cloud LoadBalancer 需要自己实现:服务注册时通过元数据挂权重值,加定时任务逐步上调权重曲线;或者集成 Sentinel 系统自适应保护——Sentinel 根据当前系统 Load、RT、线程数自动判断实例能不能接流量,自动降低不合格实例权重。生产两个要点:预热时间合理——Java 至少 180 秒(JIT+连接池填充),Go/NodeJS 30 秒够;优雅上下线配合——先摘流量(权重为 0)、等存量请求处理完、再停服务,别把预热和关停搞成两头挨打。面试一句:预热像冬天热车——打着火就 120 码上高速,发动机直接报废。前几分钟怠速跑一跑,温度上来自然能飞。


【面试题】8. 熔断器的三态状态机(Closed/Open/Half-Open)是怎么流转的?Half-Open 为什么只放探针请求而不直接恢复全量? ​

【标准答案】熔断器三态精准对应"信任→不信任→试探→信任"的情绪曲线。Closed(正常):请求正常通过,同时用滑动窗口统计失败率/慢调用比例。窗口内失败率超阈值(如 50%),熔断器切为 Open。必须是滑动窗口——拿三天前的错误说现在要熔断毫无意义。Open(熔断):所有请求直接拒绝快速失败,给下游恢复时间。持续预设时长(如 60 秒)后自动切到 Half-Open。核心设计在 Half-Open:只放行少量探针请求(1~3 个),而不是全量恢复。原因——全量放开的代价:下游刚喘了口气还没完全恢复,所有流量又来把它打挂,熔断→恢复→熔断震荡循环,下游永远好不了。探针请求成功→切回 Closed 全量恢复;探针失败→重新切回 Open 继续等一个周期。工程上状态变更必须原子(CAS 或单线程状态机),避免多线程并发检测时状态来回跳动。面试一句:半开状态是"先派侦察兵去看看敌人死没死"——侦察兵活着回来大部队压上,侦察兵阵亡继续封锁,绝不把全军人命押在一次试探上。


【面试题】9. Redis 分布式锁的过期时间怎么设?设长了死锁,设短了业务没跑完锁过期——Redisson 的 Watch Dog 是怎么解决这个两难困境的? ​

【标准答案】这是 Redis 分布式锁最经典的两难:设 10 秒业务可能跑 15 秒(GC 停顿、网络抖动)→锁提前过期并发冲突;设 60 秒业务 5 秒就完成但进程崩溃→死锁 60 秒。Redisson 的 Watch Dog 精妙地用"自动续期"把这个两难变成无关。原理三步:一、加锁时不设过期时间,Watch Dog 线程默认每 10 秒(lockWatchdogTimeout/3)向 Redis 发一条 PEXPIRE key 30000 把 TTL 重置回 30 秒;二、业务执行期间 Watch Dog 持续续命——只要客户端进程活着,锁永不过期;三、进程主动解锁或崩溃→Watch Dog 死亡→30 秒后 Redis 自行删除。关键:Watch Dog 不依赖客户端的 finally 块释放锁,而是依赖"客户端活跃则续期→不活跃则过期"的自愈逻辑。两个工程细节:Watch Dog 只对没显式设 leaseTime 的锁生效——你手动指定锁时间说明你清楚业务最多跑多久,Redisson 不帮你续期;公平锁、联锁等高级锁也受 Watch Dog 守护。面试一句:Watch Dog 的设计哲学是"只要我还在呼吸,你就不准把锁收走;我死了,你不用管我,锁自己会消亡"。


【面试题】10. 微服务系统的告警风暴是怎么产生的?怎么设计告警分级体系(P0/P1/P2)避免"所有告警都是 P0 导致最终没人关心任何告警"? ​

【标准答案】告警风暴的根源是"把症状当根因"——一条慢 SQL→连接池耗尽→线程池打满→上游超时→网关 502,每个环节都设了阈值,全触发→运维群突突突 30 条全是噪音。告警分级的核心是"按影响面分级、按根因归并"。P0(生命级):核心业务黄金链路不可用——用户下单失败率>5%、支付成功率<95%。5 分钟无 ACK 自动打电话。P1(功能级):核心功能降级但没全挂——"订单详情报错但下单正常"、"Redis 主从切换查询走磁盘"。30 分钟内处理,不打电话。P2(预警方):资源水位——"数据库连接池利用率>70%"、"磁盘剩余<20%",不紧急但必须看,可能是明天 P0 的前奏。两个防风暴技巧:抑制规则——"慢 SQL"和"线程池打满"同时触发,只发慢 SQL(根因),抑制线程池告警(症状),Prometheus Alertmanager 原生支持;告警聚合——10 条"订单服务 CPU>90%"合并成一条带计数的通知,不重复刷屏。面试一句:告警不是越多越安全,是"响的每一声都值得被重视"才安全——分级做不好,运维终有一天把所有告警静音,真出事的时候一群人在睡觉。


📋 考点覆盖:微服务架构设计原则(DDD 聚合设计原则)→ 服务注册与发现(客户端发现 vs 服务端发现)→ 服务网关(CORS 跨域与安全)→ 配置中心(敏感配置加密管理)→ 分布式核心理论(Quorum 法定人数机制)→ 分布式事务方案(Seata TC 高可用设计)→ 负载均衡策略(服务预热与权重动态调整)→ 服务熔断降级限流(熔断器三态状态机)→ 分布式锁实现(Watch Dog 自动续期机制)→ 可观测性三支柱(告警分级与防风暴)

题型分布:概念题(1,5) | 原理题(8,9) | 方案对比题(2) | 架构设计题(6,10) | 场景面试题(3,4,7)

难度递进:基础(1-3) → 进阶(4-7) → 高阶架构(8-10)