微服务常见组件

微服务常见组件详解

一、组件总览

1.1 微服务架构全景图

复制代码
                                     ┌──────────────────┐
                                     │      Nacos       │
                                     │ 注册中心+配置中心 │
                                     └────────┬─────────┘
                                              │
              ┌───────────────────────────────┼───────────────────────────────┐
              │                               │                               │
              ▼                               ▼                               ▼
┌──────────────────┐               ┌──────────────────┐             ┌──────────────────┐
│     Gateway      │               │   User-Service   │             │  Order-Service   │
│      网关        │─────────────▶│     用户服务      │◀────── ───▶│     订单服务     │
└────────┬─────────┘   OpenFeign   └────────┬─────────┘  OpenFeign  └────────┬─────────┘
         │                                  │                                │
         │                                  ▼                                │
         │                         ┌──────────────────┐                      │
         │                         │    Sentinel      │                      │
         │                         │  限流/熔断/降级   │                      │
         │                         └──────────────────┘                      │
         │                                                                   │
         └───────────────────────────────┬───────────────────────────────────┘
                                         │
                                         ▼
                                ┌──────────────────┐
                                │      Seata       │
                                │    分布式事务     │
                                └──────────────────┘

1.2 核心组件一览

组件 定位 核心作用 解决的问题
Nacos 注册中心 + 配置中心 服务注册发现、配置集中管理 服务地址动态变化、配置散落各处
Gateway 服务网关 统一入口、路由转发、认证鉴权 客户端需要知道每个服务地址
OpenFeign 服务调用 声明式HTTP客户端、负载均衡 手写HTTP调用代码繁琐
Sentinel 服务容错 限流、熔断、降级 服务雪崩、系统过载
Seata 分布式事务 跨服务事务一致性 分布式环境下数据不一致

1.3 请求处理流程

一个典型的微服务请求流程:

复制代码
用户请求 ──▶ Gateway(网关)
              │
              ├── 1. 认证鉴权(JWT校验)
              ├── 2. 从Nacos获取服务列表
              ├── 3. 路由转发到目标服务
              │
              ▼
         目标服务(如订单服务)
              │
              ├── 4. Sentinel限流/熔断检查
              ├── 5. 执行业务逻辑
              ├── 6. OpenFeign调用其他服务(如用户服务、库存服务)
              ├── 7. Seata管理分布式事务
              │
              ▼
         返回响应

1.4 技术选型对比

功能 Spring Cloud Netflix(停更) Spring Cloud Alibaba(推荐)
注册中心 Eureka Nacos
配置中心 Spring Cloud Config Nacos
服务调用 Feign + Ribbon OpenFeign + LoadBalancer
服务网关 Zuul Gateway
服务容错 Hystrix Sentinel
分布式事务 - Seata

二、Nacos 详解(注册中心 + 配置中心)

2.1 是什么

Nacos是阿里巴巴开源的服务发现、配置管理平台,名称来源于 Na ming and Co nfiguration Service。它同时充当注册中心和配置中心,是Spring Cloud Alibaba的核心组件。

2.2 核心功能

功能 说明
服务注册 服务启动时将自身信息注册到Nacos
服务发现 消费者从Nacos获取服务列表
健康检查 心跳检测,自动剔除不健康实例
配置管理 集中管理配置,支持动态刷新
命名空间 环境隔离(dev/test/prod)

2.3 数据模型详解

Nacos的数据模型采用三层结构:

复制代码
┌───────────────────────────────────────────────────────────────┐
│                        Nacos 数据模型                          │
├───────────────────────────────────────────────────────────────┤
│  Namespace(命名空间)                                         │
│  └── 用于环境隔离,如 dev / test / prod                        │
│  └── 默认命名空间为 public                                     │
│                                                               │
│  Group(分组)                                                 │
│  └── 用于业务隔离,如不同项目或模块                              │
│  └── 默认分组为 DEFAULT_GROUP                                  │
│                                                               │
│  Service/DataId                                               │
│  └── 服务名(注册中心)或配置ID(配置中心)                      │
└───────────────────────────────────────────────────────────────┘

层级关系:Namespace > Group > Service/DataId

配置DataId命名规则

复制代码
${spring.application.name}-${spring.profiles.active}.${file-extension}

示例:user-service-dev.yaml

2.4 服务注册与发现原理

复制代码
┌─────────────┐     ① 注册(服务名、IP、端口)       ┌─────────────┐
│  服务提供者  │ ────────────────────────────────▶ │    Nacos    │
│  Provider   │ ◀──────────────────────────────── │    Server   │
└─────────────┘     ② 心跳(每5秒)                 └─────────────┘
                                                          │
                                                          │ ③ 服务列表
                                                          ▼
┌─────────────┐     ④ 拉取服务列表 + 订阅变更         ┌─────────────┐
│  服务消费者  │ ◀───────────────────────────────── │  本地缓存    │
│  Consumer   │                                     └─────────────┘
└─────────────┘

心跳机制详解

时间节点 状态变化 说明
0-5秒 健康 正常发送心跳
5-15秒 健康 未收到心跳,仍在容忍期
15-30秒 不健康 标记为不健康,不参与负载均衡
>30秒 剔除 从注册表删除

2.5 临时实例 vs 持久实例

Nacos支持两种实例类型:

特性 临时实例(默认) 持久实例
健康检查 客户端主动上报心跳 服务端主动探测
下线方式 心跳超时自动剔除 手动下线或探测失败
存储位置 内存 磁盘持久化
适用场景 微服务、K8s 传统IP固定服务
CAP模式 AP(可用性优先) CP(一致性优先)
yaml 复制代码
# 配置为持久实例
spring:
  cloud:
    nacos:
      discovery:
        ephemeral: false  # false=持久实例,true=临时实例(默认)

2.6 配置中心原理详解

长轮询机制

复制代码
┌─────────────┐                              ┌─────────────┐
│   客户端    │ ── ① 长轮询请求(30秒超时)──▶ │   Nacos     │
│   Client    │                              │   Server    │
└─────────────┘                              └─────────────┘
      ▲                                            │
      │                                            │
      │            ② 配置变更时立即返回              │
      └────────────────────────────────────────────┘
      
      ③ 客户端拉取新配置,刷新 @RefreshScope Bean

长轮询工作流程

  1. 客户端发起请求,携带当前配置的MD5值
  2. 服务端hold住请求,最多等待30秒
  3. 如果配置有变化,立即返回变化的DataId
  4. 如果无变化,30秒后返回空结果
  5. 客户端收到响应后,主动拉取最新配置

2.7 配置优先级与共享配置

配置优先级(从高到低):

复制代码
1. 精确匹配:  ${name}-${profile}.${ext}     如 user-service-dev.yaml
2. 同服务不同环境:${name}.${ext}              如 user-service.yaml
3. 扩展配置:  extension-configs            自定义扩展配置
4. 共享配置:  shared-configs               多服务共享配置

共享配置示例

yaml 复制代码
spring:
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
        file-extension: yaml
        # 共享配置(所有服务公用)
        shared-configs:
          - data-id: common-redis.yaml
            group: COMMON_GROUP
            refresh: true           # 是否动态刷新
          - data-id: common-mysql.yaml
            group: COMMON_GROUP
            refresh: true
        # 扩展配置(本服务专用)
        extension-configs:
          - data-id: user-special.yaml
            group: DEFAULT_GROUP
            refresh: true

