微服务架构
难度等级:⭐⭐⭐⭐ 前置知识:Web 后端开发、数据存储 后续衔接:架构设计、DevOps
学习路径
- 入门阶段:理解单体与微服务的区别,掌握服务注册发现
- 进阶阶段:实现服务间通信、熔断限流、配置管理
- 精通阶段:能够设计分布式系统架构,处理分布式事务
一、微服务基础
1.1 什么是微服务
微服务架构(Microservices Architecture)是一种将单一应用程序拆分为一组小型服务的方法,每个服务运行在独立的进程中,服务之间通过轻量级通信机制(通常是 HTTP/REST 或 gRPC)进行协作。每个服务围绕业务能力构建,可以独立部署、独立扩展、独立技术栈。
单体架构的痛点:
- 代码库膨胀:随着业务增长,代码库越来越庞大,编译时间变长,新人上手困难
- 耦合严重:模块间依赖复杂,修改一个功能可能影响其他模块,回归测试成本高
- 扩展困难:只能整体扩展,无法针对瓶颈模块单独扩容,资源利用率低
- 技术栈锁定:整个项目绑定在一种技术栈上,难以引入新技术
- 持续交付受阻:每次发布都需要全量部署,发布频率低,风险高
微服务的优势:
- 独立部署:每个服务可独立发布,缩短交付周期,降低发布风险
- 技术异构:不同服务可根据场景选择最合适的技术栈
- 精准扩展:只扩展有瓶颈的服务,资源利用更高效
- 团队自治:小团队负责单一服务,减少跨团队协调成本
- 故障隔离:单个服务故障不会导致整个系统崩溃
微服务的挑战:
- 分布式系统复杂度:网络延迟、服务发现、容错处理等分布式问题必须面对
- 运维成本上升:服务数量增加,部署、监控、日志聚合复杂度呈指数增长
- 数据一致性:跨服务数据一致性需要引入分布式事务或最终一致性方案
- 测试困难:集成测试和端到端测试需要搭建完整的服务拓扑
- 调试困难:请求跨多个服务,问题定位需要完整的链路追踪
何时选择微服务:
微服务不是银弹。当团队规模较小(<10人)、业务简单、快速验证阶段,单体架构更合适。当系统复杂度上升、团队规模扩大、需要独立扩展和快速迭代时,才考虑向微服务演进。Martin Fowler 提出的”单体优先”(MonolithFirst)策略建议:先从单体开始,随着业务增长逐步拆分。
1.2 服务拆分原则
服务拆分是微服务架构中最关键的决策之一。拆分过粗,退化为分布式单体;拆分过细,带来不必要的网络开销和运维负担。
单一职责原则(SRP):
每个服务应该只有一个变更的理由,围绕一个明确的业务能力(Business Capability)构建。例如,电商系统中的订单服务只负责订单的创建、查询、状态流转,不处理库存扣减或支付逻辑。
领域驱动设计(DDD)指导拆分:
DDD 提供了系统化的拆分方法论:
- 战略设计:通过事件风暴(Event Storming)识别领域事件,划分限界上下文(Bounded Context)
- 限界上下文:每个上下文有独立的领域模型和统一语言(Ubiquitous Language),上下文之间通过防腐层(ACL)隔离
- 上下文映射:明确上下文之间的协作关系(上游/下游、共享内核、开放主机服务等)
拆分步骤:
- 识别业务领域:梳理核心域、支撑域、通用域
- 划定限界上下文:根据业务边界而非技术边界拆分
- 定义服务契约:明确服务间的 API 接口和数据格式
- 设计数据边界:每个服务拥有独立数据库,避免共享表
- 渐进式拆分:从单体中逐步剥离服务,而非一次性重写
常见拆分误区:
- 按技术层拆分:将 Controller、Service、DAO 拆成不同服务,这是错误的,应该按业务领域拆分
- 过度拆分:一个服务只有几十个方法,运维成本远超收益
- 共享数据库:多个服务直连同一数据库,形成隐式耦合
- 分布式单体:服务间强同步调用,一个服务挂导致级联故障
服务粒度参考:
| 服务类型 | 方法数量 | 团队规模 | 数据库 |
|---|---|---|---|
| 过细 | < 50 | 1-2人 | 独立 |
| 合理 | 50-200 | 2-5人(Two-Pizza Team) | 独立 |
| 过粗 | > 200 | 5-10人 | 可能共享 |
1.3 服务通信
微服务之间的通信是架构的核心环节,主要分为同步和异步两种模式。
同步通信:
同步通信中,调用方等待被调用方返回结果,行为类似本地函数调用。
- REST/HTTP:最通用的方式,基于 JSON 数据格式,可读性好,调试方便。适合面向外部系统或前端的 API。缺点是文本协议开销较大,性能不如二进制协议。
// .NET HttpClient 调用示例
var client = _httpClientFactory.CreateClient("OrderService");
var response = await client.GetAsync($"/api/orders/{orderId}");
var order = await response.Content.ReadFromJsonAsync<Order>();
// Spring Boot RestTemplate 调用示例
ResponseEntity<Order> response = restTemplate.getForEntity(
"http://order-service/api/orders/{id}",
Order.class, orderId
);
- gRPC:基于 Protocol Buffers 的高性能 RPC 框架,支持双向流、服务端流、客户端流。适合服务间内部通信,性能比 REST 高 5-10 倍。
// gRPC 服务定义
service OrderService {
rpc GetOrder (OrderRequest) returns (OrderResponse);
rpc StreamOrders (StreamRequest) returns (stream Order);
}
异步通信:
异步通信中,调用方发送消息后不等待立即返回,通过消息中间件解耦服务。
- 消息队列:RabbitMQ、Kafka、RocketMQ 等。适合事件驱动架构,削峰填谷,最终一致性场景。
- 发布/订阅:生产者发布事件,多个消费者独立处理,实现扇出(Fan-out)。
// CAP 发布事件示例
await _capPublisher.PublishAsync("order.created", new OrderCreatedEvent {
OrderId = orderId,
Amount = amount
});
通信方式选型:
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 需要即时响应 | REST/gRPC | 同步获取结果 |
| 高性能内部调用 | gRPC | 二进制协议,低延迟 |
| 事件通知 | 消息队列 | 解耦,异步处理 |
| 削峰填谷 | Kafka/RocketMQ | 高吞吐,持久化 |
| 多消费者 | 发布/订阅 | 扇出,独立处理 |
通信最佳实践:
- 设置合理的超时时间,避免级联等待
- 实现重试机制,处理网络瞬态故障
- 使用熔断器防止雪崩效应
- 记录请求 TraceId,便于链路追踪
- 避免循环依赖,保持调用链单向
二、.NET 微服务
2.1 Ocelot API Gateway
Ocelot 是 .NET 生态中最流行的开源 API 网关,负责将客户端请求路由到后端微服务,并提供限流、认证、日志聚合等横切关注点的统一处理。
核心功能:
- 路由转发:基于 URL 路径、Host、Header 等条件将请求转发到下游服务
- 请求聚合:将多个下游请求合并为一个响应返回给客户端
- 认证授权:集成 IdentityServer4、JWT 等认证方案,在网关层统一鉴权
- 限流:基于令牌桶或固定窗口算法限制请求频率
- 缓存:对幂等 GET 请求进行响应缓存
- 负载均衡:支持轮询、最少连接、随机等策略
- 服务发现:集成 Consul 实现动态服务发现
配置示例:
{
"Routes": [
{
"DownstreamPathTemplate": "/api/orders/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{ "Host": "localhost", "Port": 5001 }
],
"UpstreamPathTemplate": "/orders/{everything}",
"UpstreamHttpMethod": [ "GET", "POST" ],
"RateLimitOptions": {
"ClientWhitelist": [],
"EnableRateLimiting": true,
"Period": "1s",
"PeriodTimespan": 1,
"Limit": 10
},
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": [ "orders.read" ]
}
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:5000",
"ServiceDiscoveryProvider": {
"Host": "localhost",
"Port": 8500,
"Type": "Consul"
}
}
}
Program.cs 注册:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOcelot();
var app = builder.Build();
await app.UseOcelot();
app.Run();
网关部署模式:
- 单网关:适合开发环境,存在单点故障风险
- 多网关 + 负载均衡:生产环境推荐,通过 Nginx 或云负载均衡器分发到多个 Ocelot 实例
- BFF 模式(Backend for Frontend):为不同客户端(Web、Mobile)提供不同的网关,裁剪不必要的字段
Ocelot 的局限:
Ocelot 基于 .NET,性能在高并发场景下不如 Envoy、Nginx 等原生网关。对于超高吞吐场景,可考虑 YARP(Yet Another Reverse Proxy),这是微软官方推出的新一代反向代理,性能更优且与 .NET 生态深度集成。
2.2 Polly 弹性处理
Polly 是 .NET 的弹性与瞬态故障处理库,提供重试、熔断、超时、舱壁隔离、缓存等策略,是构建健壮微服务的基础组件。
重试策略(Retry):
处理瞬态故障(网络抖动、服务短暂不可用),通过重试提高成功率。
var retryPolicy = Policy
.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)),
onRetry: (outcome, timespan, attempt, context) =>
{
_logger.LogWarning("第 {Attempt} 次重试,等待 {Timespan}ms",
attempt, timespan.TotalMilliseconds);
});
var response = await retryPolicy.ExecuteAsync(() => _httpClient.GetAsync(url));
熔断策略(Circuit Breaker):
当失败率达到阈值时,快速失败(Fail Fast),避免持续调用注定失败的服务,给下游服务恢复时间。
var circuitBreakerPolicy = Policy
.Handle<Exception>()
.CircuitBreakerAsync(
exceptionsAllowedBeforeBreaking: 5, // 连续 5 次失败后熔断
durationOfBreak: TimeSpan.FromSeconds(30), // 熔断 30 秒
onBreak: (ex, duration) => _logger.LogWarning("熔断开启 {Duration}s", duration.TotalSeconds),
onReset: () => _logger.LogInformation("熔断关闭,恢复正常"),
onHalfOpen: () => _logger.LogInformation("熔断半开,尝试放行一次")
);
熔断器三态:
- Closed(关闭):正常状态,请求正常通过
- Open(开启):失败率超阈值,请求直接拒绝,快速失败
- Half-Open(半开):熔断时间过后,放行一次试探请求,成功则关闭熔断,失败则重新开启
超时策略(Timeout):
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(5),
TimeoutStrategy.Pessimistic
);
舱壁隔离(Bulkhead):
限制并发调用数量,防止某个服务的故障耗尽所有线程池资源。
var bulkheadPolicy = Policy.BulkheadAsync<HttpResponseMessage>(
maxParallelization: 10, // 最大并发 10 个
maxQueuingActions: 5, // 排队队列长度 5
onBulkheadRejected: async context =>
{
_logger.LogError("舱壁已满,请求被拒绝");
});
策略包装(PolicyWrap):
将多个策略组合使用,形成完整的弹性处理链。
var policyWrap = Policy.WrapAsync(
bulkheadPolicy, // 最外层:舱壁隔离
circuitBreakerPolicy, // 第二层:熔断
retryPolicy, // 第三层:重试
timeoutPolicy // 最内层:超时
);
var result = await policyWrap.ExecuteAsync(async () =>
await _httpClient.GetAsync(url));
与 HttpClientFactory 集成:
builder.Services.AddHttpClient("OrderService")
.AddPolicyHandler(retryPolicy)
.AddPolicyHandler(circuitBreakerPolicy)
.AddPolicyHandler(timeoutPolicy);
2.3 服务注册发现
服务注册发现是微服务架构的基础设施,解决”服务在哪里”的问题。服务启动时向注册中心注册自己的地址,调用方通过注册中心查询目标服务的地址列表。
Consul 集成:
Consul 是 HashiCorp 推出的服务网格解决方案,提供服务注册发现、健康检查、KV 存储、多数据中心等功能。
// 服务注册
var consulClient = new ConsulClient(config => config.Address = new Uri("http://localhost:8500"));
var registration = new AgentServiceRegistration
{
ID = $"order-service-{Guid.NewGuid():N}",
Name = "order-service",
Address = "localhost",
Port = 5001,
Tags = new[] { "v1", "production" },
Check = new AgentServiceCheck
{
HTTP = "http://localhost:5001/health",
Interval = TimeSpan.FromSeconds(10),
Timeout = TimeSpan.FromSeconds(5),
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(30)
}
};
await consulClient.Agent.ServiceRegister(registration);
Nacos 集成:
Nacos 是阿里巴巴开源的服务发现与配置管理平台,在 .NET 中可通过 nacos-sdk-csharp 集成。
builder.Services.AddNacosAspNet(c =>
{
c.ServerAddresses = new[] { "http://localhost:8848" };
c.Namespace = "production";
c.ServiceName = "order-service";
c.GroupName = "DEFAULT_GROUP";
c.Weight = 1.0;
});
注册中心对比:
| 特性 | Consul | Nacos | Eureka |
|---|---|---|---|
| CAP 理论 | CP(强一致) | AP/CP 可切换 | AP(高可用) |
| 健康检查 | HTTP/TCP/Script | HTTP/TCP | 心跳 |
| 配置中心 | KV 存储 | 内置支持 | 不支持 |
| 多数据中心 | 原生支持 | 支持 | 不支持 |
| 语言生态 | 多语言 | Java/.NET | Java |
| 运维复杂度 | 中等 | 较低 | 低 |
服务发现的两种模式:
- 客户端发现:客户端直接查询注册中心获取服务地址,自行负载均衡。代表:Eureka + Ribbon。优点是简单直接,缺点是客户端耦合注册中心逻辑。
- 服务端发现:通过负载均衡器(如 API 网关)查询注册中心,客户端无需感知。代表:Kubernetes Service + kube-proxy。优点是客户端无感知,缺点是增加了一层组件。
服务下线与优雅关闭:
服务关闭时应主动从注册中心注销,避免流量继续路由到已下线实例。
// 优雅关闭
app.Lifetime.ApplicationStopping.Register(async () =>
{
await consulClient.Agent.ServiceDeregister(registration.ID);
_logger.LogInformation("服务已从 Consul 注销");
});
2.4 CAP 事件总线
CAP 是 .NET 平台下的分布式事务解决方案,基于本地消息表模式实现服务间的最终一致性,同时支持事件总线的发布/订阅功能。
本地消息表模式:
本地消息表是保证业务操作与消息发送原子性的经典方案。核心思路:将消息发送与业务数据保存在同一个本地事务中,然后通过后台任务轮询发送未确认的消息。
// 订单创建时使用 CAP
[Route("api/orders")]
public class OrderController : ControllerBase
{
private readonly ICapPublisher _capPublisher;
private readonly AppDbContext _dbContext;
[HttpPost]
public async Task<IActionResult> CreateOrder([FromBody] CreateOrderRequest request)
{
using var transaction = _dbContext.Database.BeginTransaction(_capPublisher, autoCommit: false);
try
{
// 1. 创建订单(本地事务)
var order = new Order { /* ... */ };
_dbContext.Orders.Add(order);
await _dbContext.SaveChangesAsync();
// 2. 发布事件(与订单在同一事务中)
await _capPublisher.PublishAsync("order.created", new OrderCreatedEvent
{
OrderId = order.Id,
UserId = order.UserId,
Amount = order.TotalAmount
});
// 3. 提交事务(订单和消息同时提交)
await transaction.CommitAsync();
return Ok(order.Id);
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
}
消息订阅:
public class OrderEventHandler : ICapSubscribe
{
private readonly InventoryService _inventoryService;
[CapSubscribe("order.created")]
public async Task HandleOrderCreated(OrderCreatedEvent @event)
{
// 扣减库存
await _inventoryService.DeductStock(@event.OrderId, @event.Items);
}
}
消息可靠性保证:
- 消息不丢失:消息先写入本地数据库,与业务操作同事务提交,后台任务保证最终发送
- 消息不重复消费:通过幂等性设计(如唯一订单号)保证重复消费的正确性
- 消息顺序性:CAP 默认不保证顺序,如需顺序性可使用同一 Partition Key
CAP 支持的消息中间件:
- RabbitMQ
- Kafka
- Azure Service Bus
- Amazon SQS
- In-Memory(开发测试用)
CAP Dashboard:
CAP 内置 Web Dashboard,可查看消息发布/订阅状态、重试失败消息、手动重发消息。
builder.Services.AddCap(x =>
{
x.UseEntityFramework<AppDbContext>();
x.UseRabbitMQ("localhost");
x.UseDashboard(); // 启用 Dashboard,默认路径 /cap
});
2.5 Dapr 分布式运行时
Dapr(Distributed Application Runtime)是微软开源的可移植、事件驱动的运行时,通过 Sidecar 模式为应用提供分布式构建块,使开发者专注于业务逻辑而非分布式系统复杂性。
核心构建块:
- 服务调用(Service Invocation):带重试和熔断的服务间调用
- 状态管理(State Management):可插拔的状态存储(Redis、CosmosDB 等)
- 发布/订阅(Pub/Sub):事件驱动的消息传递
- 输入/输出绑定(Bindings):与外部系统(数据库、队列、存储)集成
- Actor 模型:虚拟 Actor 编程模型
- 密钥管理(Secrets):密钥存储和轮转
- 可观测性(Observability):分布式追踪、指标、日志
Sidecar 模式:
Dapr 通过 Sidecar 模式运行,每个应用实例旁边运行一个 Dapr Sidecar 进程,应用通过 HTTP 或 gRPC 与 Sidecar 通信,Sidecar 负责处理所有分布式系统复杂性。
┌─────────────┐ HTTP/gRPC ┌──────────────┐
│ 应用程序 │ ◄──────────────► │ Dapr Sidecar │
│ (你的代码) │ 本地调用 │ (基础设施) │
└─────────────┘ └──────────────┘
│
┌────────▼────────┐
│ 外部基础设施 │
│ Redis/K8s/MQ │
└─────────────────┘
服务调用示例:
// 使用 Dapr SDK 调用其他服务
var daprClient = new DaprClientBuilder().Build();
// 同步调用
var order = await daprClient.InvokeMethodAsync<Order>(
HttpMethod.Get,
"order-service",
$"api/orders/{orderId}");
状态管理示例:
// 保存状态
await daprClient.SaveStateAsync("redis-store", "order-123", order);
// 读取状态
var order = await daprClient.GetStateAsync<Order>("redis-store", "order-123");
// 带 ETag 的乐观并发控制
var (state, etag) = await daprClient.GetStateAndETagAsync<Order>("redis-store", "order-123");
await daprClient.TrySaveStateAsync("redis-store", "order-123", order, etag);
Dapr 的优势:
- 语言无关:任何语言的应用都可以通过 Sidecar 使用 Dapr 能力
- 可插拔组件:状态存储、消息中间件等组件可自由替换
- 降低复杂度:开发者无需直接处理重试、熔断、服务发现等逻辑
- 云原生友好:与 Kubernetes 深度集成,适合容器化部署
Dapr 的局限:
- Sidecar 增加资源开销(每个实例额外占用约 50MB 内存)
- 增加了一层网络跳转(应用与 Sidecar 之间的本地通信)
- 学习曲线较陡,概念较多
- 社区生态不如 Spring Cloud 成熟
三、Spring Cloud 微服务
3.1 Spring Cloud Gateway
Spring Cloud Gateway 是 Spring 官方推出的基于 Spring Boot 2.x、Spring WebFlux 和 Project Reactor 构建的响应式 API 网关,取代了已停止维护的 Zuul 1.x。
核心概念:
- 路由(Route):网关的基本构建块,由 ID、目标 URI、断言集合和过滤器集合组成
- 断言(Predicate):匹配 HTTP 请求的条件(路径、方法、Header、时间等),满足条件则路由到目标服务
- 过滤器(Filter):在请求前后修改请求/响应,分为 GatewayFilter(单个路由)和 GlobalFilter(全局)
路由配置示例:
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service # lb 表示负载均衡
predicates:
- Path=/api/orders/**
- Method=GET,POST
- Header=X-Request-Source, (web|mobile)
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
- name: CircuitBreaker
args:
name: orderCircuitBreaker
fallbackUri: forward:/fallback/order
自定义全局过滤器:
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if (StringUtils.isBlank(token) || !token.startsWith("Bearer ")) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 验证 JWT 并传递用户信息给下游
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -100; // 值越小优先级越高
}
}
动态路由:
配合 Nacos 或 Consul 实现路由配置的动态刷新,无需重启网关。
@RestController
@RequestMapping("/routes")
public class DynamicRouteController {
private final RouteDefinitionWriter routeDefinitionWriter;
private final ApplicationEventPublisher publisher;
@PostMapping("/add")
public String addRoute(@RequestBody RouteDefinition definition) {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
publisher.publishEvent(new RefreshRoutesEvent(this));
return "Route added successfully";
}
}
网关高可用:
生产环境通常部署多个网关实例,前端通过 Nginx 或云负载均衡器分发流量。网关本身无状态,可水平扩展。
3.2 注册中心
注册中心是微服务架构的”电话簿”,存储服务实例的网络地址信息,供服务发现使用。
Nacos:
Nacos 是阿里巴巴开源的动态服务发现、配置管理和服务管理平台,目前已成为 Spring Cloud Alibaba 生态的核心组件。
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: production
group: DEFAULT_GROUP
metadata:
version: 1.0.0
region: cn-shanghai
Nacos 的核心优势:
- 同时支持服务发现和配置管理,一个组件解决两个问题
- 支持 AP(高可用)和 CP(强一致)模式切换
- 内置健康检查,支持 HTTP/TCP/MySQL/自定义检查
- 提供可视化的管理控制台
- 支持权重路由、保护阈值、优雅上下线
Eureka:
Eureka 是 Netflix 开源的服务注册发现组件,Spring Cloud Netflix 的核心部分。
# Eureka Server 配置
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
# Eureka Client 配置
spring:
application:
name: order-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
Eureka 的设计哲学是 AP 优先(可用性 > 一致性):
- 采用 Peer-to-Peer 复制,各节点平等
- 网络分区时,各分区独立可用,允许数据不一致
- 自我保护机制:短时间内大量服务下线时,停止驱逐实例
Eureka 2.x 已停止开源维护,新项目不建议使用。
Consul:
Consul 在 Java 生态中通过 Spring Cloud Consul 集成,特性与 .NET 生态类似。
注册中心选型对比:
| 维度 | Nacos | Eureka | Consul |
|---|---|---|---|
| 一致性协议 | Raft(CP)/ Distro(AP) | 最终一致(AP) | Raft(CP) |
| 健康检查 | TCP/HTTP/MySQL/Script | 心跳 | TCP/HTTP/Script/gRPC |
| 配置管理 | 内置支持 | 不支持 | KV 存储 |
| 多数据中心 | 支持 | 不支持 | 原生支持 |
| 社区活跃度 | 高(阿里维护) | 低(已停止维护) | 高(HashiCorp 维护) |
| 部署运维 | 简单 | 简单 | 中等 |
| 适用场景 | Spring Cloud 项目 | 遗留系统 | 多语言/多云环境 |
服务下线与优雅关闭:
Spring Cloud 2020+ 支持 Actuator 优雅关闭端点,服务关闭时先注销注册信息,等待已有请求完成后再停止。
management:
endpoint:
shutdown:
enabled: true
endpoints:
web:
exposure:
include: shutdown
3.3 OpenFeign 服务调用
OpenFeign 是声明式 HTTP 客户端,通过接口注解定义服务调用,底层自动处理序列化、负载均衡、重试等逻辑。
基本使用:
// 定义 Feign 客户端接口
@FeignClient(name = "order-service", path = "/api/orders")
public interface OrderServiceClient {
@GetMapping("/{orderId}")
Order getOrder(@PathVariable("orderId") Long orderId);
@PostMapping
Order createOrder(@RequestBody CreateOrderRequest request);
@GetMapping("/user/{userId}")
List<Order> getOrdersByUser(@PathVariable("userId") Long userId);
}
// 使用 Feign 客户端
@Service
public class UserService {
@Autowired
private OrderServiceClient orderClient;
public UserDTO getUserWithOrders(Long userId) {
User user = userRepository.findById(userId).orElseThrow();
List<Order> orders = orderClient.getOrdersByUser(userId);
return new UserDTO(user, orders);
}
}
负载均衡:
OpenFeign 默认集成 Spring Cloud LoadBalancer(替代了已停止维护的 Ribbon)。
spring:
cloud:
loadbalancer:
retry:
enabled: true # 启用重试
超时与重试配置:
spring:
cloud:
openfeign:
client:
config:
order-service: # 针对特定服务
connect-timeout: 3000
read-timeout: 5000
logger-level: basic
default: # 全局默认
connect-timeout: 2000
read-timeout: 3000
自定义配置类:
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor authRequestInterceptor() {
return template -> {
// 在每个请求中添加认证 Header
String token = SecurityContextHolder.getToken();
template.header("Authorization", "Bearer " + token);
};
}
@Bean
public Retryer feignRetryer() {
return new Retryer.Default(100, 1000, 3); // 初始间隔、最大间隔、最大次数
}
}
Feign + Sentinel 集成:
通过 fallback 实现服务降级。
@FeignClient(
name = "order-service",
path = "/api/orders",
fallbackFactory = OrderServiceFallbackFactory.class
)
public interface OrderServiceClient {
@GetMapping("/{orderId}")
Order getOrder(@PathVariable("orderId") Long orderId);
}
@Component
public class OrderServiceFallbackFactory implements FallbackFactory<OrderServiceClient> {
@Override
public OrderServiceClient create(Throwable cause) {
return new OrderServiceClient() {
@Override
public Order getOrder(Long orderId) {
// 降级逻辑:返回缓存或默认值
return Order.empty(orderId);
}
};
}
}
OpenFeign 最佳实践:
- 为每个下游服务定义独立的 Feign 客户端,不要共用
- 配置合理的超时时间,避免无限等待
- 结合 Sentinel 或 Resilience4j 实现熔断降级
- 使用 RequestInterceptor 统一传递 TraceId、认证信息
- 避免在 Feign 调用中传递大对象,考虑分页或字段裁剪
3.4 Sentinel 限流熔断
Sentinel 是阿里巴巴开源的流量治理组件,提供流量控制、熔断降级、系统自适应保护等功能,是 Hystrix 的替代方案。
流量控制:
基于 QPS 或并发线程数限制流量,支持直接拒绝或 Warm Up(预热)模式。
// 定义资源
@SentinelResource(value = "getOrder", blockHandler = "handleBlock")
public Order getOrder(Long orderId) {
return orderRepository.findById(orderId).orElse(null);
}
// 流控处理
public Order handleBlock(Long orderId, BlockException ex) {
log.warn("请求被限流: {}", ex.getClass().getSimpleName());
return Order.empty(orderId);
}
流控规则配置:
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule("getOrder");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // 按 QPS 限流
rule.setCount(100); // 阈值 100 QPS
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER); // 匀速排队
rule.setMaxQueueingTimeMs(500); // 最大排队时间 500ms
rules.add(rule);
FlowRuleManager.loadRules(rules);
熔断降级:
基于异常比例、异常数或慢调用比例触发熔断。
// 慢调用比例熔断
DegradeRule rule = new DegradeRule("getOrder")
.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())
.setCount(0.5) // 慢调用比例超过 50%
.setTimeWindow(10) // 熔断时长 10 秒
.setSlowRatioThreshold(3); // 响应时间超过 3 秒视为慢调用
系统自适应保护:
根据系统负载(CPU 使用率、系统 Load、入口 QPS 等)自动调整流量,防止系统被打垮。
SystemRule rule = new SystemRule();
rule.setHighestSystemLoad(10.0); // 系统 Load 超过 10 时触发保护
rule.setHighestCpuUsage(0.8); // CPU 使用率超过 80%
rule.setQps(5000); // 总入口 QPS 限制
SystemRuleManager.loadRules(Collections.singletonList(rule));
Sentinel Dashboard:
Sentinel 提供可视化的管理控制台,支持实时监控、规则配置、簇点链路查看。
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080 # Dashboard 地址
port: 8719 # 与 Dashboard 通信的端口
eager: true # 应用启动时立即连接 Dashboard
Sentinel vs Hystrix:
| 特性 | Sentinel | Hystrix |
|---|---|---|
| 隔离策略 | 信号量(轻量) | 线程池/信号量 |
| 熔断策略 | 慢调用/异常比例/异常数 | 异常比例 |
| 实时监控 | 支持(Dashboard) | 支持(Hystrix Dashboard) |
| 规则配置 | 动态配置,实时生效 | 静态配置为主 |
| 系统保护 | 自适应保护 | 不支持 |
| 热点限流 | 支持(参数级) | 不支持 |
3.5 Spring Cloud Stream
Spring Cloud Stream 是构建消息驱动微服务的框架,通过统一的编程模型屏蔽底层消息中间件的差异。
核心概念:
- 绑定器(Binder):消息中间件的抽象层,支持 RabbitMQ、Kafka 等
- 输入/输出通道(Input/Output):定义消息的接收和发送端点
- 消息分区(Partitioning):确保具有相同特征的消息路由到同一消费者实例
基本使用:
@SpringBootApplication
@EnableBinding({Processor.class})
public class StreamApplication {
public static void main(String[] args) {
SpringApplication.run(StreamApplication.class, args);
}
@StreamListener(Processor.INPUT)
public void handleMessage(String message) {
log.info("Received: {}", message);
}
}
spring:
cloud:
stream:
bindings:
input:
destination: order-events
group: order-service-group
output:
destination: order-events
binders:
defaultRabbit:
type: rabbit
environment:
spring:
rabbitmq:
host: localhost
port: 5672
函数式编程模型(推荐):
Spring Cloud Stream 3.x 推荐使用函数式编程模型。
@Bean
public Function<OrderEvent, OrderResult> processOrder() {
return event -> {
log.info("Processing order: {}", event.getOrderId());
return new OrderResult(event.getOrderId(), "SUCCESS");
};
}
消息分区配置:
spring:
cloud:
stream:
bindings:
input:
destination: orders
group: order-service
consumer:
partitioned: true
instanceCount: 3
instanceIndex: 0
producer:
partition-key-expression: headers.userId
partition-count: 3
3.6 配置中心
微服务架构中,配置管理是核心需求。Nacos Config 提供集中化、动态刷新的配置管理。
基本集成:
# bootstrap.yml
spring:
application:
name: order-service
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
namespace: production
group: DEFAULT_GROUP
动态刷新:
使用 @RefreshScope 注解使 Bean 支持配置刷新。
@RestController
@RefreshScope
public class ConfigController {
@Value("${order.timeout:5000}")
private Integer orderTimeout;
@GetMapping("/timeout")
public Integer getTimeout() {
return orderTimeout;
}
}
配置共享:
Nacos 支持共享配置,多个服务可以共用同一份配置。
spring:
cloud:
nacos:
config:
shared-configs:
- data-id: common-config.yaml
group: SHARED_GROUP
refresh: true
配置中心最佳实践:
- 敏感信息(密码、密钥)使用加密存储
- 按环境(dev/test/prod)划分不同 Namespace
- 按业务模块划分 Group
- 配置变更需有审计记录
3.7 链路追踪
分布式系统中,一个请求可能跨越多个服务,链路追踪帮助定位性能瓶颈和故障根因。
Sleuth + Zipkin:
Spring Cloud Sleuth 自动为请求添加 TraceId 和 SpanId,Zipkin 收集并可视化追踪数据。
spring:
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1.0 # 采样率 100%
TraceId 传递:
Sleuth 自动在 HTTP Header 和消息队列中传递 TraceId,确保链路连续。
客户端请求
│ TraceId: abc123
▼
[Gateway] ──SpanId: span1──►
│
▼
[OrderService] ──SpanId: span2──►
│
▼
[PaymentService] ──SpanId: span3
日志集成:
在日志中输出 TraceId,便于问题排查。
logging:
pattern:
level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"
日志输出示例:
INFO [order-service,abc123def456,abc123def789] Processing order 12345
SkyWalking(替代方案):
对于 Java 生态,SkyWalking 是另一个流行的 APM 工具,支持无侵入式探针注入。
# JVM 参数添加 SkyWalking 探针
-javaagent:/path/to/skywalking-agent.jar
-Dskywalking.agent.service_name=order-service
-Dskywalking.collector.backend_service=localhost:11800
四、微服务通用概念
4.1 API 设计风格
RESTful API:
REST(Representational State Transfer)是最主流的 API 设计风格,基于 HTTP 方法和资源路径。
核心原则:
- 资源导向:用名词表示资源(
/users/123),用 HTTP 方法表示操作(GET/POST/PUT/DELETE) - 无状态:每个请求包含完整上下文,服务端不保存会话
- 统一接口:标准化的请求/响应格式
GET /api/orders # 获取订单列表
GET /api/orders/123 # 获取单个订单
POST /api/orders # 创建订单
PUT /api/orders/123 # 更新订单
DELETE /api/orders/123 # 删除订单
GraphQL:
GraphQL 允许客户端精确指定需要的字段,避免过度获取或不足。
适用场景:
- 前端需要灵活组合数据
- 多端(Web、Mobile)需要不同字段组合
- 复杂关联查询
query {
user(id: "123") {
name
orders(status: "ACTIVE") {
id
totalAmount
items {
productName
quantity
}
}
}
}
gRPC:
gRPC 基于 Protocol Buffers,适合高性能内部服务通信。
优势:
- 二进制序列化,体积小、速度快
- 支持双向流、服务端流、客户端流
- 强类型接口,编译时检查
syntax = "proto3";
message OrderRequest {
int64 order_id = 1;
}
message OrderResponse {
int64 order_id = 1;
string status = 2;
double total_amount = 3;
}
service OrderService {
rpc GetOrder (OrderRequest) returns (OrderResponse);
}
选型建议:
| 场景 | 推荐风格 | 原因 |
|---|---|---|
| 面向外部开发者/公众 | RESTful | 通用标准,易理解 |
| 前端灵活数据查询 | GraphQL | 按需获取,减少请求次数 |
| 服务间内部通信 | gRPC | 高性能,强类型 |
| 移动端弱网环境 | GraphQL/gRPC | 减少数据量 |
4.2 分布式事务
微服务拆分后,原本在同一数据库的事务现在跨多个服务,需要新的解决方案。
Saga 模式:
Saga 将长事务拆分为多个本地事务,每个本地事务有对应的补偿操作。
- 编排式(Orchestration):由中央协调器(Orchestrator)按顺序调用各参与方
- 协同式(Choreography):各参与方通过事件触发,无中央协调器
订单创建 Saga 流程:
1. 订单服务:创建订单(补偿:取消订单)
2. 库存服务:扣减库存(补偿:恢复库存)
3. 支付服务:扣款(补偿:退款)
4. 物流服务:创建配送单(补偿:取消配送)
TCC 模式:
TCC(Try-Confirm-Cancel)三阶段提交:
- Try:预留资源
- Confirm:确认执行
- Cancel:取消并释放资源
@LocalTCC
public interface PaymentTCCService {
@TwoPhaseBusinessAction(name = "paymentTry", commitMethod = "paymentConfirm", rollbackMethod = "paymentCancel")
boolean paymentTry(@BusinessActionContextParameter(paramName = "orderId") String orderId,
@BusinessActionContextParameter(paramName = "amount") BigDecimal amount);
boolean paymentConfirm(BusinessActionContext context);
boolean paymentCancel(BusinessActionContext context);
}
Seata 框架:
Seata 是阿里巴巴开源的分布式事务解决方案,支持 AT、TCC、SAGA、XA 四种模式。
- AT 模式:基于 SQL 解析的自动事务,对业务侵入最小
- TCC 模式:需要手动编写 Try/Confirm/Cancel 逻辑
- SAGA 模式:适合长事务场景
- XA 模式:基于数据库 XA 协议
分布式事务选型:
| 模式 | 一致性 | 性能 | 侵入性 | 适用场景 |
|---|---|---|---|---|
| 2PC/XA | 强一致 | 低 | 低 | 同构数据库 |
| TCC | 最终一致 | 高 | 高 | 高性能要求 |
| Saga | 最终一致 | 中 | 中 | 长事务 |
| 本地消息表 | 最终一致 | 中 | 低 | 简单场景 |
4.3 Service Mesh
Service Mesh(服务网格)是基础设施层,处理服务间通信,将流量管理、安全、可观测性从业务代码中剥离。
核心概念:
- Sidecar 代理:每个服务实例旁边部署一个代理容器(如 Envoy)
- 控制面:集中管理配置和策略(如 Istio Pilot)
- 数据面:实际处理流量的代理层
Istio:
Istio 是最成熟的开源 Service Mesh 实现,基于 Envoy 代理。
核心能力:
- 流量管理:流量路由、灰度发布、故障注入
- 安全:服务间 mTLS 认证、细粒度授权
- 可观测性:指标、日志、追踪
# Istio VirtualService 示例:灰度发布
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: order-service
spec:
hosts:
- order-service
http:
- match:
- headers:
x-version:
exact: v2
route:
- destination:
host: order-service
subset: v2
- destination:
host: order-service
subset: v1
weight: 10
Sidecar 模式的优势:
- 语言无关:业务代码无需引入特定 SDK
- 统一治理:流量策略集中管理
- 升级解耦:Mesh 升级不影响业务代码
Service Mesh 的代价:
- 增加网络跳转延迟(约 1-3ms)
- 增加资源消耗(每个 Sidecar 约 50-100MB 内存)
- 运维复杂度上升
五、微服务实战考量
5.1 监控与可观测性
可观测性包含三大支柱:指标(Metrics)、日志(Logs)、链路(Traces)。
指标(Metrics):
- RED 方法:Rate(请求速率)、Errors(错误率)、Duration(响应时间)
- USE 方法:Utilization(资源利用率)、Saturation(饱和度)、Errors(错误)
- 常用工具:Prometheus(采集)+ Grafana(可视化)
# Prometheus 配置示例
scrape_configs:
- job_name: 'spring-boot-app'
metrics_path: '/actuator/prometheus'
scrape_interval: 15s
static_configs:
- targets: ['order-service:8080']
日志(Logs):
- 结构化日志(JSON 格式)便于机器解析
- 集中式日志系统(ELK、Loki)
- 日志与 TraceId 关联
{
"timestamp": "2026-04-24T10:30:00Z",
"level": "INFO",
"service": "order-service",
"traceId": "abc123def456",
"spanId": "abc123def789",
"message": "Order created successfully",
"orderId": "12345"
}
链路(Traces):
- 分布式追踪系统(Jaeger、Zipkin、SkyWalking)
- 自动注入和传递 TraceId
- 可视化服务调用拓扑
告警策略:
- 基于 SLO/SLI 定义告警阈值
- 避免告警疲劳,设置合理的告警规则
- 分级告警(P0/P1/P2)
5.2 灰度发布与蓝绿部署
蓝绿部署:
同时运行两个完全相同的生产环境(蓝色和绿色),一个处理生产流量,另一个部署新版本后切换。
优势:
- 快速回滚(切回旧版本)
- 零停机部署
缺点:
- 资源成本翻倍
- 数据库迁移需要额外处理
金丝雀发布(Canary):
逐步将流量从旧版本迁移到新版本,观察指标后再决定是否继续。
流量切换步骤:
1. 新版本部署 1% 流量
2. 观察 10 分钟,无异常则提升到 10%
3. 继续观察,提升到 50%
4. 最终全量 100%
Istio 灰度发布:
# 按权重分配流量
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: order-service-canary
spec:
hosts:
- order-service
http:
- route:
- destination:
host: order-service
subset: v1
weight: 90
- destination:
host: order-service
subset: v2
weight: 10
基于 Header 的灰度:
# 特定用户群体访问新版本
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: order-service-header
spec:
hosts:
- order-service
http:
- match:
- headers:
x-canary:
exact: "true"
route:
- destination:
host: order-service
subset: v2
- route:
- destination:
host: order-service
subset: v1
六、学习资源推荐
书籍:
- 《Building Microservices》- Sam Newman(微服务权威指南)
- 《Microservices Patterns》- Chris Richardson(模式与实践)
- 《领域驱动设计》- Eric Evans(DDD 经典著作)
- 《释放式架构》- Chris Richardson(微服务架构设计)
在线课程:
- Google Cloud 微服务架构设计(Coursera)
- Spring Cloud 官方文档与示例项目
开源项目参考:
- Spring Cloud 官方示例:https://github.com/spring-cloud-samples
- Dapr 示例:https://github.com/dapr/quickstarts
- Istio Bookinfo 示例:https://istio.io/latest/docs/examples/bookinfo/
实践建议:
- 从单体应用开始,理解痛点后再拆分
- 优先建立自动化测试和 CI/CD 流水线
- 监控和日志在第一天就应该存在
- 小步快跑,逐步验证每个拆分决策