微服务架构面试题
微服务架构面试题
简介
微服务架构是现代大型分布式系统的主流架构风格,将单体应用拆分为一组小型、独立部署的服务。本篇涵盖微服务架构的核心概念和实践经验,包括服务拆分策略、服务间通信、服务治理、容错处理等高频面试话题。
特点
面试题目
1. 什么是微服务架构?与单体架构相比有什么优缺点?
答: 微服务架构是一种将应用程序构建为一组小型、独立部署的服务的架构风格,每个服务运行在自己的进程中,通过轻量级机制(通常是 HTTP API)进行通信。
单体架构的优点: 开发简单、部署方便、调试容易、事务管理简单。缺点: 随着代码量增长,构建和部署变慢、技术栈锁定、扩展困难、团队协作冲突多。
微服务架构的优点: 独立部署、技术栈灵活、按需扩展、故障隔离、团队自治。缺点: 分布式系统复杂度(网络延迟、数据一致性)、运维成本高、调试和追踪困难、服务间通信开销。
// 单体架构示例 - 所有功能在一个项目中
public class MonolithicController : ControllerBase
{
private readonly ProductDbContext _db;
private readonly EmailSender _email;
[HttpPost("orders")]
public async Task<IActionResult> CreateOrder(CreateOrderDto dto)
{
// 订单、库存、支付、通知全部在一起
var product = await _db.Products.FindAsync(dto.ProductId);
product.Stock -= dto.Quantity;
var order = new Order { Total = product.Price * dto.Quantity };
_db.Orders.Add(order);
await _db.SaveChangesAsync();
await _email.SendOrderConfirmation(dto.Email, order.Id);
return Ok(order);
}
}
// 微服务架构 - 订单服务只负责订单逻辑
[ApiController]
[Route("api/orders")]
public class OrdersController : ControllerBase
{
private readonly IOrderRepository _orders;
private readonly IInventoryClient _inventory;
private readonly IPaymentClient _payment;
private readonly IMessageBus _bus;
[HttpPost]
public async Task<ActionResult<OrderDto>> Create(CreateOrderDto dto)
{
// 1. 检查库存(调用库存服务)
var stock = await _inventory.CheckAsync(dto.ProductId, dto.Quantity);
if (!stock.Available) return BadRequest("库存不足");
// 2. 创建订单
var order = await _orders.CreateAsync(dto);
// 3. 发布事件(异步处理支付和通知)
await _bus.PublishAsync(new OrderCreatedEvent(order.Id, order.Total));
return CreatedAtAction(nameof(Get), new { id = order.Id }, order);
}
}2. 如何进行微服务的拆分?有哪些拆分原则?
答: 服务拆分应遵循以下原则:
- 按业务能力拆分: 围绕业务功能组织服务,如订单服务、用户服务、商品服务
- 领域驱动设计(DDD): 使用限界上下文(Bounded Context)确定服务边界
- 单一职责: 每个服务只负责一个业务领域的功能
- 高内聚低耦合: 服务内部功能高度相关,服务之间依赖尽量少
- 数据独立: 每个服务拥有自己的数据库,不共享数据库
// 电商系统的服务拆分示例
// 订单服务 - OrderService
public class OrderService
{
private readonly IOrderRepository _orderRepo;
public async Task<Order> CreateOrderAsync(CreateOrderCommand cmd) { /* ... */ }
public async Task<Order> GetOrderAsync(int id) { /* ... */ }
}
// 商品服务 - ProductService
public class ProductService
{
private readonly IProductRepository _productRepo;
public async Task<Product> GetProductAsync(int id) { /* ... */ }
public async Task UpdateStockAsync(int productId, int quantity) { /* ... */ }
}
// 支付服务 - PaymentService
public class PaymentService
{
private readonly IPaymentGateway _gateway;
public async Task<PaymentResult> ChargeAsync(int orderId, decimal amount) { /* ... */ }
public async Task RefundAsync(int paymentId) { /* ... */ }
}
// 通知服务 - NotificationService
public class NotificationService
{
public async Task SendOrderConfirmationAsync(int orderId, string email) { /* ... */ }
public async Task SendShippingUpdateAsync(int orderId, string trackingNo) { /* ... */ }
}3. 微服务之间有哪些通信方式?各自的适用场景?
答: 主要有两种通信方式:同步通信和异步通信。
| 通信方式 | 技术 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
| 同步-REST | HTTP/HTTPS | 简单查询、CRUD操作 | 简单、通用 | 延迟高、紧耦合 |
| 同步-gRPC | HTTP/2 + Protobuf | 服务间高性能调用 | 高性能、强类型 | 需要IDL定义 |
| 异步-消息队列 | RabbitMQ/Kafka | 事件驱动、解耦通信 | 解耦、削峰 | 一致性复杂 |
| 异步-事件总线 | MassTransit/CAP | 领域事件传播 | 松耦合 | 调试困难 |
// 1. REST 同步通信
public class InventoryClient : IInventoryClient
{
private readonly HttpClient _httpClient;
public async Task<StockInfo> CheckAsync(int productId, int quantity)
{
var response = await _httpClient.GetAsync($"/api/inventory/{productId}?qty={quantity}");
return await response.Content.ReadFromJsonAsync<StockInfo>();
}
}
// 2. gRPC 同步通信
//Proto 文件定义
// syntax = "proto3";
// service PaymentGrpc {
// rpc Charge (ChargeRequest) returns (ChargeResponse);
// }
public class PaymentGrpcClient
{
private readonly PaymentGrpc.PaymentGrpcClient _client;
public async Task<ChargeResponse> ChargeAsync(int orderId, decimal amount)
{
return await _client.ChargeAsync(new ChargeRequest
{
OrderId = orderId,
Amount = (double)amount
});
}
}
// 3. 消息队列异步通信(MassTransit)
public class OrderEventPublisher
{
private readonly IBus _bus;
public async Task PublishOrderCreatedAsync(int orderId, decimal total)
{
await _bus.Publish(new OrderCreatedEvent(orderId, total));
}
}
public class PaymentEventConsumer : IConsumer<OrderCreatedEvent>
{
public async Task Consume(ConsumeContext<OrderCreatedEvent> context)
{
// 异步处理支付逻辑
Console.WriteLine($"处理订单支付: {context.Message.OrderId}");
}
}4. 什么是服务注册与发现?在 .NET 中如何实现?
答: 服务注册与发现是微服务架构中的基础设施,服务启动时将自己的地址注册到注册中心,其他服务通过注册中心查找目标服务的地址。
// 使用 Consul 实现服务注册与发现
// dotnet add package Consul
public class ConsulServiceRegistry
{
private readonly ConsulClient _consul;
private readonly string _serviceId;
public ConsulServiceRegistry(IConfiguration config)
{
_consul = new ConsulClient(c => c.Address = new Uri(config["Consul:Address"]!));
_serviceId = Guid.NewGuid().ToString();
}
// 服务注册
public async Task RegisterAsync(string serviceName, string host, int port)
{
var registration = new AgentServiceRegistration
{
ID = _serviceId,
Name = serviceName,
Address = host,
Port = port,
Check = new AgentServiceCheck
{
HTTP = $"http://{host}:{port}/health",
Interval = TimeSpan.FromSeconds(10),
Timeout = TimeSpan.FromSeconds(5),
DeregisterCriticalServiceAfter = TimeSpan.FromMinutes(1)
}
};
await _consul.Agent.ServiceRegister(registration);
}
// 服务发现
public async Task<string> GetServiceAddressAsync(string serviceName)
{
var services = await _consul.Health.Service(serviceName, tag: null, passingOnly: true);
var service = services.Response.FirstOrDefault();
if (service == null) throw new InvalidOperationException($"未找到服务: {serviceName}");
return $"http://{service.Service.Address}:{service.Service.Port}";
}
// 服务注销
public async Task DeregisterAsync()
{
await _consul.Agent.ServiceDeregister(_serviceId);
}
}5. 如何处理微服务中的数据一致性问题?
答: 微服务中每个服务拥有独立数据库,无法使用传统的数据库事务保证一致性。常用方案有:
- Saga 模式: 通过编排一系列本地事务,每个事务更新一个服务的数据
- 事件溯源(Event Sourcing): 通过事件日志重建状态
- 最终一致性: 通过消息队列和重试机制保证最终数据一致
// Saga 模式 - 订单创建 Saga
public class CreateOrderSaga
{
private readonly IOrderRepository _orders;
private readonly IInventoryClient _inventory;
private readonly IPaymentClient _payment;
private readonly ILogger<CreateOrderSaga> _logger;
public async Task<SagaResult> ExecuteAsync(CreateOrderCommand cmd)
{
var compensationActions = new Stack<Func<Task>>();
try
{
// 步骤1:创建订单(待处理状态)
var order = await _orders.CreateAsync(new Order { Status = "Pending" });
compensationActions.Push(async () => await _orders.DeleteAsync(order.Id));
// 步骤2:预留库存
var reserved = await _inventory.ReserveAsync(cmd.ProductId, cmd.Quantity);
if (!reserved) throw new Exception("库存预留失败");
compensationActions.Push(async () =>
await _inventory.ReleaseAsync(cmd.ProductId, cmd.Quantity));
// 步骤3:处理支付
var payment = await _payment.ChargeAsync(order.Id, order.TotalAmount);
if (!payment.Success) throw new Exception("支付失败");
compensationActions.Push(async () =>
await _payment.RefundAsync(payment.TransactionId));
// 步骤4:确认订单
order.Status = "Confirmed";
await _orders.UpdateAsync(order);
return SagaResult.Success(order.Id);
}
catch (Exception ex)
{
_logger.LogError(ex, "Saga 执行失败,开始补偿");
// 按相反顺序执行补偿操作
while (compensationActions.Count > 0)
{
var compensate = compensationActions.Pop();
try { await compensate(); }
catch (Exception compEx) { _logger.LogError(compEx, "补偿操作失败"); }
}
return SagaResult.Failure(ex.Message);
}
}
}6. 什么是 API 网关?为什么要使用它?
答: API 网关是微服务架构中的统一入口点,负责请求路由、认证授权、限流熔断、协议转换等功能。
// 使用 YARP(Yet Another Reverse Proxy)作为 API 网关
// dotnet add package Yarp.ReverseProxy
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
var app = builder.Build();
app.MapReverseProxy();
// appsettings.json 配置路由规则
// {
// "ReverseProxy": {
// "Routes": {
// "order-route": {
// "ClusterId": "order-cluster",
// "Match": { "Path": "/api/orders/{**catch-all}" }
// },
// "product-route": {
// "ClusterId": "product-cluster",
// "Match": { "Path": "/api/products/{**catch-all}" }
// }
// },
// "Clusters": {
// "order-cluster": {
// "Destinations": {
// "destination1": { "Address": "https://localhost:5001" }
// }
// },
// "product-cluster": {
// "Destinations": {
// "destination1": { "Address": "https://localhost:5002" }
// }
// }
// }
// }
// }7. 如何实现微服务的限流和熔断?
答: 限流控制请求速率防止过载,熔断在服务异常时快速失败防止级联故障。
// 使用 Polly 实现熔断
public class ResilientHttpClient
{
private readonly HttpClient _client;
private readonly IAsyncPolicy<HttpResponseMessage> _policy;
public ResilientHttpClient(HttpClient client)
{
_client = client;
// 熔断策略:连续5次失败后熔断30秒
var circuitBreaker = Policy<HttpResponseMessage>
.Handle<HttpRequestException>()
.OrResult(r => (int)r.StatusCode >= 500)
.CircuitBreakerAsync(
exceptionsAllowedBeforeBreaking: 5,
durationOfBreak: TimeSpan.FromSeconds(30),
onBreak: (ex, duration) =>
Console.WriteLine($"熔断开启,持续 {duration.TotalSeconds}s"),
onReset: () => Console.WriteLine("熔断恢复"));
// 重试策略
var retry = Policy<HttpResponseMessage>
.Handle<HttpRequestException>()
.WaitAndRetryAsync(3,
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
_policy = Policy.WrapAsync(retry, circuitBreaker);
}
public async Task<HttpResponseMessage> GetAsync(string url)
{
return await _policy.ExecuteAsync(() => _client.GetAsync(url));
}
}8. 什么是分布式链路追踪?如何实现?
答: 分布式链路追踪用于跟踪请求在多个微服务间的调用路径,帮助定位性能瓶颈和故障点。常用工具有 Jaeger、Zipkin、Application Insights。
// 使用 OpenTelemetry 实现分布式追踪
// dotnet add package OpenTelemetry.Extensions.Hosting
// dotnet add package OpenTelemetry.Exporter.Jaeger
builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
tracing
.SetResourceBuilder(ResourceBuilder.CreateDefault()
.AddService("OrderService"))
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddJaegerExporter(options =>
{
options.AgentHost = "localhost";
options.AgentPort = 6831;
});
});9. 微服务如何实现配置管理?
答: 微服务配置管理需要集中管理所有服务的配置,支持动态刷新和环境隔离。
// 使用 Azure App Configuration 或 Consul KV
public class ConfigurationService
{
private readonly IConfiguration _configuration;
private readonly ILogger<ConfigurationService> _logger;
public T Get<T>(string key, T defaultValue = default!)
{
var value = _configuration[key];
if (value == null) return defaultValue;
return (T)Convert.ChangeType(value, typeof(T));
}
// 支持配置热更新
public void Watch(string key, Action<string> onChange)
{
ChangeToken.OnChange(
() => _configuration.GetReloadToken(),
() =>
{
var newValue = _configuration[key];
onChange(newValue!);
_logger.LogInformation("配置 {Key} 已更新", key);
});
}
}10. 如何设计微服务的健康检查?
答: 健康检查用于监控服务的可用性,包括服务自身状态、依赖项(数据库、缓存、消息队列)的状态。
// ASP.NET Core 健康检查
builder.Services.AddHealthChecks()
.AddDbContextCheck<OrderDbContext>("database")
.AddRabbitMQ(rabbitConnectionString, name: "rabbitmq")
.AddRedis(redisConnectionString, name: "redis")
.AddUrlGroup(new Uri("https://api.payment.com/health"), "payment-service");
// 自定义健康检查
public class DiskSpaceHealthCheck : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context, CancellationToken ct = default)
{
var drive = new DriveInfo(Path.GetPathRoot(Directory.GetCurrentDirectory())!);
var freeSpaceGB = drive.AvailableFreeSpace / (1024.0 * 1024 * 1024);
if (freeSpaceGB < 1)
return Task.FromResult(HealthCheckResult.Unhealthy($"磁盘空间不足: {freeSpaceGB:F1} GB"));
if (freeSpaceGB < 5)
return Task.FromResult(HealthCheckResult.Degraded($"磁盘空间偏低: {freeSpaceGB:F1} GB"));
return Task.FromResult(HealthCheckResult.Healthy($"磁盘空间正常: {freeSpaceGB:F1} GB"));
}
}
app.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});11-20. 更多微服务面试题简答
11. 微服务如何处理分布式事务? 使用 Saga 模式(编排式或协调式)、TCC(Try-Confirm-Cancel)模式,或使用 Outbox 模式保证消息可靠发送。
12. 什么是 Sidecar 模式? Sidecar 是与主服务一起部署的辅助容器,处理横切关注点如日志、监控、安全,如 Dapr 的 Sidecar。
13. 什么是服务网格(Service Mesh)? 服务网格是基础设施层,处理服务间通信的负载均衡、加密、监控等功能,如 Istio、Linkerd。
14. 微服务如何实现灰度发布? 通过 API 网关路由规则、Feature Flag、金丝雀发布等方式逐步将流量切换到新版本。
15. 如何监控微服务? 使用 Prometheus + Grafana 进行指标监控,ELK/EFK 进行日志聚合,Jaeger/Zipkin 进行链路追踪。
16. 微服务如何处理版本兼容? 使用 API 版本控制(URL路径/Header方式)、向后兼容的契约设计、消费者驱动契约测试。
17. 什么是 CQRS 模式? 命令查询职责分离,将读写操作分离到不同的模型中,读操作优化查询性能,写操作通过领域事件同步到读模型。
18. 微服务中如何处理认证授权? 使用 JWT/OAuth2 进行身份认证,API 网关统一验证 Token,服务间通过内部 Token 或 mTLS 互信。
19. 什么是 BFF(Backend for Frontend)模式? 为不同前端(Web、移动端、小程序)提供定制的后端 API 网关,聚合多个微服务的数据。
20. 微服务如何实现优雅停机? 处理 SIGTERM 信号,停止接收新请求,等待进行中的请求完成(graceful shutdown period),然后从注册中心注销。
优点
缺点
总结
微服务架构面试需要从架构设计、通信机制、数据管理、服务治理、可观测性等多个维度准备。核心是理解分布式系统的基本原理(CAP 定理、最终一致性),掌握常用的解决方案(Saga、熔断、链路追踪),并能够在面试中结合项目经验阐述实际遇到的挑战和解决方案。
这组题真正考什么
- 面试官往往不只是考定义,而是在看你能否把基础概念放回真实 .NET 场景。
- 这类题经常沿着语言基础、框架设计、性能和工程实践往下追问。
- 高分答案通常有三层:结论、原因、项目中的例子。
60 秒答题模板
- 先用一句话给结论。
- 再补关键原理或底层机制。
- 最后说适用边界、常见坑或项目中的使用经验。
容易失分的点
- 只会背术语,不会举例。
- 回答太散,没有结构。
- 忽略版本差异和工程背景。
刷题建议
- 把答案拆成“定义、适用场景、风险点、实战例子”四段来复述。
- 遇到 .NET 基础题时,尽量补一个框架级别的落地场景,而不是只背术语。
- 高频概念题建议自己再追问一层:底层原理、常见坑、性能代价分别是什么。
高频追问
- 如果面试官继续追问底层实现,你能否解释运行机制或源码层面的关键点?
- 如果题目放到 ASP.NET Core、消息队列或数据库场景里,这个结论是否还成立?
- 是否存在版本差异、框架差异或特殊边界条件需要主动说明?
复习重点
- 把每道题的关键词整理成自己的知识树,而不是只背原句。
- 对容易混淆的概念要做横向比较,例如机制差异、适用边界和性能代价。
- 复习时优先补“为什么”,其次才是“怎么用”和“记住什么术语”。
面试作答提醒
- 先给结论,再补原因和例子。
- 回答基础题时不要只说“能用”,最好补一句为什么这样选。
- 如果记不清细节,优先说出适用边界和排查思路。