2.8 核心配置详解

yaml 复制代码
spring:
  application:
    name: user-service
  profiles:
    active: dev
  cloud:
    nacos:
      # ========== 注册中心配置 ==========
      discovery:
        server-addr: localhost:8848        # Nacos服务地址
        namespace: dev-namespace-id        # 命名空间ID(不是名称)
        group: DEFAULT_GROUP               # 分组
        cluster-name: SH                   # 集群名称(同集群优先调用)
        ephemeral: true                    # 临时实例(默认true)
        weight: 1                          # 权重(负载均衡用)
        metadata:                          # 元数据
          version: v1.0
          env: dev
          
      # ========== 配置中心配置 ==========
      config:
        server-addr: localhost:8848
        namespace: dev-namespace-id
        group: DEFAULT_GROUP
        file-extension: yaml               # 配置文件后缀
        refresh-enabled: true              # 启用动态刷新
        timeout: 5000                      # 超时时间(ms)

2.9 配置动态刷新

方式一:@RefreshScope + @Value

java 复制代码
@RefreshScope  // 配置变更时,Bean会重新创建
@RestController
public class ConfigController {
    
    @Value("${custom.config:default}")
    private String config;
    
    @GetMapping("/config")
    public String getConfig() {
        return config;  // 配置变更后自动获取新值
    }
}

方式二:@ConfigurationProperties(推荐)

java 复制代码
@Data
@Component
@ConfigurationProperties(prefix = "custom")
public class CustomConfig {
    private String name;
    private Integer timeout;
    private List<String> servers;
}

// 使用时直接注入,自动刷新
@Service
public class MyService {
    @Autowired
    private CustomConfig customConfig;
}

方式三:监听配置变更事件

java 复制代码
@Component
public class NacosConfigListener {
    
    @NacosConfigListener(dataId = "user-service-dev.yaml", groupId = "DEFAULT_GROUP")
    public void onConfigChange(String newConfig) {
        // 配置变更时触发
        log.info("配置变更: {}", newConfig);
        // 执行自定义逻辑,如重新加载缓存
    }
}

2.10 Nacos集群架构

复制代码
┌───────────────────────────────────────────────────────────────┐
│                        Nacos 集群架构                          │
├───────────────────────────────────────────────────────────────┤
│                                                               │
│   ┌─────────┐     ┌─────────┐    ┌─────────┐                  │
│   │ Nacos-1 │◀─▶│ Nacos-2 │◀─▶│ Nacos-3 │  Raft协议同步    │
│   └─────────┘     └─────────┘    └─────────┘                  │
│        │             │             │                          │
│        └─────────────┴─────────────┘                          │
│                      │                                        │
│               ┌──────┴──────┐                                 │
│               │    MySQL    │  外部存储(生产环境推荐)        │
│               └─────────────┘                                 │
│                                                               │
└───────────────────────────────────────────────────────────────┘

集群要求:至少3个节点,保证Raft协议正常选主

三、Sentinel 详解(服务容错)

3.1 是什么

Sentinel是阿里巴巴开源的流量控制组件,以流量为切入点,提供流量控制、熔断降级、系统负载保护等功能,保护服务的稳定性。

3.2 核心功能一览

功能 说明 应用场景
流量控制 控制QPS或并发线程数 防止突发流量压垂服务
熔断降级 根据失败率/慢调用比例熔断 防止级联故障
系统保护 根据系统负载自适应限流 保护服务器不被压幊
热点参数 针对热点参数限流 秒杀商品ID限流
授权规则 黑白名单控制 控制调用来源

3.3 流量控制详解

3.3.1 限流类型
限流类型 说明 配置参数
QPS限流 每秒请求数超过阈值则限流 grade=1, count=100
并发线程数限流 并发线程数超过阈值则限流 grade=0, count=10
3.3.2 流控效果(限流算法)
复制代码
┌────────────────────────────────────────────────────────────┐
│  1. 直接拒绝(默认)                                         │
│     超过阈值的请求直接拒绝,返回BlockException                │
│                                                            │
│     请求 ──▶ [阈值检查] ──▶ 通过/拒绝                      │
├────────────────────────────────────────────────────────────┤
│  2. Warm Up(预热)                                         │
│     冷启动场景,阈值从低到高逐渐增加                          │
│     防止服务刚启动时被突发流量压垂                            │
│                                                            │
│     初始阈值 = 阈值/3                                       │
│     预热时长内逐渐升高到设定阈值                              │
│                                                            │
│     阈值/3 ────▶ 逐渐升高 ────▶ 最终阈值                   │
│     (冷启动)    (预热时长内)   (设定阈值)                    │
├────────────────────────────────────────────────────────────┤
│  3. 排队等待(匀速排队)                                     │
│     基于漏桶算法,请求匀速通过                                │
│     适合秒杀场景,将突发流量平滑化                            │
│                                                            │
│     请求 ──▶ [队列等待] ──▶ 匀速通过                       │
│            (最大等待时长)                                   │
└────────────────────────────────────────────────────────────┘
3.3.3 限流算法原理

滑动窗口算法(Sentinel默认):

复制代码
时间线:  |----|----|----|----|----|
         t1   t2   t3   t4   t5
         
窗口样本:    [  窗口1  ]          ← 统计t1-t3的请求数
              [  窗口2  ]     ← 窗口滑动,统计t2-t4
                   [  窗口3  ]← 统计t3-t5

特点:每个时间片独立计数,窗口滑动时累加计算
优点:更平滑,避免固定窗口的临界突发问题

令牌桶算法

复制代码
         ┌─────────┐
         │ 令牌桶   │ ← 以固定速率放入令牌
         │ ○○○○○   │
         │ ○○○     │ ← 桶有容量上限(burstCapacity)
         └───┬─────┘
             │
             ▼
请求 ──▶ [获取令牌] ──▶ 有令牌则通过,无则拒绝

特点:允许一定程度的突发流量(令牌桶未满时可突发)

漏桶算法

复制代码
         ┌─────────┐
请求 ──▶│  漏桶    │ ← 请求进入桶中等待
         │ ▓▓▓▓▓   │
         │ ▓▓▓▓▓   │ ← 桶满则溢出(拒绝)
         └───┬─────┘
             │ 匀速流出
             ▼
         处理请求

特点:严格限制输出速率,无突发
适用:秒杀场景,将突发流量平滑化

3.4 熔断降级详解

3.4.1 三种熔断策略
策略 触发条件 配置参数 适用场景
慢调用比例 慢调用比例 > 阈值 slowRatioThreshold=0.5, minRequestAmount=5 接口RT波动大
异常比例 异常比例 > 阈值 errorRatio=0.5, minRequestAmount=5 服务不稳定
异常数 异常数 > 阈值 exceptionCount=5, statIntervalMs=60000 简单场景
3.4.2 熔断状态流转
复制代码
                    失败率/慢调用超阈值
               ┌──────────────────────────┐
               │                          ▼
┌───────────────┐                ┌───────────────┐
│    CLOSED     │                │     OPEN      │
│    关闭态     │                │    打开态      │
│ (正常放行请求) │                │ (直接拒绝请求) │
└───────────────┘                └───────────────┘
       ▲                                   │
       │                                   │
       │   探测请求成功                      │ 熔断时长结束
       │                                   ▼
       │                         ┌───────────────┐
       └─────────────────────────│   HALF_OPEN   │
                                 │    半开态     │
                                 │ (放行一个探测) │
                                 └───────────────┘
                                        │
                                        │ 探测请求失败
                                        ▼
                                   回到 OPEN

