Microservices 面试题
微服务面试评估你设计和构建可扩展、去中心化系统的能力。它们通常针对具有深厚分布式计算、API 和数据一致性知识的高级工程师和架构师。预计会有架构讨论、权衡分析和实际编码场景。
Microservices 面试涵盖内容
服务分解与设计
如何将单体应用拆分为服务、限界上下文和领域驱动设计原则。
服务间通信
同步与异步模式、API 网关、服务网格和通信协议。
数据管理与一致性
处理分布式事务、最终一致性、Saga 模式以及 CQRS/事件溯源。
部署与可观测性
分布式系统中的容器化、编排(Kubernetes)、监控、日志记录和追踪。
Microservices 面试题示例
- 如何将大型单体应用分解为微服务?逐步说明你的决策过程。好回答应覆盖
- 进行领域驱动设计(DDD),识别限界上下文
- 评估模块间的耦合度,优先解耦低内聚高耦合模块
- 逐步拆分,先构建新微服务并代理对单体模块的调用
- 为每个微服务设计独立的数据库或数据模式
- 实施自动化部署和测试,确保服务独立可发布
查看范例答案
分解单体应用的首要步骤是通过领域驱动设计(DDD)识别限界上下文,这些上下文自然对应微服务的边界。然后分析现有模块间的依赖关系,优先分解那些与业务逻辑清晰、依赖较少的模块。拆分时采用绞杀者模式(Strangler Pattern),先将单个功能抽取为微服务,通过API代理替代直接调用,逐步迁移所有功能。数据也需要解耦,每个服务应有自己的持久化存储或至少独立的数据表,并通过事件或API共享数据。最后,为每个服务建立CI/CD流水线,确保独立部署和扩展,同时通过契约测试和API版本管理保持兼容性。
- 解释 Saga 模式中编排和编舞的区别。什么时候你会使用每种?好回答应覆盖
- 编排(Choreography)依赖事件驱动,各服务自主响应事件
- 编舞(Orchestration)依赖中央协调器控制流程
- 编排适合事件松散、服务数量多的场景,编舞适合需要严格流程控制的场景
查看范例答案
Saga模式中的编排与编舞是两种不同的协调方式。编排中没有一个中央协调器,每个服务在完成本地事务后发布事件,其他服务通过监听这些事件来触发下一步动作,形成事件链。编舞则有一个中央协调器(如状态机)负责发起每个步骤,并接收结果决定下一步。选择编排的场景是当服务数量较多且耦合度要求低时,例如电商订单流程中,订单服务发布“订单创建”事件,库存服务监听并扣减库存。选择编舞的场景是当业务流程复杂且对操作顺序有严格要求时,例如银行转账需要严格的先扣款后入账,且失败时需要明确补偿逻辑。编排更易扩展和演进,但可能导致事件环回和调试困难;编舞更易控制,但协调器可能成为瓶颈和单点故障。
- 用你偏好的语言编写一个简单的断路器实现(可接受伪代码)。好回答应覆盖
- 使用状态机模式管理三种状态:CLOSED(关闭)、OPEN(打开)、HALF_OPEN(半开)
- CLOSED状态下正常调用,记录失败次数,达到阈值后进入OPEN
- OPEN状态下直接拒绝请求,经过超时后进入HALF_OPEN
- HALF_OPEN状态下允许有限请求探活,成功则回到CLOSED,失败则回到OPEN
查看范例答案
以下是用Python实现的一个简易断路器,包含状态转换、失败计数和超时机制。注意:本实现仅为演示原理,生产环境应使用成熟库,如Hystrix或Resilience4j。代码中维护了失败计数和阈值,通过time.sleep模拟探活等待。实际使用时需考虑线程安全、并发和更精细的配置。
参考代码python import time from enum import Enum class CircuitState(Enum): CLOSED = 1 OPEN = 2 HALF_OPEN = 3 class CircuitBreaker: def __init__(self, threshold=5, timeout=10): self.state = CircuitState.CLOSED self.fail_count = 0 self.threshold = threshold self.timeout = timeout self.last_failure_time = None def call(self, func, *args, **kwargs): if self.state == CircuitState.OPEN: if time.time() - self.last_failure_time >= self.timeout: self.state = CircuitState.HALF_OPEN else: raise Exception("Circuit breaker is OPEN, request blocked") try: result = func(*args, **kwargs) self._on_success() return result except Exception as e: self._on_failure() raise def _on_success(self): self.fail_count = 0 if self.state == CircuitState.HALF_OPEN: self.state = CircuitState.CLOSED def _on_failure(self): self.fail_count += 1 self.last_failure_time = time.time() if self.state == CircuitState.HALF_OPEN: self.state = CircuitState.OPEN elif self.state == CircuitState.CLOSED and self.fail_count >= self.threshold: self.state = CircuitState.OPEN - 如何处理具有最终一致性的分布式事务?提供一个具体示例。好回答应覆盖
- 采用Saga模式协调多个本地事务,每个事务有对应补偿操作
- 使用最终一致性,允许短暂数据不一致,但最终会汇聚到一致状态
- 示例:订单创建与库存扣减,使用编排Saga确保要么都成功要么补偿
查看范例答案
处理最终一致性的分布式事务通常采用Saga模式,它由一系列本地事务组成,每个事务都有对应的补偿事务。当某个步骤失败时,执行之前步骤的补偿以回滚所有变更。例如,在电商下单流程中,订单服务创建一个待支付订单并发布“订单创建”事件,库存服务监听事件并扣减库存,如果库存不足则发布“库存不足”事件并启动补偿:订单服务取消订单。整个过程中,订单服务与库存服务没有全局锁,数据在事务提交后可能短暂不一致(如订单已创建但库存扣减失败),但最终通过补偿达到一致。实现时可以使用编排(中央协调器)或编舞(事件驱动),常见工具包括消息队列和分布式事务协调框架。
- 为一个包含 10 个以上微服务的系统设计一个 API 网关。它应该包含哪些功能?好回答应覆盖
- 统一入口和路由,根据请求路径或主机分发到不同微服务
- 认证与授权,集中管理API密钥和令牌校验
- 限流与熔断,保护后端服务免受过载
- 请求与响应转换,如协议转换、数据聚合
- 日志、监控和缓存,提升可观测性和性能
查看范例答案
API网关作为微服务系统的统一入口,需要提供以下核心功能:1) 路由和负载均衡,将外部请求转发到正确的服务实例;2) 身份认证和授权,统一校验JWT、API Key等,使用OAuth2或自定义策略;3) 限流和断路器,基于令牌桶或滑动窗口算法,防止雪崩效应;4) 请求/响应转换,如将多个服务响应聚合为一个,或将REST转为gRPC;5) 日志和监控,收集所有请求的延迟、状态码等信息,导出到ELK或Prometheus;6) 缓存,对不经常变化的数据(如产品详情)进行边缘缓存;7) API版本管理,允许不同客户端使用不同版本。常用实现工具有Kong、Spring Cloud Gateway、NGINX Plus等。
- 描述一个你会选择 gRPC 而不是 REST 进行服务间通信的场景。好回答应覆盖
- 微服务间需要高吞吐和低延迟通信
- 服务需要双向流或服务器推送能力
- 接口契约需要强类型且跨语言支持
- 内部通信,客户端不受浏览器限制
查看范例答案
当微服务间通信需要高性能和低延迟时,应优先选择gRPC而非REST。例如,在实时数据管道中,服务间需要高频交换大量结构化数据(如监控指标、传感器数据)。gRPC基于HTTP/2的多路复用和二进制序列化(Protobuf),显著减少网络开销和序列化时间。此外,gRPC原生支持双向流,适合订阅-发布模式或需要持续推送的场景。REST的文本格式(JSON)在复杂查询场景下解析成本高,且没有内置的流支持。但gRPC在浏览器端和公共API中兼容性较差,因此仅推荐用于内部服务间通信。如果场景包含公开API或前端直接调用,则REST更合适。
- 如何在微服务间实现分布式追踪?你会使用哪些工具?好回答应覆盖
- 为每个请求生成全局唯一的Trace ID,并跨服务传递
- 每个服务内记录Span,包含开始时间、持续时间、标签等信息
- 收集所有Span并发送到后端存储,如Jaeger或Zipkin
- 通过Trace ID关联所有Span,构建完整调用链
查看范例答案
分布式追踪通过在请求入口生成Trace ID,并在服务间通过HTTP头或消息元数据传递。每个服务创建一个Span记录该请求的处理过程(如数据库查询、外部调用),并附加标签和日志。所有Span被收集并发送到追踪系统(如Jaeger、Zipkin,或基于OpenTelemetry的实现)进行存储和聚合。后端通过Trace ID将Span串联成树状调用图,展示每个阶段的耗时。实现时需要在网关或首个服务注入Trace ID,并在所有服务中传播,同时考虑采样策略以减少性能开销。推荐使用OpenTelemetry库,它支持多语言且可扩展,配合Jaeger作为可视化工具。
- 编写一个健康检查端点,报告下游依赖项(例如数据库、缓存、其他服务)的状态。好回答应覆盖
- 检查与每个下游依赖的连接快照,如数据库、缓存、消息队列
- 返回整体状态(UP/DOWN)及每个依赖的状态和错误信息
- 设置超时和断路器,防止健康检查本身影响服务可用性
- 端点通常位于 /health 或 /actuator/health
查看范例答案
以下是一个基于Python Flask的健康检查端点实现,它依次检查数据库(MySQL)、缓存(Redis)和一个下游服务的可达性,并汇总状态。返回200表示全部正常,503表示部分依赖不可用。注意:健康检查应使用短超时,并在检查失败时记录日志但避免抛出异常。生产环境中可使用Spring Boot Actuator或类似库自动完成。
参考代码python from flask import Flask, jsonify import pymysql import redis import requests app = Flask(__name__) def check_mysql(): try: conn = pymysql.connect(host='localhost', user='root', password='pass', db='test', connect_timeout=2) conn.close() return {'status': 'UP'} except Exception as e: return {'status': 'DOWN', 'error': str(e)} def check_redis(): try: r = redis.Redis(host='localhost', port=6379, socket_connect_timeout=2) r.ping() return {'status': 'UP'} except Exception as e: return {'status': 'DOWN', 'error': str(e)} def check_downstream_service(): try: resp = requests.get('http://other-service/api/health', timeout=2) if resp.status_code == 200: return {'status': 'UP'} else: return {'status': 'DOWN', 'error': f'HTTP {resp.status_code}'} except Exception as e: return {'status': 'DOWN', 'error': str(e)} @app.route('/health', methods=['GET']) def health(): deps = { 'mysql': check_mysql(), 'redis': check_redis(), 'other_service': check_downstream_service() } overall = 'UP' if all(d['status'] == 'UP' for d in deps.values()) else 'DOWN' return jsonify({'status': overall, 'dependencies': deps}), 200 if overall == 'UP' else 503 if __name__ == '__main__': app.run(host='0.0.0.0', port=8080)
如何准备
- 掌握分布式系统基础:CAP 定理、一致性模型和容错性。
- 使用领域驱动设计(实体、聚合、限界上下文)练习服务分解。
- 理解最终一致性以及如何实现 Saga 模式(例如使用补偿事务)。
- 准备讨论权衡:编排与编舞、同步与异步、有状态与无状态。
- 为系统设计问题做好准备:练习在白板上绘制微服务架构,重点关注通信、数据和可观测性。
常见问题
微服务面试中最重要的概念是什么?
关键概念包括服务分解、服务间通信(REST、gRPC、消息传递)、数据一致性(Saga、最终一致性)和可观测性(日志、指标、追踪)。
微服务面试中需要了解 Kubernetes 吗?
是的,Kubernetes 常用于编排容器。你应该了解 Pod、服务、部署以及如何在集群中管理微服务。
微服务面试与一般系统设计面试有何不同?
微服务面试特别关注分布式架构挑战,如服务边界、数据分布和网络弹性,而一般系统设计也可能涵盖单体应用。
微服务面试中可能会遇到哪些编码问题?
你可能会被要求实现断路器、重试机制或简单的服务健康端点。此外,你可能需要设计 API 或为服务间通信编写代码。
如果我没有在生产环境中使用过微服务,如何展示相关经验?
使用微服务模式构建一个小项目(例如使用 Docker、Node.js/Spring Boot 和消息队列)。讨论权衡和经验教训。理论知识结合个人项目可以很有说服力。
练习 Microservices 题目,即时获取 AI 反馈
上传简历,获得个性化模拟面试,并了解需要改进的地方——免费开始。