Spring Cloud微服务¶
🎯 学习目标¶
学习时间:3-4周 | 难度:⭐⭐⭐⭐⭐ 高级
完成本章学习后,你将能够: - 掌握Spring Cloud Alibaba生态核心组件(Nacos/Sentinel/Seata/RocketMQ) - 理解服务注册与发现机制,能对比Nacos vs Consul vs Eureka - 熟练使用Spring Cloud Gateway构建微服务网关 - 使用Spring Cloud LoadBalancer实现客户端负载均衡 - 掌握Nacos Config与Apollo动态配置中心 - 使用Sentinel实现熔断降级与热点参数限流 - 理解Seata分布式事务的AT/TCC/Saga模式 - 集成OpenTelemetry/Jaeger/SkyWalking实现全链路追踪 - 使用Spring Cloud Stream整合RocketMQ/Kafka消息驱动 - 掌握微服务Docker Compose编排与K8s部署策略
📖 目录¶
1. Spring Cloud Alibaba生态概览¶
1.1 微服务架构演进¶
从单体应用到微服务架构,是大规模系统的必然演进:
微服务核心原则: - 单一职责:每个服务只做一件事 - 自治性:独立开发、部署、扩展 - 去中心化:数据、治理去中心化 - 弹性设计:容错、限流、降级
1.2 Spring Cloud Alibaba版本选型¶
Spring Cloud Alibaba是Spring Cloud的中国最佳实践,提供一站式微服务解决方案。
<!-- pom.xml 版本依赖管理 -->
<properties>
<spring-boot.version>3.2.4</spring-boot.version>
<spring-cloud.version>2023.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2023.0.1.0</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- Spring Cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Cloud Alibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
1.3 核心组件矩阵¶
| 功能域 | Spring Cloud Alibaba | Spring Cloud Netflix(已停更) |
|---|---|---|
| 服务注册发现 | Nacos | Eureka |
| 配置中心 | Nacos Config | Spring Cloud Config |
| 熔断降级 | Sentinel | Hystrix |
| 分布式事务 | Seata | — |
| 消息驱动 | RocketMQ | — |
| 服务网关 | Spring Cloud Gateway | Zuul |
| 负载均衡 | Spring Cloud LoadBalancer | Ribbon |
1.4 多模块项目结构¶
microservice-demo/
├── pom.xml # 父POM(版本管理)
├── common/ # 公共模块(DTO、工具类)
│ └── pom.xml
├── gateway/ # 网关服务
│ └── pom.xml
├── user-service/ # 用户服务
│ └── pom.xml
├── order-service/ # 订单服务
│ └── pom.xml
├── product-service/ # 商品服务
│ └── pom.xml
└── docker-compose.yml # 容器编排
2. 服务注册与发现¶
2.1 Nacos注册中心¶
Nacos(Dynamic Naming and Configuration Service)是阿里巴巴开源的服务注册与配置管理平台。
引入依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
配置文件:
# application.yml
spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: dev # 命名空间隔离环境
group: DEFAULT_GROUP
cluster-name: BJ # 集群名(就近访问)
weight: 1 # 权重(负载均衡)
metadata:
version: v1
env: dev
server:
port: 8081
启动类:
@SpringBootApplication
@EnableDiscoveryClient // Spring Cloud标准注解,兼容多种注册中心
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
服务间调用(OpenFeign):
// ============ 声明式Feign客户端 ============
@FeignClient(name = "product-service", fallbackFactory = ProductClientFallback.class)
public interface ProductClient {
@GetMapping("/api/products/{id}")
ProductDTO getProduct(@PathVariable("id") Long id);
@GetMapping("/api/products/batch")
List<ProductDTO> batchGetProducts(@RequestParam("ids") List<Long> ids);
}
// ============ 降级工厂 ============
@Component
public class ProductClientFallback implements FallbackFactory<ProductClient> {
@Override
public ProductClient create(Throwable cause) {
return new ProductClient() {
@Override
public ProductDTO getProduct(Long id) {
// 降级逻辑:返回兜底数据
return ProductDTO.builder()
.id(id)
.name("降级商品")
.price(BigDecimal.ZERO)
.build();
}
@Override
public List<ProductDTO> batchGetProducts(List<Long> ids) {
return Collections.emptyList();
}
};
}
}
2.2 Nacos vs Consul vs Eureka对比¶
| 特性 | Nacos | Consul | Eureka |
|---|---|---|---|
| CAP模型 | AP / CP可切换 | CP | AP |
| 健康检查 | TCP/HTTP/MySQL/自定义 | TCP/HTTP/gRPC/脚本 | 心跳(客户端) |
| 雪崩保护 | ✅ 有 | ❌ 无 | ✅ 自我保护模式 |
| 配置中心 | ✅ 内置 | ✅ KV Store | ❌ 无 |
| 多数据中心 | ✅ 支持 | ✅ 原生支持 | ❌ 有限 |
| Watch机制 | 长轮询 + Push | 长轮询 | 短轮询 |
| 一致性协议 | Raft(CP)/ Distro(AP) | Raft | — |
| Spring Cloud集成 | 非常好 | 好 | 好(已停更) |
| 适用场景 | 国内微服务首选 | 多语言/多DC | 遗留项目 |
Nacos AP/CP切换:
// 临时实例(AP模式,默认)— 适合微服务
// 心跳上报,Nacos不主动检查,注册快速
spring.cloud.nacos.discovery.ephemeral=true
// 持久实例(CP模式)— 适合基础设施服务(如数据库代理)
// Nacos主动健康检查,适合不能主动上报心跳的服务
spring.cloud.nacos.discovery.ephemeral=false
2.3 Nacos集群部署¶
# nacos/conf/cluster.conf
192.168.1.10:8848
192.168.1.11:8848
192.168.1.12:8848
# 使用MySQL做持久化存储(替代内嵌Derby)
# nacos/conf/application.properties
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8
db.user=nacos
db.password=nacos
3. 服务网关¶
3.1 Spring Cloud Gateway基础¶
Spring Cloud Gateway是Spring Cloud官方推荐的API网关,基于WebFlux(响应式编程)构建,性能优于Zuul 1.x。
核心概念: - Route(路由):网关的基本构建块,包含ID、目标URI、谓词集合、过滤器集合 - Predicate(谓词):匹配HTTP请求的条件(路径、Header、参数等) - Filter(过滤器):对请求/响应进行修改
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
3.2 路由配置¶
# gateway-service application.yml
spring:
cloud:
gateway:
# ===== 路由规则 =====
routes:
# 用户服务
- id: user-service
uri: lb://user-service # lb:// 表示从注册中心获取地址
predicates:
- Path=/api/users/** # 路径匹配
- Method=GET,POST # HTTP方法
filters:
- StripPrefix=1 # 去掉前缀 /api
- AddRequestHeader=X-Gateway, true
# 订单服务 — 带权重路由(灰度发布)
- id: order-service-v1
uri: lb://order-service
predicates:
- Path=/api/orders/**
- Weight=group1, 90 # 90%流量
metadata:
version: v1
- id: order-service-v2
uri: lb://order-service-v2
predicates:
- Path=/api/orders/**
- Weight=group1, 10 # 10%流量(灰度)
metadata:
version: v2
# ===== 全局默认过滤器 =====
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Origin
- AddResponseHeader=X-Response-Time, ${response.time}
3.3 自定义全局过滤器¶
/**
* JWT鉴权全局过滤器
*/
@Component
@Order(-1) // 优先级最高
public class AuthGlobalFilter implements GlobalFilter {
private static final List<String> WHITE_LIST = List.of(
"/api/auth/login",
"/api/auth/register",
"/api/products/public"
);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getPath().value();
// 白名单放行
if (WHITE_LIST.stream().anyMatch(path::startsWith)) {
return chain.filter(exchange);
}
// 提取Token
String token = request.getHeaders().getFirst("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
return unauthorized(exchange, "Missing or invalid token");
}
// 验证Token
try {
String jwt = token.substring(7);
Claims claims = JwtUtil.parseToken(jwt);
String userId = claims.getSubject();
// 将用户信息传递给下游服务
ServerHttpRequest mutatedRequest = request.mutate()
.header("X-User-Id", userId)
.header("X-User-Role", claims.get("role", String.class))
.build();
return chain.filter(exchange.mutate().request(mutatedRequest).build());
} catch (ExpiredJwtException e) {
return unauthorized(exchange, "Token expired");
} catch (Exception e) {
return unauthorized(exchange, "Invalid token");
}
}
private Mono<Void> unauthorized(ServerWebExchange exchange, String message) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
String body = """
{"code": 401, "message": "%s"}
""".formatted(message);
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(buffer));
}
}
3.4 网关限流¶
/**
* 基于Redis的请求速率限流
*/
@Configuration
public class RateLimitConfig {
/**
* 按IP限流的KeyResolver
*/
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(
Objects.requireNonNull(exchange.getRequest().getRemoteAddress())
.getAddress().getHostAddress()
);
}
}
# 网关限流配置
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 100 # 每秒100个令牌
redis-rate-limiter.burstCapacity: 200 # 令牌桶容量200
redis-rate-limiter.requestedTokens: 1 # 每个请求消耗1个令牌
key-resolver: "#{@ipKeyResolver}"
4. 负载均衡¶
4.1 Spring Cloud LoadBalancer¶
Spring Cloud LoadBalancer取代了已弃用的Netflix Ribbon,提供客户端负载均衡能力。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
4.2 负载均衡策略¶
/**
* 自定义负载均衡配置
* 注意:不要添加 @Configuration,避免被全局扫描
*/
public class CustomLoadBalancerConfig {
/**
* 轮询策略(默认)
*/
@Bean
public ReactorLoadBalancer<ServiceInstance> roundRobinLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RoundRobinLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
name
);
}
/**
* 随机策略
*/
@Bean
public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
name
);
}
}
// ============ 指定服务使用特定负载均衡策略 ============
@Configuration
@LoadBalancerClients({
@LoadBalancerClient(name = "user-service", configuration = CustomLoadBalancerConfig.class),
@LoadBalancerClient(name = "order-service", configuration = CustomLoadBalancerConfig.class)
})
public class LoadBalancerAutoConfig {
}
4.3 基于Nacos权重的负载均衡¶
/**
* Nacos权重负载均衡器 — 结合Nacos控制台动态调权
*/
public class NacosWeightLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private final String serviceId;
private final ObjectProvider<ServiceInstanceListSupplier> supplierProvider;
public NacosWeightLoadBalancer(
ObjectProvider<ServiceInstanceListSupplier> supplierProvider,
String serviceId) {
this.supplierProvider = supplierProvider;
this.serviceId = serviceId;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
return supplierProvider.getIfAvailable()
.get()
.next()
.map(this::selectByWeight);
}
private Response<ServiceInstance> selectByWeight(List<ServiceInstance> instances) {
if (instances.isEmpty()) {
return new EmptyResponse();
}
// 使用Nacos权重进行随机选择
List<Pair<ServiceInstance, Double>> weightedInstances = instances.stream()
.map(inst -> {
double weight = Double.parseDouble(
inst.getMetadata().getOrDefault("nacos.weight", "1.0"));
return Pair.of(inst, weight);
})
.toList();
double totalWeight = weightedInstances.stream()
.mapToDouble(Pair::getRight).sum();
double random = ThreadLocalRandom.current().nextDouble(totalWeight);
double cumulative = 0;
for (Pair<ServiceInstance, Double> pair : weightedInstances) {
cumulative += pair.getRight();
if (random < cumulative) {
return new DefaultResponse(pair.getLeft());
}
}
return new DefaultResponse(instances.get(0));
}
}
5. 配置中心¶
5.1 Nacos Config¶
Nacos不仅是注册中心,也是强大的配置中心,支持动态配置刷新。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Spring Cloud 2023.x 需要引入 bootstrap -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
# bootstrap.yml(优先于application.yml加载)
spring:
application:
name: order-service
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
namespace: dev
group: DEFAULT_GROUP
file-extension: yaml # 配置格式
# 共享配置:多服务共用的配置
shared-configs:
- data-id: common-database.yaml
group: SHARED_GROUP
refresh: true
- data-id: common-redis.yaml
group: SHARED_GROUP
refresh: true
# 扩展配置
extension-configs:
- data-id: order-datasource.yaml
group: DEFAULT_GROUP
refresh: true
5.2 动态配置刷新¶
/**
* 动态配置示例:降级开关、限流阈值等运行时可调整参数
*/
@Component
@RefreshScope // Spring Cloud原生注解,配置变更时重新创建Bean
public class DynamicConfigProperties {
@Value("${order.max-amount:99999}")
private BigDecimal maxOrderAmount;
@Value("${feature.toggle.new-checkout:false}")
private boolean newCheckoutEnabled;
@Value("${rate-limit.qps:100}")
private int rateLimitQps;
// Getters ...
public BigDecimal getMaxOrderAmount() { return maxOrderAmount; }
public boolean isNewCheckoutEnabled() { return newCheckoutEnabled; }
public int getRateLimitQps() { return rateLimitQps; }
}
/**
* 也可以使用 @ConfigurationProperties 方式
*/
@Component
@ConfigurationProperties(prefix = "order")
@RefreshScope
public class OrderConfig {
private BigDecimal maxAmount;
private int timeout;
private RetryConfig retry;
@Data
public static class RetryConfig {
private int maxRetries;
private long delay;
}
// Getters & Setters ...
}
5.3 Nacos Config vs Apollo对比¶
| 特性 | Nacos Config | Apollo |
|---|---|---|
| 配置格式 | Properties/YAML/JSON/XML | Properties/YAML/JSON/XML |
| 实时推送 | ✅ 长轮询(1s内) | ✅ 推拉结合(1s内) |
| 灰度发布 | ✅ 支持 | ✅ 支持(更强) |
| 权限管理 | 基础 | ✅ 细粒度(项目/命名空间) |
| 版本回滚 | ✅ 支持 | ✅ 支持(更完善) |
| 多环境 | 命名空间隔离 | 环境 + 集群 |
| 部署复杂度 | 低(单一组件) | 较高(Portal + Admin + Config) |
| 适用场景 | 配置+注册一体化 | 大规模配置管理 |
5.4 配置加密¶
/**
* 敏感配置加密 — 结合Jasypt
*/
// 1. 引入依赖
// com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5
// 2. 配置
// jasypt.encryptor.password=your-secret-key
// 3. 加密值使用 ENC() 包裹
// spring.datasource.password=ENC(加密后的密文)
@Configuration
public class JasyptConfig {
@Bean("jasyptStringEncryptor")
public StringEncryptor stringEncryptor() {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(System.getenv("JASYPT_PASSWORD")); // 从环境变量获取密钥
config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
config.setKeyObtentionIterations("1000");
config.setPoolSize("1");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
encryptor.setConfig(config);
return encryptor;
}
}
6. 熔断降级¶
6.1 Sentinel核心概念¶
Sentinel是阿里巴巴开源的流量治理组件,面向分布式服务架构的流量控制、熔断降级、系统负载保护。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- Sentinel Dashboard数据源持久化到Nacos -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
spring:
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080 # Sentinel Dashboard地址
port: 8719 # 与Dashboard通信端口
# 规则持久化到Nacos
datasource:
flow:
nacos:
server-addr: 127.0.0.1:8848
data-id: ${spring.application.name}-flow-rules
group-id: SENTINEL_GROUP
data-type: json
rule-type: flow
6.2 流控规则¶
/**
* 注解方式定义Sentinel资源 + 降级
*/
@Service
public class OrderService {
/**
* @SentinelResource 定义资源,配置降级和异常处理
* - value: 资源名称
* - blockHandler: 被Sentinel限流/降级时的处理方法
* - fallback: 业务异常时的降级方法
*/
@SentinelResource(
value = "createOrder",
blockHandler = "createOrderBlockHandler",
fallback = "createOrderFallback"
)
public OrderDTO createOrder(CreateOrderRequest request) {
// 正常业务逻辑
ProductDTO product = productClient.getProduct(request.getProductId());
if (product.getStock() < request.getQuantity()) {
throw new BusinessException("库存不足");
}
// ... 创建订单
return orderDTO;
}
/**
* 被Sentinel限流/熔断时调用
* 参数列表必须与原方法一致 + 多一个BlockException
*/
public OrderDTO createOrderBlockHandler(CreateOrderRequest request, BlockException ex) {
if (ex instanceof FlowException) {
throw new ApiException(429, "下单太快了,请稍后重试");
}
if (ex instanceof DegradeException) {
throw new ApiException(503, "订单服务暂时不可用,请稍后重试");
}
throw new ApiException(429, "请求被限流: " + ex.getClass().getSimpleName());
}
/**
* 业务异常降级
*/
public OrderDTO createOrderFallback(CreateOrderRequest request, Throwable ex) {
log.error("创建订单降级处理, request={}", request, ex);
// 写入消息队列延迟处理
mqTemplate.asyncSend("ORDER_FALLBACK", request);
return OrderDTO.builder()
.status("PENDING")
.message("订单已提交,稍后处理")
.build();
}
}
6.3 热点参数限流¶
/**
* 热点参数限流 — 针对频繁访问的特定参数值限流
* 场景:秒杀活动中,对热门商品ID限流
*/
@SentinelResource(
value = "getProduct",
blockHandler = "getProductBlockHandler"
)
public ProductDTO getProduct(Long productId) {
return productRepository.findById(productId)
.map(this::convertToDTO)
.orElseThrow(() -> new NotFoundException("商品不存在: " + productId));
}
/**
* 编程方式配置热点参数限流规则
*/
@PostConstruct
public void initHotParamRules() {
ParamFlowRule rule = new ParamFlowRule();
rule.setResource("getProduct");
rule.setParamIdx(0); // 第0个参数(productId)
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(100); // 默认每个参数值QPS上限100
// 特殊值例外项:热门商品ID限流更严格
ParamFlowItem item1 = new ParamFlowItem()
.setObject("1001") // 热门商品ID
.setClassType(long.class.getName())
.setCount(20); // 该商品QPS上限20
ParamFlowItem item2 = new ParamFlowItem()
.setObject("1002")
.setClassType(long.class.getName())
.setCount(20);
rule.setParamFlowItemList(List.of(item1, item2));
ParamFlowRuleManager.loadRules(List.of(rule));
}
6.4 熔断降级策略¶
/**
* 编程方式配置熔断降级规则
*/
@PostConstruct
public void initDegradeRules() {
List<DegradeRule> rules = new ArrayList<>();
// 策略1:慢调用比例 — RT超过200ms的请求比例超过50%时熔断
DegradeRule slowCallRule = new DegradeRule("createOrder")
.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())
.setCount(0.5) // 慢调用比例阈值50%
.setSlowRatioThreshold(200) // 慢调用RT阈值200ms
.setTimeWindow(30) // 熔断持续30秒
.setMinRequestAmount(10) // 最小统计请求数
.setStatIntervalMs(10000); // 统计时长10秒
// 策略2:异常比例 — 异常比例超过30%时熔断
DegradeRule errorRatioRule = new DegradeRule("createOrder")
.setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType())
.setCount(0.3) // 异常比例30%
.setTimeWindow(60) // 熔断持续60秒
.setMinRequestAmount(10)
.setStatIntervalMs(10000);
// 策略3:异常数 — 异常数超过5时熔断
DegradeRule errorCountRule = new DegradeRule("queryOrder")
.setGrade(CircuitBreakerStrategy.ERROR_COUNT.getType())
.setCount(5) // 异常数5
.setTimeWindow(60)
.setMinRequestAmount(5)
.setStatIntervalMs(60000);
rules.addAll(List.of(slowCallRule, errorRatioRule, errorCountRule));
DegradeRuleManager.loadRules(rules);
}
7. 分布式事务¶
7.1 Seata概述¶
Seata(Simple Extensible Autonomous Transaction Architecture)是阿里巴巴开源的分布式事务解决方案,支持AT、TCC、Saga三种模式。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
seata:
application-id: order-service
tx-service-group: my_tx_group
service:
vgroup-mapping:
my_tx_group: default
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: ""
group: SEATA_GROUP
7.2 AT模式(自动补偿,推荐)¶
AT模式是Seata默认模式,对业务无侵入,自动生成回滚SQL。
/**
* AT模式 — 下单扣库存扣余额(跨服务事务)
* 只需在发起方加 @GlobalTransactional
*/
@Service
public class OrderService {
@Autowired private OrderRepository orderRepository;
@Autowired private ProductClient productClient;
@Autowired private AccountClient accountClient;
@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
public OrderDTO createOrder(CreateOrderRequest request) {
// Step 1: 创建订单
Order order = Order.builder()
.userId(request.getUserId())
.productId(request.getProductId())
.quantity(request.getQuantity())
.totalAmount(request.getTotalAmount())
.status(OrderStatus.PENDING)
.build();
orderRepository.save(order);
// Step 2: 扣减库存(远程调用product-service)
productClient.deductStock(request.getProductId(), request.getQuantity());
// Step 3: 扣减余额(远程调用account-service)
accountClient.deductBalance(request.getUserId(), request.getTotalAmount());
// Step 4: 更新订单状态
order.setStatus(OrderStatus.CONFIRMED);
orderRepository.save(order);
// 任何一步失败,Seata自动回滚所有分支事务
return convertToDTO(order);
}
}
AT模式原理:
第一阶段(Prepare):
1. 解析SQL → 查询前镜像(before image)
2. 执行业务SQL
3. 查询后镜像(after image)
4. 生成Undo Log → 写入undo_log表
5. 注册分支事务 → 获取全局锁
6. 提交本地事务(业务数据 + undo log一起提交)
第二阶段(Commit/Rollback):
- 全局提交 → 异步删除undo log
- 全局回滚 → 根据undo log逆向生成SQL执行回滚
7.3 TCC模式(高性能)¶
TCC(Try-Confirm-Cancel)模式需要业务代码实现三个阶段,适合高并发场景。
/**
* TCC模式 — 库存扣减
* 需要手动实现Try/Confirm/Cancel三个方法
*/
@LocalTCC
public interface StockTccAction {
/**
* Try阶段:冻结库存
*/
@TwoPhaseBusinessAction(
name = "deductStock",
commitMethod = "confirm",
rollbackMethod = "cancel"
)
boolean tryDeduct(
@BusinessActionContextParameter(paramName = "productId") Long productId,
@BusinessActionContextParameter(paramName = "quantity") int quantity
);
/**
* Confirm阶段:确认扣减(释放冻结量)
*/
boolean confirm(BusinessActionContext context);
/**
* Cancel阶段:取消冻结,恢复库存
*/
boolean cancel(BusinessActionContext context);
}
@Service
public class StockTccActionImpl implements StockTccAction {
@Autowired private StockRepository stockRepository;
@Override
@Transactional
public boolean tryDeduct(Long productId, int quantity) {
// 冻结库存:available - quantity, frozen + quantity
int updated = stockRepository.freezeStock(productId, quantity);
if (updated == 0) {
throw new BusinessException("库存不足");
}
return true;
}
@Override
@Transactional
public boolean confirm(BusinessActionContext context) {
Long productId = context.getActionContext("productId", Long.class);
int quantity = context.getActionContext("quantity", Integer.class);
// 确认扣减:frozen - quantity
stockRepository.confirmDeduct(productId, quantity);
return true;
}
@Override
@Transactional
public boolean cancel(BusinessActionContext context) {
Long productId = context.getActionContext("productId", Long.class);
int quantity = context.getActionContext("quantity", Integer.class);
// 取消冻结:available + quantity, frozen - quantity
stockRepository.cancelFreeze(productId, quantity);
return true;
}
}
7.4 Saga模式(长事务)¶
Saga模式适合业务流程长、参与服务多的场景,通过编排或协同方式实现最终一致性。
/**
* Saga模式 — 状态机编排方式
* 通过JSON定义状态机流转
*/
// saga-statemachine.json(Seata Saga状态机定义)
/*
{
"Name": "createOrderSaga",
"Comment": "创建订单Saga",
"StartState": "CreateOrder",
"States": {
"CreateOrder": {
"Type": "ServiceTask",
"ServiceName": "orderService",
"ServiceMethod": "createOrder",
"CompensateState": "CompensateCreateOrder",
"Next": "DeductStock"
},
"DeductStock": {
"Type": "ServiceTask",
"ServiceName": "stockService",
"ServiceMethod": "deductStock",
"CompensateState": "CompensateDeductStock",
"Next": "DeductBalance"
},
"DeductBalance": {
"Type": "ServiceTask",
"ServiceName": "accountService",
"ServiceMethod": "deductBalance",
"CompensateState": "CompensateDeductBalance",
"Next": "Succeed"
},
"Succeed": { "Type": "Succeed" },
"CompensateCreateOrder": { ... },
"CompensateDeductStock": { ... },
"CompensateDeductBalance": { ... }
}
}
*/
// AT vs TCC vs Saga 对比
// | 模式 | 侵入性 | 性能 | 适用场景 |
// |------|--------|---------|-------------------|
// | AT | 无侵入 | 中等 | 中低并发的CRUD |
// | TCC | 高侵入 | 高 | 高并发资金类操作 |
// | Saga | 中侵入 | 中等 | 长事务/多步骤流程 |
8. 链路追踪¶
8.1 OpenTelemetry集成¶
OpenTelemetry(OTel)是CNCF的标准化可观测性框架,整合了Tracing、Metrics、Logs。
<!-- Spring Boot 3.x + Micrometer + OTel -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>
# application.yml
management:
tracing:
sampling:
probability: 1.0 # 采样率100%(生产环境建议0.1~0.5)
otlp:
tracing:
endpoint: http://localhost:4318/v1/traces # OTel Collector地址
logging:
pattern:
level: "%5p [${spring.application.name},%X{traceId},%X{spanId}]"
8.2 自定义Span¶
@Service
public class PaymentService {
private final Tracer tracer;
public PaymentService(Tracer tracer) {
this.tracer = tracer;
}
public PaymentResult processPayment(PaymentRequest request) {
// 创建自定义Span
Span span = tracer.nextSpan().name("process-payment").start();
try (Tracer.SpanInScope ws = tracer.withSpan(span)) {
span.tag("payment.method", request.getMethod());
span.tag("payment.amount", request.getAmount().toString());
span.tag("payment.orderId", request.getOrderId());
// 调用第三方支付
PaymentResult result = callThirdPartyPayment(request);
span.tag("payment.status", result.getStatus());
span.event("payment-completed");
return result;
} catch (Exception e) {
span.error(e);
throw e;
} finally {
span.end();
}
}
}
8.3 Jaeger与SkyWalking对比¶
| 特性 | Jaeger | SkyWalking |
|---|---|---|
| 数据协议 | OpenTelemetry / Jaeger原生 | OTel / SW私有协议 |
| 探针方式 | SDK集成 | Java Agent(无侵入) |
| 存储后端 | Elasticsearch / Cassandra | ES / MySQL / BanyanDB |
| 拓扑图 | 服务拓扑 | ✅ 更丰富(实例/端点级别) |
| 告警 | 基础 | ✅ 强大(支持Webhook/钉钉) |
| 性能分析 | ❌ | ✅ 代码级性能剖析 |
| 生态 | CNCF毕业项目 | Apache顶级项目 |
| 适用场景 | 轻量级链路追踪 | 全方位APM监控 |
# SkyWalking Java Agent方式(无侵入)
# JVM启动参数:
# -javaagent:/path/to/skywalking-agent.jar
# -Dskywalking.agent.service_name=order-service
# -Dskywalking.collector.backend_service=127.0.0.1:11800
9. 消息驱动¶
9.1 Spring Cloud Stream简介¶
Spring Cloud Stream是Spring Cloud的消息驱动框架,提供统一的编程模型,屏蔽底层MQ差异。
<!-- RocketMQ Binder -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
</dependency>
9.2 基于函数式编程的消息处理¶
/**
* Spring Cloud Stream 函数式消息处理
* Spring Cloud 2022.x+ 推荐使用函数式方式
*/
@Configuration
public class MessageFunctions {
/**
* 消费者:处理订单创建事件
*/
@Bean
public Consumer<OrderCreatedEvent> orderCreated() {
return event -> {
log.info("收到订单创建事件: orderId={}, userId={}",
event.getOrderId(), event.getUserId());
// 发送通知、积分处理等
notificationService.sendOrderConfirmation(event);
pointService.addPoints(event.getUserId(), event.getAmount());
};
}
/**
* 供应者:定时生成报表事件
*/
@Bean
public Supplier<ReportEvent> reportGenerator() {
return () -> {
ReportEvent event = new ReportEvent();
event.setTimestamp(Instant.now());
event.setType("DAILY_SUMMARY");
return event;
};
}
/**
* 处理器:消息转换(输入 → 处理 → 输出)
*/
@Bean
public Function<PaymentEvent, NotificationEvent> paymentProcessor() {
return paymentEvent -> {
log.info("处理支付事件: {}", paymentEvent);
NotificationEvent notification = new NotificationEvent();
notification.setUserId(paymentEvent.getUserId());
notification.setMessage("支付成功,金额: " + paymentEvent.getAmount());
notification.setChannel("SMS");
return notification;
};
}
}
# application.yml — Spring Cloud Stream + RocketMQ配置
spring:
cloud:
stream:
# 绑定函数到channel
function:
definition: orderCreated;reportGenerator;paymentProcessor
bindings:
# 消费者绑定
orderCreated-in-0:
destination: order-topic
group: order-consumer-group
content-type: application/json
# 供应者绑定(定时Poller)
reportGenerator-out-0:
destination: report-topic
content-type: application/json
# 处理器绑定
paymentProcessor-in-0:
destination: payment-topic
group: payment-consumer-group
paymentProcessor-out-0:
destination: notification-topic
rocketmq:
binder:
name-server: 127.0.0.1:9876
bindings:
orderCreated-in-0:
consumer:
tags: "CREATE||UPDATE" # 消息Tag过滤
orderly: true # 顺序消费
9.3 手动发送消息¶
@Service
public class OrderEventPublisher {
@Autowired
private StreamBridge streamBridge;
/**
* 手动发送消息到指定Topic
*/
public void publishOrderCreatedEvent(Order order) {
OrderCreatedEvent event = OrderCreatedEvent.builder()
.orderId(order.getId())
.userId(order.getUserId())
.amount(order.getTotalAmount())
.timestamp(Instant.now())
.build();
// 发送到指定绑定
boolean sent = streamBridge.send("orderCreated-out-0", event);
log.info("订单创建事件发送{}: orderId={}", sent ? "成功" : "失败", order.getId());
}
/**
* 发送延迟消息(RocketMQ特性)
*/
public void publishDelayedEvent(String orderId) {
Message<String> message = MessageBuilder.withPayload(orderId)
.setHeader("DELAY_LEVEL", 16) // RocketMQ延迟等级(约2小时)
.build();
streamBridge.send("order-timeout-out-0", message);
}
}
9.4 RocketMQ vs Kafka对比¶
| 特性 | RocketMQ | Kafka |
|---|---|---|
| 延迟消息 | ✅ 原生支持(固定等级 / 5.x任意时间) | ❌ 需额外方案 |
| 事务消息 | ✅ 原生支持 | ❌ 仅有事务生产者 |
| 消费模型 | Push + Pull | Pull |
| 消息回溯 | ✅ 按时间戳 | ✅ 按offset |
| 消息过滤 | Tag / SQL92 | ❌ 需消费端过滤 |
| 吞吐量 | 十万级/秒 | 百万级/秒 |
| 延迟 | ms级 | ms级 |
| 适用场景 | 电商/金融/可靠消息 | 大数据/日志/流处理 |
10. 微服务部署¶
10.1 Docker Compose编排¶
# docker-compose.yml
version: '3.8'
services:
# ===== 基础设施 =====
nacos:
image: nacos/nacos-server:v2.3.0
ports:
- "8848:8848"
- "9848:9848"
environment:
- MODE=standalone
- SPRING_DATASOURCE_PLATFORM=mysql
- MYSQL_SERVICE_HOST=mysql
- MYSQL_SERVICE_DB_NAME=nacos_config
- MYSQL_SERVICE_USER=root
- MYSQL_SERVICE_PASSWORD=root
depends_on:
mysql:
condition: service_healthy
mysql:
image: mysql:8.0
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: nacos_config
volumes:
- mysql-data:/var/lib/mysql
- ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
# ===== 微服务 =====
gateway:
build: ./gateway
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=docker
- NACOS_ADDR=nacos:8848
depends_on:
- nacos
user-service:
build: ./user-service
ports:
- "8081:8081"
environment:
- SPRING_PROFILES_ACTIVE=docker
- NACOS_ADDR=nacos:8848
- DB_HOST=mysql
- REDIS_HOST=redis
depends_on:
- nacos
- mysql
- redis
deploy:
replicas: 2 # 启动2个实例
order-service:
build: ./order-service
ports:
- "8082:8082"
environment:
- SPRING_PROFILES_ACTIVE=docker
- NACOS_ADDR=nacos:8848
- DB_HOST=mysql
- REDIS_HOST=redis
depends_on:
- nacos
- mysql
- redis
deploy:
replicas: 2
volumes:
mysql-data:
10.2 Dockerfile优化(多阶段构建)¶
# ===== 第一阶段:构建 =====
FROM eclipse-temurin:21-jdk AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
# 使用Maven包装器构建
COPY .mvn .mvn
COPY mvnw .
RUN chmod +x mvnw && ./mvnw clean package -DskipTests
# ===== 第二阶段:运行 =====
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
# 安全:使用非root用户
RUN addgroup -S app && adduser -S app -G app
USER app
# 拷贝jar
COPY --from=builder /app/target/*.jar app.jar
# JVM调优参数
ENV JAVA_OPTS="-XX:+UseZGC \
-XX:MaxRAMPercentage=75.0 \
-XX:+UseStringDeduplication \
-Djava.security.egd=file:/dev/./urandom"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
10.3 Kubernetes部署¶
# k8s/user-service-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
labels:
app: user-service
spec:
replicas: 3
selector:
matchLabels:
app: user-service
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # 零宕机发布
template:
metadata:
labels:
app: user-service
version: v1
spec:
containers:
- name: user-service
image: registry.example.com/user-service:latest
ports:
- containerPort: 8081
env:
- name: SPRING_PROFILES_ACTIVE
value: k8s
- name: NACOS_ADDR
valueFrom:
configMapKeyRef:
name: app-config
key: nacos-addr
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
# 就绪探针
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8081
initialDelaySeconds: 30
periodSeconds: 10
# 存活探针
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8081
initialDelaySeconds: 60
periodSeconds: 30
# 优雅停机
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 10"]
terminationGracePeriodSeconds: 60
---
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:
- port: 8081
targetPort: 8081
type: ClusterIP
10.4 灰度发布(金丝雀发布)¶
# 灰度版本Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service-canary
labels:
app: user-service
version: v2-canary
spec:
replicas: 1 # 少量实例
selector:
matchLabels:
app: user-service
version: v2-canary
template:
metadata:
labels:
app: user-service
version: v2-canary
spec:
containers:
- name: user-service
image: registry.example.com/user-service:v2-canary
ports:
- containerPort: 8081
# 使用Nginx Ingress实现灰度流量切分
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: user-service-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10" # 10%流量到灰度
# 也可以按Header分流:
# nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
# nginx.ingress.kubernetes.io/canary-by-header-value: "true"
spec:
rules:
- host: api.example.com
http:
paths:
- path: /api/users
pathType: Prefix
backend:
service:
name: user-service-canary
port:
number: 8081
11. 面试题¶
Q1: Nacos作为注册中心,AP模式和CP模式有什么区别?什么场景用哪种?¶
A:
AP模式(临时实例,默认):
- 使用Distro协议(类似Gossip),最终一致性
- 服务实例通过心跳上报,Nacos不主动检查
- 实例宕机后快速移除,注册速度快
- 适合:微服务实例(频繁上下线)
CP模式(持久实例):
- 使用Raft协议,强一致性
- Nacos主动健康检查
- 实例下线后标记为不健康而非删除
- 适合:基础设施服务(DNS、数据库代理 — 不能主动上报心跳的服务)
选择依据:
- 对一致性要求不高、希望高可用 → AP(大多数微服务场景)
- 对数据一致性要求高、服务不能主动上报心跳 → CP
Q2: Spring Cloud Gateway和Nginx的区别?网关限流怎么实现?¶
A:
Spring Cloud Gateway vs Nginx:
┌──────────────┬─────────────────────┬──────────────────────┐
│ 特性 │ Spring Cloud GW │ Nginx │
├──────────────┼─────────────────────┼──────────────────────┤
│ 定位 │ 业务网关 │ 流量网关(反向代理) │
│ 服务发现 │ 集成注册中心 │ 需额外配置upstream │
│ 动态路由 │ 支持(配置中心) │ 需reload │
│ 限流 │ Sentinel/Redis │ limit_req模块 │
│ 鉴权 │ 编程式JWT/OAuth2 │ 基础(Lua脚本扩展) │
│ 性能 │ 中等(JVM) │ 高(C语言/事件驱动) │
│ 协议 │ HTTP/WebSocket │ HTTP/TCP/UDP │
└──────────────┴─────────────────────┴──────────────────────┘
生产实践中常用 Nginx(流量网关) → Gateway(业务网关) 二级网关架构。
限流方案:
1. Gateway内置:RequestRateLimiter + Redis(令牌桶算法)
2. Sentinel整合:spring-cloud-alibaba-sentinel-gateway
3. 自定义:GlobalFilter + Redis计数器(滑动窗口)
Q3: Seata AT模式的原理?有什么缺点?¶
A:
原理:
1. TM(事务管理器)开启全局事务,获取XID
2. RM(资源管理器)执行分支事务:
- 解析SQL → 查询前镜像 → 执行SQL → 查询后镜像
- 生成undo_log → 注册分支事务 → 获取全局锁
- 提交本地事务(业务数据 + undo_log)
3. 全局提交 → 异步清理undo_log
4. 全局回滚 → 根据undo_log反向补偿
缺点:
- 全局锁导致并发度降低(写写冲突要排队)
- 不支持SQL联合主键的某些场景
- undo_log增加额外存储成本
- 不适合高并发写场景(锁竞争严重)
优化建议:
- 高并发场景考虑TCC模式
- 减小事务范围,避免长事务
- 适当设置事务超时时间
Q4: 微服务链路追踪的核心原理是什么?TraceId如何跨服务传播?¶
A:
核心原理:
- 分布式追踪基于Google Dapper论文
- 核心概念:Trace(整个请求链路)→ Span(单个操作)
- 每个Span包含:TraceId、SpanId、ParentSpanId、开始/结束时间、标签
TraceId跨服务传播机制:
1. HTTP调用:通过Header传递
- W3C标准:traceparent: 00-{traceId}-{spanId}-{flags}
- B3格式:X-B3-TraceId, X-B3-SpanId, X-B3-ParentSpanId
2. Feign/RestTemplate:自动注入Interceptor,发送请求时添加Header
3. MQ消息:放入消息属性(Message Properties/Headers)
4. 线程池:使用包装的 TraceableExecutorService 传递上下文
Spring Boot 3.x的传播链:
Micrometer Tracing → OTel Bridge → OTel SDK → Exporter → Jaeger/Zipkin/SkyWalking
Q5: Sentinel和Hystrix的核心区别?Sentinel的滑动窗口如何实现?¶
A:
核心区别:
┌──────────────┬───────────────────────┬──────────────────┐
│ 特性 │ Sentinel │ Hystrix │
├──────────────┼───────────────────────┼──────────────────┤
│ 隔离策略 │ 信号量(默认) │ 线程池 / 信号量 │
│ 熔断策略 │ 慢调用/异常比/异常数 │ 异常比例 │
│ 实时统计 │ 滑动窗口(LeapArray) │ 滑动窗口(RxJava)│
│ 规则配置 │ 控制台动态推送 │ 需重启生效 │
│ 限流 │ QPS/线程数/热点参数 │ 有限 │
│ 系统保护 │ CPU/Load/RT自适应 │ ❌ │
│ 维护状态 │ 活跃维护 │ 已停更 │
└──────────────┴───────────────────────┴──────────────────┘
Sentinel滑动窗口实现(LeapArray):
- 使用环形数组(ArrayMetric),每个元素是一个时间窗格(WindowWrap)
- 默认1秒分为2个窗格(500ms/个)
- 每个窗格记录:通过数、阻塞数、异常数、RT
- 获取当前时间对应窗格 → 如果窗格已过期则重置
- 统计时遍历所有有效窗格求和
- 相比固定窗口,避免了临界点突发流量问题
Q6: 微服务灰度发布有哪些方案?Spring Cloud如何实现全链路灰度?¶
A:
/**
* 全链路灰度发布实现方案:
*
* 1. 网关层:根据Header/Cookie/用户ID/IP等打标
* 2. 服务间传递:通过请求Header传递灰度标记
* 3. 负载均衡:根据灰度标记选择对应版本的服务实例
*/
// ===== Step 1: 网关灰度标记过滤器 =====
@Component
public class GrayMarkFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
// 灰度策略:指定用户 / 百分比 / AB测试组
boolean isGray = grayRuleService.isGrayUser(userId);
if (isGray) {
ServerHttpRequest request = exchange.getRequest().mutate()
.header("X-Gray", "true")
.header("X-Version", "v2")
.build();
return chain.filter(exchange.mutate().request(request).build());
}
return chain.filter(exchange);
}
@Override
public int getOrder() { return 0; }
}
// ===== Step 2: Feign拦截器传递灰度Header =====
@Component
public class GrayFeignInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// 从当前请求上下文获取灰度标记并传递
ServletRequestAttributes attrs = (ServletRequestAttributes)
RequestContextHolder.getRequestAttributes();
if (attrs != null) {
String gray = attrs.getRequest().getHeader("X-Gray");
if ("true".equals(gray)) {
template.header("X-Gray", "true");
template.header("X-Version",
attrs.getRequest().getHeader("X-Version"));
}
}
}
}
// ===== Step 3: 自定义负载均衡器(按版本路由) =====
// 根据X-Version Header选择metadata中version匹配的实例
下一章:18-Java新特性与GraalVM - Java 22-25新特性、GraalVM Native Image、虚拟线程深入、Panama FFM API