关键参数说明

  • statIntervalMs:统计时长,默认1000ms
  • minRequestAmount:最小请求数,不足则不触发熔断
  • slowRatioThreshold:慢调用比例阈值(0-1)
  • maxSlowRt:慢调用RT阈值(ms)
  • recoveryTimeoutMs:熔断持续时间(ms)

3.5 热点参数限流

针对某个特定参数值进行限流,如对某个热点商品ID限流。

java 复制代码
// 注解方式
@SentinelResource(
    value = "getProduct",
    blockHandler = "getProductBlockHandler"
)
public Product getProduct(Long productId) {
    return productService.getById(productId);
}

// 热点参数配置(通过控制台或API)
// 对第0个参数限流,默认阈值100 QPS
// 特殊值:productId=1001 限流10 QPS(秒杀商品)

场景示例

复制代码
普通商品请求:100 QPS
热门商品(id=1001)请求:10 QPS  ← 限制秒杀商品的访问频率

3.6 系统保护规则

根据系统负载自适应限流,保护服务器不被压幊。

指标 说明 触发条件
LOAD 系统load1(Linux) load1 > 阈值且并发 > 核数*2.5
CPU使用率 CPU使用百分比 CPU > 阈值
平均RT 入口平均响应时间 RT > 阈值
并发线程数 入口并发线程数 线程数 > 阈值
入口QPS 入口QPS QPS > 阈值

3.7 代码示例详解

方式一:注解方式(推荐)

java 复制代码
@SentinelResource(
    value = "getUser",                              // 资源名
    blockHandler = "getUserBlockHandler",           // 限流/熔断时的处理
    blockHandlerClass = UserBlockHandler.class,     // 处理类(方法需static)
    fallback = "getUserFallback",                   // 业务异常时的降级
    fallbackClass = UserFallback.class,             // 降级类
    exceptionsToIgnore = {IllegalArgumentException.class}  // 忽略的异常
)
public User getUser(Long id) {
    return userService.getById(id);
}

// 限流/熔断处理方法(必须是static,参数一致+BlockException)
public class UserBlockHandler {
    public static User getUserBlockHandler(Long id, BlockException ex) {
        log.warn("用户{}:请求被限流", id);
        return new User(-1L, "限流中,请稍后重试");
    }
}

// 业务异常降级方法(必须是static,参数一致+Throwable)
public class UserFallback {
    public static User getUserFallback(Long id, Throwable ex) {
        log.error("用户{}查询异常: {}", id, ex.getMessage());
        return new User(-1L, "服务异常,返回默认用户");
    }
}

方式二:整合OpenFeign

java 复制代码
// 1. 开启Feign对Sentinel的支持
feign:
  sentinel:
    enabled: true

// 2. 定义Feign客户端
@FeignClient(
    name = "user-service",
    fallbackFactory = UserClientFallbackFactory.class
)
public interface UserClient {
    @GetMapping("/users/{id}")
    User getUser(@PathVariable Long id);
    
    @PostMapping("/users")
    User createUser(@RequestBody User user);
}

// 3. 实现FallbackFactory(可获取异常信息)
@Component
@Slf4j
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable cause) {
        log.error("调用user-service失败: {}", cause.getMessage());
        return new UserClient() {
            @Override
            public User getUser(Long id) {
                return new User(-1L, "降级用户-查询失败");
            }
            
            @Override
            public User createUser(User user) {
                throw new RuntimeException("创建用户服务不可用");
            }
        };
    }
}

3.8 规则持久化

默认规则存储在内存中,重启丢失。生产环境需要持久化到Nacos。

yaml 复制代码
spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080
      # 配置规则持久化到Nacos
      datasource:
        # 流控规则
        flow:
          nacos:
            server-addr: localhost:8848
            data-id: ${spring.application.name}-flow-rules
            group-id: SENTINEL_GROUP
            data-type: json
            rule-type: flow
        # 熔断规则
        degrade:
          nacos:
            server-addr: localhost:8848
            data-id: ${spring.application.name}-degrade-rules
            group-id: SENTINEL_GROUP
            data-type: json
            rule-type: degrade

Nacos中的规则配置示例

json 复制代码
[
  {
    "resource": "getUser",
    "limitApp": "default",
    "grade": 1,
    "count": 100,
    "strategy": 0,
    "controlBehavior": 0,
    "clusterMode": false
  }
]

3.9 核心配置汇总

