微服务常见组件

微服务常见组件详解

一、组件总览

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配置中心动态推送
相关推荐
小小unicorn2 小时前
[微服务即时通讯系统]文件存储子服务的实现与测试
c++·redis·微服务·云原生·架构
喵叔哟2 小时前
69.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--新增功能--财务健康度
运维·微服务·.net
小小unicorn2 小时前
[微服务即时通讯系统]3.服务端-环境搭建
数据库·c++·redis·微服务·云原生·架构
九河云2 小时前
容器化与微服务:企业上云过程中的技术债务治理
大数据·微服务·云原生·重构·架构·数字化转型
吾日三省Java2 小时前
GracefulResponse:告别手动Result包装,拥抱企业级统一响应处理
java·微服务·系统架构
bug攻城狮3 小时前
Docker高级篇03:Docker微服务实战
docker·微服务·容器
Java练习两年半3 小时前
互联网大厂 Java 求职面试:探讨微服务与云原生
java·微服务·云原生·面试·技术栈
小小unicorn3 小时前
[微服务即时通讯系统]语音子服务的实现与测试
c++·算法·微服务·云原生·架构·xcode