yaml 复制代码
spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080       # 控制台地址
        port: 8719                      # 与控制台通信端口
      eager: true                       # 饥饿加载,启动时就连接控制台
      web-context-unify: false          # 关闭上下文整合,支持链路模式
      filter:
        enabled: true                   # 开启web过滤器
        url-patterns: /**               # 拦截路径

# Feign整合Sentinel
feign:
  sentinel:
    enabled: true

四、Gateway 详解(服务网关)

4.1 是什么

Spring Cloud Gateway是Spring官方推出的API网关,基于WebFlux实现,具有高性能、支持异步非阻塞的特点,是微服务架构的统一入口。

4.2 核心概念

复制代码
                         ┌─────────────────────────────────────┐
                         │            Gateway                  │
外部请求 ──────────────▶ │  ┌─────────┐    ┌─────────┐         │
                         │  │Predicate│ ─▶│ Filter  │         │
                         │  │ 断言    │    │ 过滤器   │         │
                         │  └─────────┘    └─────────┘         │
                         │        │             │              │
                         │        ▼             ▼              │
                         │  ┌─────────────────────────┐        │
                         │  │        Route 路由       │        │
                         │  │  id + uri + predicates  │        │
                         │  │      + filters          │        │
                         │  └─────────────────────────┘        │
                         └─────────────────────────────────────┘
                                          │
                                          ▼
                                   后端微服务
概念 说明 示例
Route 路由,网关基本构建块 id, uri, predicates, filters
Predicate 断言,匹配请求条件 Path, Method, Header, Query
Filter 过滤器,修改请求/响应 AddHeader, StripPrefix, 限流

4.3 路由配置

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        # 用户服务路由
        - id: user-service
          uri: lb://user-service        # lb表示负载均衡
          predicates:
            - Path=/api/user/**         # 路径匹配
          filters:
            - StripPrefix=1             # 去掉第一层路径 /api

        # 订单服务路由
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/order/**
            - Method=GET,POST           # 请求方法匹配
          filters:
            - AddRequestHeader=X-Source, gateway  # 添加请求头

        # 基于时间的路由(活动页面)
        - id: activity-service
          uri: lb://activity-service
          predicates:
            - Path=/activity/**
            - After=2024-06-18T00:00:00.000+08:00  # 指定时间后生效

4.4 常用Predicate

Predicate 说明 示例
Path 路径匹配 Path=/api/**
Method 请求方法 Method=GET,POST
Header 请求头 Header=X-Token, \d+
Query 请求参数 Query=name, jack
Cookie Cookie值 Cookie=sessionId, abc
After/Before 时间条件 After=2024-01-01T00:00:00Z
Weight 权重路由 Weight=group1, 8

4.5 常用Filter

yaml 复制代码
filters:
  # 路径相关
  - StripPrefix=1                    # 去掉前缀
  - PrefixPath=/api                  # 添加前缀
  - RewritePath=/old/(?.*)/, /new/$\{segment}  # 重写路径
  
  # 请求头相关
  - AddRequestHeader=X-Request-Id, 123
  - RemoveRequestHeader=Cookie
  - SetRequestHeader=Host, newhost.com
  
  # 响应头相关
  - AddResponseHeader=X-Response-Time, 100
  - RemoveResponseHeader=Server
  
  # 限流
  - name: RequestRateLimiter
    args:
      redis-rate-limiter.replenishRate: 10   # 每秒放入令牌数
      redis-rate-limiter.burstCapacity: 20   # 令牌桶容量
      key-resolver: "#{@userKeyResolver}"    # 限流维度

4.6 自定义全局过滤器

java 复制代码
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        
        // 1. 获取Token
        String token = request.getHeaders().getFirst("Authorization");
        
        // 2. 校验Token
        if (token == null || !token.startsWith("Bearer ")) {
            // 返回401
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        
        // 3. 解析Token,传递用户信息到下游
        String userId = parseToken(token);
        ServerHttpRequest newRequest = request.mutate()
                .header("X-User-Id", userId)
                .build();
        
        return chain.filter(exchange.mutate().request(newRequest).build());
    }

    @Override
    public int getOrder() {
        return -100;  // 数值越小,优先级越高
    }
}

4.7 跨域配置

yaml 复制代码
spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods:
              - GET
              - POST
              - PUT
              - DELETE
            allowedHeaders: "*"
            allowCredentials: true
            maxAge: 3600

4.8 请求处理流程详解

复制代码
┌─────────────────────────────────────────────────────────────────────────┐
│                     Gateway 请求处理完整流程                             │
└─────────────────────────────────────────────────────────────────────────┘

外部请求
    │
    ▼
┌────────────────────┐
│ 1.HttpWebHandler   │  接收HTTP请求,转换为ServerWebExchange
└─────────┬──────────┘
          │
          ▼
┌────────────────────┐
│ 2.DispatcherHandler│  WebFlux入口,分发请求
└─────────┬──────────┘
          │
          ▼
┌────────────────────────┐
│ 3.RoutePredicateHandler│  路由断言匹配
│    - 遍历所有Route      │
│    - 执行Predicate      │
│    - 找到匹配的路由      │
└─────────┬──────────────┘
          │
          ▼
┌──────────────────────┐
│ 4.FilteringWebHandler│  过滤器链执行
│    ┌──────────────┐  │
│    │Global Filter │  │  全局过滤器(鉴权、日志等)
│    └──────────────┘  │
│    ┌──────────────┐  │
│    │Gateway Filter│  │  网关过滤器(路由级别)
│    └──────────────┘  │
└─────────┬────────────┘
          │
          ▼
┌─────────────────────┐
│5. NettyRoutingFilter│  代理请求到下游服务
│    - 负载均衡选择实例│
│    - 建立连接发送请求│
└─────────┬──────────┘
          │
          ▼
┌────────────────────┐
│ 6. Response Filter │  响应过滤器(逆序执行)
└─────────┬──────────┘
          │
          ▼
     返回响应

4.9 自定义路由过滤器(GatewayFilter)

java 复制代码
// 自定义路由过滤器 - 记录请求耗时
@Component
public class RequestTimeGatewayFilterFactory extends AbstractGatewayFilterFactory<RequestTimeGatewayFilterFactory.Config> {

    private static final String START_TIME = "startTime";
    private static final Logger log = LoggerFactory.getLogger(RequestTimeGatewayFilterFactory.class);

    public RequestTimeGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            // pre过滤:记录开始时间
            exchange.getAttributes().put(START_TIME, System.currentTimeMillis());
            
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                // post过滤:计算耗时
                Long startTime = exchange.getAttribute(START_TIME);
                if (startTime != null) {
                    long duration = System.currentTimeMillis() - startTime;
                    String path = exchange.getRequest().getURI().getPath();
                    
                    if (config.isEnabled()) {
                        log.info("[{}] {} 耗时: {}ms", 
                            exchange.getRequest().getMethod(),
                            path,
                            duration);
                    }
                    
                    // 添加响应头
                    exchange.getResponse().getHeaders().add("X-Response-Time", duration + "ms");
                }
            }));
        };
    }

    @Data
    public static class Config {
        private boolean enabled = true;
    }
}

// 配置使用
// routes:
//   - id: user-service
//     uri: lb://user-service
//     predicates:
//       - Path=/api/user/**
//     filters:
//       - RequestTime=true

4.10 限流配置详解(基于Redis)

java 复制代码
// 1. 自定义限流Key解析器
@Configuration
public class RateLimiterConfig {

    // 基于IP限流
    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(
            exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
        );
    }

    // 基于用户ID限流
    @Bean
    public KeyResolver userKeyResolver() {
        return exchange -> Mono.just(
            exchange.getRequest().getHeaders().getFirst("X-User-Id")
        );
    }

    // 基于接口路径限流
    @Bean
    public KeyResolver pathKeyResolver() {
        return exchange -> Mono.just(
            exchange.getRequest().getPath().value()
        );
    }
}
yaml 复制代码
# 限流配置详解
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
          filters:
            - name: RequestRateLimiter
              args:
                # 令牌桶每秒填充速率(允许的稳定请求速率)
                redis-rate-limiter.replenishRate: 10
                # 令牌桶最大容量(允许的突发流量)
                redis-rate-limiter.burstCapacity: 20
                # 每个请求消耗的令牌数
                redis-rate-limiter.requestedTokens: 1
                # 限流Key解析器
                key-resolver: "#{@ipKeyResolver}"
  redis:
    host: localhost
    port: 6379

限流原理说明

  • replenishRate=10:每秒往令牌桶放入10个令牌
  • burstCapacity=20:桶最多容纳20个令牌
  • 正常情况:每秒最多处理10个请求
  • 突发情况:瞬时可处理20个请求(消耗桶内存量)

4.11 灰度发布(金丝雀发布)

yaml 复制代码
# 基于Header的灰度路由
spring:
  cloud:
    gateway:
      routes:
        # 灰度版本路由(优先级高)
        - id: user-service-gray
          uri: lb://user-service-gray
          predicates:
            - Path=/api/user/**
            - Header=X-Gray, true           # 灰度标识
          order: 0                          # 数字越小优先级越高
          
        # 正式版本路由
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
          order: 1

---
# 基于权重的灰度路由
spring:
  cloud:
    gateway:
      routes:
        # 90%流量到正式版本
        - id: user-service-prod
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
            - Weight=user-group, 90
            
        # 10%流量到灰度版本
        - id: user-service-gray
          uri: lb://user-service-gray
          predicates:
            - Path=/api/user/**
            - Weight=user-group, 10

4.12 动态路由(从Nacos加载)

java 复制代码
@Component
@Slf4j
public class DynamicRouteLoader implements ApplicationEventPublisherAware {

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;
    
    private ApplicationEventPublisher publisher;

    // 监听Nacos配置变更
    @NacosConfigListener(dataId = "gateway-routes", groupId = "GATEWAY_GROUP")
    public void onRouteChange(String config) {
        log.info("路由配置变更: {}", config);
        
        List<RouteDefinition> routes = JSON.parseArray(config, RouteDefinition.class);
        
        // 删除旧路由
        // 添加新路由
        routes.forEach(route -> {
            routeDefinitionWriter.save(Mono.just(route)).subscribe();
        });
        
        // 发布刷新事件
        publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }
}

4.13 核心配置汇总

yaml 复制代码
spring:
  cloud:
    gateway:
      # 全局默认过滤器
      default-filters:
        - DedupeResponseHeader=Access-Control-Allow-Origin
        - AddResponseHeader=X-Gateway-Version, 1.0.0
        
      # 服务发现路由(自动从注册中心创建路由)
      discovery:
        locator:
          enabled: true                    # 开启服务发现路由
          lower-case-service-id: true      # 服务名小写
          
      # 全局超时配置
      httpclient:
        connect-timeout: 5000              # 连接超时5秒
        response-timeout: 10000            # 响应超时10秒
        pool:
          type: ELASTIC                    # 连接池类型
          max-connections: 1000            # 最大连接数
          acquire-timeout: 5000            # 获取连接超时
          
      # 重试配置
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
          filters:
            - name: Retry
              args:
                retries: 3                 # 重试次数
                statuses: BAD_GATEWAY,SERVICE_UNAVAILABLE
                methods: GET,POST
                backoff:
                  firstBackoff: 100ms      # 首次重试间隔
                  maxBackoff: 500ms        # 最大重试间隔
                  factor: 2                # 间隔倍数

五、OpenFeign 详解(声明式服务调用)

5.1 是什么

OpenFeign是Spring Cloud的声明式HTTP客户端,通过注解方式定义接口,自动生成HTTP调用代码,简化服务间调用。

5.2 工作原理

复制代码
┌────────────────────────────────────────────────────────────────┐
│                      OpenFeign 工作流程                         │
├────────────────────────────────────────────────────────────────┤
│  1. @FeignClient 标注接口                                       │
│                    ↓                                            │
│  2. 启动时扫描接口,生成动态代理                                  │
│                    ↓                                            │
│  3. 调用方法时,解析注解(@GetMapping、@PathVariable等)          │
│                    ↓                                            │
│  4. 从注册中心获取服务实例列表                                    │
│                    ↓                                            │
│  5. 负载均衡选择实例                                             │
│                    ↓                                            │
│  6. 构造HTTP请求并发送                                           │
│                    ↓                                            │
│  7. 反序列化响应结果                                             │
└────────────────────────────────────────────────────────────────┘

5.3 基本使用

java 复制代码
// 1. 启用Feign客户端
@SpringBootApplication
@EnableFeignClients
public class OrderApplication { }

// 2. 定义Feign接口
@FeignClient(name = "user-service")  // 服务名
public interface UserClient {
    
    @GetMapping("/users/{id}")
    User getUser(@PathVariable("id") Long id);
    
    @PostMapping("/users")
    User createUser(@RequestBody User user);
    
    @GetMapping("/users")
    List<User> listUsers(@RequestParam("name") String name);
}

// 3. 注入使用
@Service
public class OrderService {
    
    @Autowired
    private UserClient userClient;
    
    public Order createOrder(Long userId) {
        // 像调用本地方法一样调用远程服务
        User user = userClient.getUser(userId);
        // ...
    }
}

5.4 核心配置

yaml 复制代码
feign:
  client:
    config:
      default:  # 全局配置
        connectTimeout: 5000        # 连接超时
        readTimeout: 5000           # 读取超时
        loggerLevel: BASIC          # 日志级别
      user-service:  # 针对特定服务配置
        connectTimeout: 3000
        readTimeout: 3000

  # 开启压缩
  compression:
    request:
      enabled: true
      mime-types: application/json
      min-request-size: 2048
    response:
      enabled: true

  # 整合Sentinel
  sentinel:
    enabled: true

5.5 日志级别

级别 说明
NONE 不记录任何日志(默认)
BASIC 记录请求方法、URL、响应状态码、执行时间
HEADERS BASIC + 请求头和响应头
FULL 全部信息,包括请求体和响应体
java 复制代码
@Configuration
public class FeignConfig {
    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

5.6 服务降级

java 复制代码
// 方式一:fallback
@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient {
    @GetMapping("/users/{id}")
    User getUser(@PathVariable Long id);
}

@Component
public class UserClientFallback implements UserClient {
    @Override
    public User getUser(Long id) {
        return new User(-1L, "降级用户");
    }
}

// 方式二:fallbackFactory(可获取异常信息)
@FeignClient(name = "user-service", fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {
    @GetMapping("/users/{id}")
    User getUser(@PathVariable Long id);
}

@Component
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable cause) {
        return new UserClient() {
            @Override
            public User getUser(Long id) {
                log.error("调用user-service失败: {}", cause.getMessage());
                return new User(-1L, "服务暂不可用");
            }
        };
    }
}

5.7 底层原理详解

复制代码
┌─────────────────────────────────────────────────────────────────────────┐
│                     OpenFeign 核心组件架构                               │
└─────────────────────────────────────────────────────────────────────────┘

┌──────────────────┐
│  @FeignClient接口│
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│ ReflectiveFeign  │  动态代理生成器
│  (JDK Proxy)     │  根据接口生成代理对象
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│ FeignInvocation  │  方法调用处理器
│   Handler        │  将方法调用转换为HTTP请求
└────────┬─────────┘
         │
         ▼
┌─────────────────────────────────────────────┐
│              拦截器链                        │
│ ┌──────────────────┐  ┌───────────────────┐ │
│ │RequestInterceptor│  │ResponseInterceptor│ │
│ └──────────────────┘  └───────────────────┘ │
└─────────────────────┬───────────────────────┘
                      │
                      ▼
┌───────────────────────────────────────────┐
│             负载均衡层                     │
│  ┌─────────────────────────────────────┐  │
│  │   LoadBalancerFeignClient           │  │
│  │   ↓                                 │  │
│  │   从Nacos获取服务实例列表             │  │
│  │   ↓                                 │  │
│  │   负载均衡策略选择实例                │  │
│  └─────────────────────────────────────┘  │
└─────────────────────┬─────────────────────┘
                      │
                      ▼
┌────────────────────────────────────────────┐
│              HTTP客户端层                   │
│  ┌─────────────┐ ┌─────────┐ ┌───────────┐ │
│  │URLConnection│ │  OkHttp │ │Apache Http│ │
│  │   (默认)    │  │(推荐)   │ │  Client   │ │
│  └─────────────┘ └─────────┘ └───────────┘ │
└────────────────────────────────────────────┘

5.8 HTTP客户端优化(连接池配置)

默认的URLConnection不支持连接池,每次请求都新建连接,性能差。推荐使用OkHttp或Apache HttpClient。

方式一:OkHttp(推荐)

xml 复制代码
<!-- 1. 添加依赖 -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>
yaml 复制代码
# 2. 配置启用
feign:
  okhttp:
    enabled: true
  httpclient:
    enabled: false
java 复制代码
// 3. 自定义OkHttp配置(可选)
@Configuration
@ConditionalOnClass(OkHttpClient.class)
public class OkHttpConfig {

    @Bean
    public OkHttpClient okHttpClient() {
        return new OkHttpClient.Builder()
            .connectTimeout(5, TimeUnit.SECONDS)      // 连接超时
            .readTimeout(10, TimeUnit.SECONDS)        // 读取超时
            .writeTimeout(10, TimeUnit.SECONDS)       // 写入超时
            .retryOnConnectionFailure(true)           // 连接失败重试
            .connectionPool(new ConnectionPool(
                100,                                  // 最大空闲连接数
                5, TimeUnit.MINUTES                   // 空闲连接存活时间
            ))
            .build();
    }
}

方式二:Apache HttpClient

xml 复制代码
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>
yaml 复制代码
feign:
  httpclient:
    enabled: true
    max-connections: 200           # 最大连接数
    max-connections-per-route: 50  # 单路由最大连接
    connection-timeout: 5000       # 连接超时
    time-to-live: 60               # 连接存活时间(秒)
  okhttp:
    enabled: false

5.9 请求拦截器详解

java 复制代码
// 全局请求拦截器 - 传递Token、链路追踪等信息
@Component
public class FeignRequestInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate template) {
        // 1. 传递认证Token
        ServletRequestAttributes attributes = 
            (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            String token = request.getHeader("Authorization");
            if (StringUtils.hasText(token)) {
                template.header("Authorization", token);
            }
        }
        
        // 2. 传递链路追踪ID
        String traceId = MDC.get("traceId");
        if (StringUtils.hasText(traceId)) {
            template.header("X-Trace-Id", traceId);
        }
        
        // 3. 传递其他自定义头
        template.header("X-Request-Source", "feign-client");
    }
}

重要注意:异步场景下RequestContextHolder可能为null

java 复制代码
// 解决异步调用时无法获取Request的问题
@Configuration
public class FeignAsyncConfig {

    @Bean
    public Executor feignAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setQueueCapacity(100);
        // 关键:使用装饰器传递上下文
        executor.setTaskDecorator(runnable -> {
            RequestAttributes context = RequestContextHolder.getRequestAttributes();
            return () -> {
                try {
                    RequestContextHolder.setRequestAttributes(context);
                    runnable.run();
                } finally {
                    RequestContextHolder.resetRequestAttributes();
                }
            };
        });
        executor.initialize();
        return executor;
    }
}

5.10 超时与重试机制

yaml 复制代码
feign:
  client:
    config:
      default:
        connectTimeout: 5000         # 连接超时
        readTimeout: 10000           # 读取超时
      # 针对特定服务配置(覆盖默认)
      user-service:
        connectTimeout: 3000
        readTimeout: 5000

spring:
  cloud:
    loadbalancer:
      retry:
        enabled: true                # 开启重试
      clients:
        user-service:
          retry:
            maxRetriesOnSameServiceInstance: 1    # 同一实例重试次数
            maxRetriesOnNextServiceInstance: 2    # 切换实例重试次数
            retryableStatusCodes: 500,502,503     # 可重试的状态码

超时优先级:Feign配置 > Ribbon配置 > 全局默认

5.11 常见问题排查

问题 原因 解决方案
Read timeout 读取超时 增大readTimeout或优化下游服务
Connect timeout 连接超时 检查网络或服务是否存活
No instances available 无可用实例 检查服务注册和服务名称
请求参数丢失 @PathVariable未指定value @PathVariable("id")
Body传递失败 GET请求不支持Body 改用POST或@SpringQueryMap
异步调用头信息丢失 线程上下文未传递 使用TaskDecorator传递上下文

5.12 性能优化清单

yaml 复制代码
# 最佳实践配置
feign:
  # 1. 使用连接池
  okhttp:
    enabled: true
    
  # 2. 开启压缩
  compression:
    request:
      enabled: true
      mime-types: application/json,text/xml
      min-request-size: 2048       # 超过2KB才压缩
    response:
      enabled: true
      
  # 3. 关闭日志(生产环境)
  client:
    config:
      default:
        loggerLevel: NONE          # 关闭日志提升性能
        
  # 4. 开启Sentinel熔断
  sentinel:
    enabled: true

# 5. 负载均衡优化
spring:
  cloud:
    loadbalancer:
      cache:
        enabled: true              # 启用服务列表缓存
        ttl: 35s                   # 缓存时间

六、Seata 详解(分布式事务)

6.1 是什么

Seata是阿里巴巴开源的分布式事务解决方案,提供AT、TCC、SAGA、XA四种事务模式,让分布式事务像本地事务一样简单。

6.2 核心角色

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                        Seata 架构                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   ┌─────────────┐        ┌─────────────┐        ┌─────────────┐ │
│   │     TM      │        │     TC      │        │     RM      │ │
│   │ Transaction │◀────▶│ Transaction │◀────▶ │  Resource   │ │
│   │   Manager   │        │ Coordinator │        │   Manager   │ │
│   │  事务管理器  │        │  事务协调器  │        │  资源管理器  │ │
│   └─────────────┘        └─────────────┘        └─────────────┘ │
│         │                       │                      │         │
│         │                       │                      │         │
│   定义事务边界            协调全局事务              管理分支事务    │
│   @GlobalTransactional    提交/回滚决策            本地事务执行    │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘
角色 说明
TC Transaction Coordinator,事务协调器,维护全局和分支事务状态
TM Transaction Manager,事务管理器,定义全局事务范围
RM Resource Manager,资源管理器,管理分支事务的资源

6.3 AT模式原理(推荐)

AT模式是Seata默认模式,无代码侵入,基于两阶段提交:

复制代码
┌────────────────────────────────────────────────────────────────┐
│                       AT模式 - 一阶段                           │
├────────────────────────────────────────────────────────────────┤
│  1. 解析SQL,获取表名、条件等信息                                │
│                    ↓                                            │
│  2. 查询前镜像(before image)- 修改前的数据                     │
│                    ↓                                            │
│  3. 执行业务SQL                                                 │
│                    ↓                                            │
│  4. 查询后镜像(after image)- 修改后的数据                      │
│                    ↓                                            │
│  5. 生成undo log,插入UNDO_LOG表                                │
│                    ↓                                            │
│  6. 注册分支事务,获取全局锁                                     │
│                    ↓                                            │
│  7. 提交本地事务                                                │
└────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────┐
│                       AT模式 - 二阶段                           │
├────────────────────────────────────────────────────────────────┤
│  【提交】                                                       │
│     - 删除undo log                                              │
│     - 释放全局锁                                                │
│     - 异步批量处理,性能高                                       │
│                                                                 │
│  【回滚】                                                       │
│     - 根据before image生成反向SQL                               │
│     - 执行反向SQL恢复数据                                        │
│     - 删除undo log                                              │
│     - 释放全局锁                                                │
└────────────────────────────────────────────────────────────────┘

6.4 使用示例

java 复制代码
// 1. 在事务发起方添加 @GlobalTransactional
@Service
public class OrderService {
    
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private StockClient stockClient;   // Feign客户端
    @Autowired
    private AccountClient accountClient;
    
    @GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
    public void createOrder(OrderDTO dto) {
        // 1. 创建订单
        Order order = new Order();
        order.setUserId(dto.getUserId());
        order.setProductId(dto.getProductId());
        order.setAmount(dto.getAmount());
        orderMapper.insert(order);
        
        // 2. 扣减库存(远程调用)
        stockClient.deduct(dto.getProductId(), dto.getCount());
        
        // 3. 扣减余额(远程调用)
        accountClient.deduct(dto.getUserId(), dto.getAmount());
        
        // 任意一步失败,全局回滚
    }
}

6.5 核心配置

yaml 复制代码
seata:
  enabled: true
  application-id: order-service
  tx-service-group: my_tx_group  # 事务组名称
  
  service:
    vgroup-mapping:
      my_tx_group: default       # 事务组映射到TC集群
      
  registry:
    type: nacos
    nacos:
      server-addr: localhost:8848
      namespace: ""
      group: SEATA_GROUP
      
  config:
    type: nacos
    nacos:
      server-addr: localhost:8848
      namespace: ""
      group: SEATA_GROUP

6.6 四种事务模式对比

模式 原理 优点 缺点 适用场景
AT 自动补偿 无侵入、简单 需要数据库支持 大多数场景
TCC Try-Confirm-Cancel 性能高 代码侵入大 资金类高一致性
SAGA 长事务补偿 适合长流程 隔离性差 业务流程长
XA 两阶段提交 强一致 性能差、锁时间长 传统数据库

6.7 TCC模式详解

TCC(Try-Confirm-Cancel)是一种业务层面的两阶段提交,适用于高性能、高一致性要求的场景。

复制代码
┌─────────────────────────────────────────────────────────────────────────┐
│                        TCC 三个阶段                                     │
└─────────────────────────────────────────────────────────────────────────┘

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│       Try       │     │     Confirm     │     │     Cancel      │
│     尝试阶段     │     │     确认阶段     │     │     取消阶段    │
├─────────────────┤     ├─────────────────┤      ├─────────────────┤
│ - 检查业务可行性  │    │ - 真正执行业务   │      │ - 释放预留资源   │
│ - 预留资源       │ ──▶│ - 提交预留资源   │  or  │ - 回滚业务操作   │
│ - 不实际执行     │     │ - 幂等性保证     │      │ - 幂等性保证     │
└─────────────────┘     └─────────────────┘      └─────────────────┘

【示例:转账场景】
Try:     冻结A账户100元,不实际扣款
Confirm: 扣除A已冻结的100元,加到B账户
Cancel:  解冻结A账户的100元
java 复制代码
// TCC模式代码实现
@LocalTCC
public interface AccountTccService {

    /**
     * Try - 冻结金额
     */
    @TwoPhaseBusinessAction(
        name = "deductTcc",
        commitMethod = "confirm",
        rollbackMethod = "cancel"
    )
    boolean tryDeduct(
        @BusinessActionContextParameter(paramName = "userId") Long userId,
        @BusinessActionContextParameter(paramName = "amount") BigDecimal amount
    );

    /**
     * Confirm - 确认扣款
     */
    boolean confirm(BusinessActionContext context);

    /**
     * Cancel - 取消冻结
     */
    boolean cancel(BusinessActionContext context);
}

@Service
@Slf4j
public class AccountTccServiceImpl implements AccountTccService {

    @Autowired
    private AccountMapper accountMapper;

    @Override
    @Transactional
    public boolean tryDeduct(Long userId, BigDecimal amount) {
        log.info("Try: 冻结用户{}金额{}", userId, amount);
        
        // 1. 检查余额是否充足
        Account account = accountMapper.selectById(userId);
        if (account.getBalance().compareTo(amount) < 0) {
            throw new RuntimeException("余额不足");
        }
        
        // 2. 冻结金额(不实际扣款)
        accountMapper.freezeAmount(userId, amount);
        
        return true;
    }

    @Override
    @Transactional
    public boolean confirm(BusinessActionContext context) {
        Long userId = (Long) context.getActionContext("userId");
        BigDecimal amount = (BigDecimal) context.getActionContext("amount");
        
        log.info("Confirm: 确认扣款用户{}金额{}", userId, amount);
        
        // 幂等性检查:确认资源是否已处理
        if (isAlreadyConfirmed(context.getXid())) {
            return true;
        }
        
        // 真正扣款:将冻结金额转为实际扣款
        accountMapper.confirmDeduct(userId, amount);
        
        return true;
    }

    @Override
    @Transactional
    public boolean cancel(BusinessActionContext context) {
        Long userId = (Long) context.getActionContext("userId");
        BigDecimal amount = (BigDecimal) context.getActionContext("amount");
        
        log.info("Cancel: 取消冻结用户{}金额{}", userId, amount);
        
        // 幂等性检查
        if (isAlreadyCancelled(context.getXid())) {
            return true;
        }
        
        // 空回滚检查:Try未执行成功,Cancel不需要执行
        if (!isTrySuccess(context.getXid())) {
            return true;
        }
        
        // 解冻金额
        accountMapper.unfreezeAmount(userId, amount);
        
        return true;
    }
}

TCC三大难点及解决方案

问题 说明 解决方案
幂等性 Confirm/Cancel可能被重复调用 通过xid+分支事务状态记录判断
空回滚 Cancel在Try之前执行 检查Try是否执行过,未执行则直接返回
悬挂 Try在Cancel之后执行 检查是否已回滚,已回滚则Try不执行

6.8 UNDO_LOG表结构

AT模式需要在每个业务数据库创建undo_log表。

sql 复制代码
-- 每个业务库必须创建此表
CREATE TABLE `undo_log` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `branch_id` BIGINT(20) NOT NULL COMMENT '分支事务ID',
  `xid` VARCHAR(100) NOT NULL COMMENT '全局事务ID',
  `context` VARCHAR(128) NOT NULL COMMENT '上下文',
  `rollback_info` LONGBLOB NOT NULL COMMENT '回滚信息(前后镜像)',
  `log_status` INT(11) NOT NULL COMMENT '日志状态 0-正常 1-全局已完成',
  `log_created` DATETIME NOT NULL COMMENT '创建时间',
  `log_modified` DATETIME NOT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='AT模式undo日志表';

rollback_info字段内容示例

json 复制代码
{
  "branchId": 641789253,
  "xid": "192.168.1.1:8091:641789253",
  "undoItems": [{
    "afterImage": {
      "rows": [{"fields": [{"name": "id", "value": 1}, {"name": "balance", "value": 800}]}],
      "tableName": "account"
    },
    "beforeImage": {
      "rows": [{"fields": [{"name": "id", "value": 1}, {"name": "balance", "value": 1000}]}],
      "tableName": "account"
    },
    "sqlType": "UPDATE"
  }]
}

6.9 全局锁机制详解

复制代码
┌─────────────────────────────────────────────────────────────────────────┐
│                      Seata 全局锁机制                                   │
└─────────────────────────────────────────────────────────────────────────┘

【问题场景】
事务A在修改记录X,还未提交
事务B也要修改记录X(不在Seata管理下)
如果事务B先提交,事务A回滚时会覆盖事务B的数据 -> 脏写

【解决方案】全局锁(Global Lock)

┌────────────────────┐              ┌────────────────────┐
│     TC Server      │              │    lock_table      │
├────────────────────┤              ├────────────────────┤
│                    │              │ xid: 192.168.1:8091│
│   维护全局锁表      │  ◀───────── │ table: account     │
│                    │              │ pk: 1              │
│   冲突检测          │              │ branch_id: 641789  │
│                    │              └────────────────────┘
└────────────────────┘

【全局锁工作流程】
1. 一阶段本地事务提交前,向TC注册分支事务,申请全局锁
2. TC检查该记录是否已被锁定
3. 未被锁定:获取锁成功,本地事务提交
4. 已被锁定:等待重试,超时则回滚
5. 二阶段全局提交/回滚后释放全局锁
sql 复制代码
-- TC Server端锁表结构
CREATE TABLE `lock_table` (
  `row_key` VARCHAR(128) NOT NULL COMMENT '行键:表名+主键值',
  `xid` VARCHAR(96) NOT NULL COMMENT '全局事务ID',
  `transaction_id` BIGINT NOT NULL COMMENT '事务ID',
  `branch_id` BIGINT NOT NULL COMMENT '分支事务ID',
  `resource_id` VARCHAR(256) NOT NULL COMMENT '资源ID',
  `table_name` VARCHAR(32) NOT NULL COMMENT '表名',
  `pk` VARCHAR(36) NOT NULL COMMENT '主键值',
  `gmt_create` DATETIME NOT NULL COMMENT '创建时间',
  `gmt_modified` DATETIME NOT NULL COMMENT '修改时间',
  PRIMARY KEY (`row_key`),
  KEY `idx_branch_id` (`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

6.10 TC Server部署

yaml 复制代码
# seata-server配置(file.conf或registry.conf)
store:
  mode: db                          # 存储模式:file/db/redis
  db:
    datasource: druid
    db-type: mysql
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true
    user: root
    password: root
    min-conn: 5
    max-conn: 100

registry:
  type: nacos
  nacos:
    server-addr: 127.0.0.1:8848
    namespace: ""
    group: SEATA_GROUP
    application: seata-server

config:
  type: nacos
  nacos:
    server-addr: 127.0.0.1:8848
    namespace: ""
    group: SEATA_GROUP
sql 复制代码
-- TC Server数据库表(seata库)
-- 全局事务表
CREATE TABLE `global_table` (
  `xid` VARCHAR(128) NOT NULL,
  `transaction_id` BIGINT,
  `status` TINYINT NOT NULL,
  `application_id` VARCHAR(32),
  `transaction_service_group` VARCHAR(32),
  `transaction_name` VARCHAR(128),
  `timeout` INT,
  `begin_time` BIGINT,
  `application_data` VARCHAR(2000),
  `gmt_create` DATETIME,
  `gmt_modified` DATETIME,
  PRIMARY KEY (`xid`),
  KEY `idx_gmt_modified_status` (`gmt_modified`,`status`),
  KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 分支事务表
CREATE TABLE `branch_table` (
  `branch_id` BIGINT NOT NULL,
  `xid` VARCHAR(128) NOT NULL,
  `transaction_id` BIGINT,
  `resource_group_id` VARCHAR(32),
  `resource_id` VARCHAR(256),
  `branch_type` VARCHAR(8),
  `status` TINYINT,
  `client_id` VARCHAR(64),
  `application_data` VARCHAR(2000),
  `gmt_create` DATETIME(6),
  `gmt_modified` DATETIME(6),
  PRIMARY KEY (`branch_id`),
  KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

6.11 常见问题与解决方案

问题 原因 解决方案
no available service TC Server未注册到Nacos 检查TC配置和网络
Global lock acquire failed 其他事务持有锁 增大超时时间或优化业务
undo_log不存在 未创建undo_log表 在业务库创建undo_log表
回滚失败 数据被篡改 检查是否有绕过Seata的修改
事务超时 业务执行太长 调整timeout配置
性能差 全局锁竞争激烈 优化业务粒度,减少热点数据

6.12 性能优化建议

yaml 复制代码
seata:
  # 1. 异步提交(提升性能)
  client:
    rm:
      async-commit-buffer-limit: 10000  # 异步提交队列大小
      report-retry-count: 5             # 一阶段上报TC重试次数
    tm:
      commit-retry-count: 5             # 全局提交重试次数
      rollback-retry-count: 5           # 全局回滚重试次数
      
  # 2. 服务端优化
server:
  undo:
    log-save-days: 7                    # undo_log保留天数
    log-delete-period: 86400000         # 清理周期
  recovery:
    committing-retry-period: 1000       # 提交重试周期
    async-committing-retry-period: 1000
    rollbacking-retry-period: 1000
    timeout-retry-period: 1000

优化要点

  1. 减小事务范围:只包含必要的跨服务调用
  2. 避免热点数据:设计时尽量分散数据访问
  3. 选择合适模式:资金类用TCC,其他场景用AT
  4. TC Server集群:生产环境部署多个TC Server

七、组件协作全景图

复制代码
                                    ┌──────────────────┐
                                    │   Nacos          │
                                    │  注册中心+配置中心│
                                    └────────┬─────────┘
                                             │
                           ┌─────────────────┼─────────────────┐
                           │                 │                 │
                           ▼                 ▼                 ▼
┌─────────┐         ┌─────────────┐   ┌─────────────┐   ┌─────────────┐
│  用户   │ ──────▶ │   Gateway   │   │ User-Service│   │Order-Service│
│ Client  │         │    网关     │   │   用户服务   │   │   订单服务   │
└─────────┘         └──────┬──────┘   └──────┬──────┘   └──────┬──────┘
                           │                 │                 │
                           │    ┌────────────┴────────────┐    │
                           │    │        OpenFeign        │    │
                           │    │       服务间调用         │    │
                           │    └────────────┬────────────┘    │
                           │                 │                 │
                           ▼                 ▼                 ▼
                    ┌─────────────────────────────────────────────┐
                    │               Sentinel                      │
                    │          限流、熔断、降级                    │
                    └─────────────────────────────────────────────┘
                                             │
                                             ▼
                    ┌─────────────────────────────────────────────┐
                    │                 Seata                       │
                    │              分布式事务                      │
                    └─────────────────────────────────────────────┘

请求流程

  1. 用户请求到达Gateway网关
  2. 网关从Nacos获取服务列表,路由到对应服务
  3. 服务间通过OpenFeign调用,自动负载均衡
  4. Sentinel对请求进行限流、熔断保护
  5. 跨服务事务由Seata保证一致性
  6. 配置变更通过Nacos配置中心动态推送
相关推荐
qq_3829492220 小时前
推荐:《Spring Cloud Alibaba 微服务架构实战课》—— 从零到一构建企业级微服务系统
微服务·云原生·架构
JAVA社区1 天前
Java高级全套教程(十一)—— Kubernetes 超详细企业级实战详解
java·运维·微服务·容器·面试·kubernetes
qq_382949221 天前
推荐一门不错的微服务实战课:Spring Cloud Alibaba 从入门到落地
微服务·云原生·架构
无聊的老谢1 天前
DDD 驱动的电信网络优化微服务建模实战
微服务·云原生·架构
IronMurphy1 天前
微服务拷打第一讲!
微服务·云原生·架构
_codemonster2 天前
30分钟快速搭建 Spring Cloud Alibaba 微服务实战(一)
微服务·架构·毕业设计·课程设计
Dongwoo Jeong2 天前
微服务架构(MSA)是如何诞生的?
微服务·云原生·架构
半旧夜夏2 天前
【保姆级】微服务组件环境搭建(Docker Compose版)
java·linux·spring cloud·微服务·云原生·容器
西凉的悲伤2 天前
Spring Boot 、Spring Cloud 微服务架构认证授权方案
spring boot·spring cloud·微服务·架构·认证授权
苏渡苇2 天前
Seata 番外篇:使用 docker-compose 部署 Seata Server(TC)及 K8S 部署 Seata 高可用
spring boot·docker·微服务·容器·kubernetes·seata·springcloud