博主头像
简单

tired

头图

微服务组件学习

组件学习

Nacos(注册中心以及配置中心)

1.安装启动

启动Nacos:到Nacos的bin目录下使用cmd打开,然后输入:startup.cmd -m standalone

image-1
image-1

访问localhost:8848/nacos:

image-2
image-2

2.导入依赖

注册:

<!-- 服务发现-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

需要打开服务发现:

@EnableDiscoveryClient//服务发现
@SpringBootApplication
public class OrderMainApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderMainApplication.class, args);
    }
}

修改配置文件(微服务的端口号,名称,nacos的地址):

server.port=8000
spring.application.name=service-order
spring.cloud.nacos.server-addr=127.0.0.1:8848

配置:

<!-- 配置中心-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

修改配置文件:

spring.config.import=nacos:service-order.properties

或者:

spring.cloud.nacos.discovery.server-addr=192.168.137.128:8848
spring.cloud.nacos.config.server-addr=192.168.137.128:8848

一个标准的配置(bootstrap.properties):

server.port=8087
spring.application.name=mall-user-service
# 具体激活的命名空间
spring.profiles.active=mall-dev
spring.cloud.nacos.config.namespace=0fced4a2-59cc-4a53-833b-222504c3c534
spring.cloud.nacos.discovery.namespace=0fced4a2-59cc-4a53-833b-222504c3c534
spring.main.allow-bean-definition-overriding=true
spring.cloud.nacos.discovery.server-addr=192.168.137.128:8848
spring.cloud.nacos.config.server-addr=192.168.137.128:8848
spring.cloud.nacos.config.prefix=${spring.application.name}
spring.cloud.nacos.config.file-extension=yaml

# 显式开启配置自动刷新
spring.cloud.nacos.config.refresh-enabled=true

# 导入两个配置文件(从右到左),如果配置文件不存在,也不会报错
spring.config.import=optional:nacos:${spring.application.name}-dev.yaml,optional:nacos:mall-common.yaml

其中的service-order.properties是在配置中心创建的配置,同样可以在其中修改对应的微服务的配置

image-3
image-3

配置刷新:

(1)使用@Value获取配置,@RefreshScope实现自动刷新

image-4
image-4

(2)@ConfigurationProperties

创建配置类,然后使用:@ConfigurationProperties(prefix = "order")//配置批量绑定在nacos下,前缀为order,无需@RefreshScope,标注在类上,最后在控制器中注入,使用。

@Component
@ConfigurationProperties(prefix = "order")//配置批量绑定在nacos下,前缀为order,无需@RefreshScope
@Data
public class OrderProperties {

    String timeout;

    String autoConfirm;

}
@Autowired
OrderProperties orderProperties;

@GetMapping("/config")
public String config(){
    return "order.timeout:"+orderProperties.getTimeout()+" order.auto-confirm:"+orderProperties.getAutoConfirm();
}

3.数据隔离

一般来说,项目有多种环境,如dev,test,prov三种环境。每种环境下,可能微服务的配置都有所不同,项目通过切换环境,就可以加载对应的环境配置。

image-5
image-5

image-6
image-6

不同的命名空间区分不同的开发环境。

不同的组区分不同的微服务。

不同的数据集对应不同开发环境下不同的微服务对应的配置。

实现配置环境的隔离。

激活对应的开发环境

spring.profiles.active=dev
spring.cloud.nacos.config.prefix=${spring.application.name}
spring.cloud.nacos.config.file-extension=yaml
spring.cloud.nacos.config.shared-configs[0].data-id=common-account.yaml

OpenFeign(远程调用)

1.导入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

启动类上开启:

@EnableFeignClients//开启feign

声明远程接口:

@FeignClient(value = "service-product")
public interface ProductFeignClient {

    @GetMapping("/product/{id}")
    Product getProductById(@PathVariable("id") Long id);

}

标注在FeignClient上,是发送这样的请求,并且发送请求是负载均衡的,因为feign整合了ribbon。

2.超时控制

image-20250529162039373
image-20250529162039373

当设置了超时控制后,在远程调用时,如果未超时就返回正确的结果,如果超时了,就会返回错误信息或者兜底数据(如果设置了兜底回调就返回兜底数据)

connectTimeout: 1000//连接超时
readTimeout: 2000//读取超时

spring:
  cloud:
    openfeign:
      client:
        config:
          default:
            connectTimeout: 1000
            readTimeout: 2000
            loggerLevel: full
          service-product:
            connectTimeout: 3000
            readTimeout: 5000
            loggerLevel: full

3.重试机制

在config中陪bean

@Bean
Retryer retryer(){
    return new Retryer.Default();
}

4.拦截器

可以拦截发送的请求,在请求发送前,对请求进行定制修改。

image-8
image-8

例如:

//请求拦截器
@Component
public class XTokenRequestInterceptor implements RequestInterceptor {

    /**
     * 发送请求之前,会执行这个方法
     * @param template 请求模板
     */
    @Override
    public void apply(RequestTemplate template) {
        System.out.println("XTokenRequestInterceptor启动");
        template.header("X-Token", UUID.randomUUID().toString());
    }

}

5.兜底回调(fallback)

导入sentinel后才能实现。

image-9
image-9

feign:
  sentinel:
    enabled: true

定义兜底回调类,在发生错误后,返回该方法设定的兜底数据:

@Component
public class ProductFeignClientFallback implements ProductFeignClient {
    @Override
    public Product getProductById(Long id) {

        System.out.println("兜底回调方法");
        Product product = new Product();
        product.setId(id);
        product.setPrice(new BigDecimal("0"));
        product.setProductName("未知商品");
        product.setNum(0);

        return product;
    }
}

Sentinel(服务熔断,服务降级,服务保护,限流)

1.启动

在目录下启动cmd窗口,输入:java -jar sentinel-dashboard-1.8.8.jar,启动成功后然后在网页输入localhost:8080启动sentinel(默认登陆账号和密码都是sentinel)

image-10
image-10

2.作用

image-11
image-11

3.导入依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

配置:

spring:
  cloud:
      sentinel:
          transport:
              dashboard: localhost:8080
          eager: true

4.使用场景

在服务方法上添加:@SentinelResource(value = "createOrder"),定义资源点,然后浏览器访问发送请求

image
image

可以对簇点添加规则,如流控规则,熔断规则等等。

例如设定了流控规则后,当快速发送请求时就会出现返回默认错误页(因为设置了QPS 阈值为1):

image
image

image
image

5.异常处理

(1) Web接口

image
image

出现异常时,使用的是默认:DefaultBlockExceptionHandler

public class DefaultBlockExceptionHandler implements BlockExceptionHandler {
    public DefaultBlockExceptionHandler() {
    }

    public void handle(HttpServletRequest request, HttpServletResponse response, String resourceName, BlockException ex) throws Exception {
        response.setStatus(429);
        PrintWriter out = response.getWriter();
        out.print("Blocked by Sentinel (flow limiting)");
        out.flush();
        out.close();
    }
}

这种方式的异常处理可以自行定义(在微服务添加exception.MyBlockExceptionHandler):

@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {

    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
                       String resourceName, BlockException e) throws Exception {
        response.setContentType("application/json;charset=utf-8");

        PrintWriter writer = response.getWriter();

        R error = R.error(500, resourceName + "被sentinel限流了,原因:" + e.getClass());

        String json = objectMapper.writeValueAsString(error);
        writer.write(json);
        writer.flush();
        writer.close();

    }
}

再次刷新页面,去sentinel控制台重新添加流控规则,最终结果:

image
image

(2)@SentinelResource

使用@SentinelResource标注的方法超过流控规则时,发生异常处理规则是:

(1)如果标注了blockHandler,则优先使用blockHandler处理,只能处理BlockException异常

(2)没有指定block,标注了fallback,就使用fallback处理,且fallback可以处理业务异常

(3)没有指定任何处理器,向上抛出异常给springboot处理(使用默认异常处理)

注解如下,标注在createOrder方法上:

@SentinelResource(value = "createOrder", blockHandler = "createOrderFallback")
public Order createOrderFallback(Long productId, Long userId, BlockException e) {

    Order order = new Order();
    order.setId(0L);
    order.setTotalAmount(BigDecimal.ZERO);
    order.setUserId(userId);
    order.setNickName("未知");
    order.setAddress("异常:"+ e.getClass());

    return order;
}

最终逻辑为:如果被@SentinelResource标注的方法没有违反流控逻辑,就调用真实逻辑,如果违反了,则调用blockHandler指定的方法返回兜底数据

image
image

(3) OpenFeign调用

如果OpenFeign远程调用失败了,有兜底回调,自然使用兜底回调,每一个远程调用最好写上兜底回调。

@FeignClient(value = "service-product" , fallback = ProductFeignClientFallback.class)
public interface ProductFeignClient {

    @GetMapping("/product/{id}")
    Product getProductById(@PathVariable("id") Long id);

}
@Component
public class ProductFeignClientFallback implements ProductFeignClient {
    @Override
    public Product getProductById(Long id) {

        System.out.println("兜底回调方法");
        Product product = new Product();
        product.setId(id);
        product.setPrice(new BigDecimal("0"));
        product.setProductName("未知商品");
        product.setNum(0);

        return product;
    }
}

image
image

(4) SphU try-catch硬编码方法

// 1.5.0 版本开始可以利用 try-with-resources 特性
// 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
try (
Entry entry = SphU.entry("resourceName")) {
        // 被保护的业务逻辑
        // do something here...
        } catch (
BlockException ex) {
        // 资源访问阻止,被限流或被降级
        // 在此处进行相应的处理操作
        }

最后,如果所有的异常处理都没有写,则最好使用一个springboot的全局处理器来处理。

6.流控规则

image
image

QPS:统计每秒请求数(一般使用QPS)

并发线程数:统计并发线程数(使用线程池来控制)

集群模式:

(1)单机均摊为5,每一个集群每秒可通过5次请求(整个集群相当于能处理15个请求每秒,假设现在集群数为3)

(2)总体阈值为5,总集群每秒5个请求(整个集群能处理5个请求每秒,假设现在集群数为3)

控制台方式限流:

image
image

也可以使用代码方式,到Nacos 中改变流控规则可以实时观察到变化:

image
image

@Service
public class TestService {
 
 @PostConstruct
    public void init(){
        initFlowRule();
    }
 
    private static void initFlowRule(){
        //流控规则集合
        List<FlowRule> rules = new ArrayList<>();
        //创建规则
        FlowRule rule = new FlowRule();
        //设置受保护的资源
        rule.setResource("sayHello");
        //设置流控规则 QPS 限流阈值类型:QPS、并发线程数
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置受保护的资源的阈值
        rule.setCount(2);
        //设置流控手段:快速失败
        rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
        rules.add(rule);
        //加载配置好的规则
        FlowRuleManager.loadRules(rules);
    }
 
    @SentinelResource(value = "sayHello")
    public String sayHello(String name) {
        return "Hello, " + name;
    }
}

持久化流控规则:

在nacos的配置管理中创建数据集,sentinel配置要使用JSON格式的数据

导入依赖:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <version>${sentinel.version}</version>
</dependency>

配置数据源,例如:

private void initFlowRules() {
    ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(
        "nacos-server:8848", "sentinel-demo", "DEFAULT_GROUP",
        source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {})
    );
    FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
}

在nacos中配置:

image
image

"nacos-server:8848":地址

"sentinel-demo":data-id

"DEFAULT_GROUP":group

Data ID命名:建议使用 {application-name}-{rule-type} 格式,如 order-service-flow-rules

三种流控模式:

image
image

  1. 直接模式(RuleConstant.STRATEGY_DIRECT

    • 默认模式,针对当前资源本身进行限流。

    例如:

    直接保护某个接口,如 /api/order 超过 100 QPS 时拒绝请求。

  2. 关联模式(RuleConstant.STRATEGY_RELATE

    • 关联另一个资源,当它达到阈值时,限制当前资源。

    例如:

    1. 如果 /api/write 的 QPS 超过 50,则 /api/read 会被限流。
    2. /api/read 本身的访问量不受影响。
  3. 链路模式(RuleConstant.STRATEGY_CHAIN

    • 只统计从某个入口资源(Entry)进入的流量,进行限流。

    例如:

    1. 只有通过 /web/entry 调用 /api/queryData 的请求会被统计。
    2. 如果直接访问 /api/queryData(如 API 网关直接调用),则不会被限流。

    或者:

    1. 通过/create (资源A)的逻辑里会去调用 createOrder (资源B),这个链路方向上不会因为链路模式的限流规则而受到影响 ,比如正常购买商品时。
    2. 通过/seckill (资源C)的代码逻辑里会触发 createOrder (资源B)相关操作 ,就会受到链路模式下对资源C的限流规则约束,比如秒杀时,这个商品只有多少件,或者说QPS不能超过50这样。

三种流控效果:

image
image

(1)快速失败
如果QPS为1,那么每秒只能通过1个请求,多的请求会被丢弃。

(2)Warm Up
当请求流量大时,有预热时间,在预热时间内的请求大约为1/3,然后逐步增加QPS至设置的值

image
image

(3) 匀速排队

  • 将突发流量整形为匀速请求,严格按间隔时间放行(如 100 QPS = 每 10ms 放行 1 次)。
  • 超时的请求会被拒绝。

image
image

7.熔断降级

image
image

无熔断规则下:每次A都会把请求发给B,出现错误后,才发送回调

有熔断规则下(更快):例如,现在五秒内发现80%的请求都是慢调用,那么开启熔断,时长为30s,那么在这30s内,都不会给B发请求了,直接调用回调

(1)慢调用比例

最大RT(response time):超过最大RT没有响应,就认为是一个慢请求
熔断时长:在这段时长内发起的请求都不会发给远程服务
比例阀值(0.0-1.0):有多少请求为慢请求
统计时长:每个统计时长段内,假设现在有很多请求,但是这些请求中,如果有超过比例阈值的请求(例如80%的请求)响应时间大于最大RT,认为就是慢调用,不可靠的,然后开启熔断
最小请求数:虽然是在每个统计时长段内进行统计(假设每五秒),但是最少要发起最小请求数以上才行

例如:

  • 监控资源的平均响应时间(RT),当请求的平均响应时间超过设定的阈值,并且在统计时间窗口内,慢调用(响应时间大于阈值的调用)比例超过设定的比例时,触发熔断。
  • 例如,设置平均响应时间阈值为500ms,慢调用比例阈值为30%,时间窗口为10秒。如果在10秒内,该资源的平均响应时间大于500ms,并且慢调用的请求数占总请求数的比例超过30%,就会触发熔断。

(2)异常比例

熔断时长:在这段时长内发起的请求都不会发给远程服务
比例阀值(0.0-1.0):有多少请求出现异常
统计时长:每统计时长内,假设现在有很多请求,但是这些请求中,如果有比例阈值的请求(例如80%的请求)都发生异常,开启熔断
最小请求数:虽然每统计时长进行统计(假设每五秒),但是最少要发起最小请求数以上才行

例如:

  • 统计资源在统计时间窗口内的异常(包括业务异常和系统异常)请求数与总请求数的比例。当异常比例超过设定的阈值时,触发熔断。
  • 例如,设置异常比例阈值为50%,时间窗口为10秒。如果在10秒内,该资源的异常请求数占总请求数的比例超过50%,就会触发熔断。

(3) 异常数

异常数:最大的异常数目,只要在统计时长中请求发生的异常次数超过该值,就开始熔断
熔断时长:熔断时长,在这段时长内发起的请求都不会发给远程服务
统计时长:每统计时长内,假设现在有很多请求,但是这些请求中,如果有比例阈值的请求(例如80%的请求)都发生异常,开启熔断
最小请求数:虽然每统计时长进行统计(假设每五秒),但是最少要发起最小请求数以上才行

例如:

  • 统计资源在统计时间窗口内的异常(包括业务异常和系统异常)请求数量。当异常请求数量超过设定的阈值时,触发熔断。
  • 例如,设置异常数阈值为100,时间窗口为1分钟。如果在1分钟内,该资源的异常请求数达到或超过100次,就会触发熔断。

GateWay(网关)

1.作用

image
image

2.导入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

负载均衡:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

导入负载均衡后,使用类似:lb://ruoyi-api 就有负载均衡效果

配置文件:

server:
  port: 8080

spring: 
  application:
    name: ruoyi-gateway
  cloud:
    gateway:
      routes:
        # 系统模块
        - id: ruoyi-system
          uri: http://localhost:9201/
          predicates:
            - Path=/system/**
          filters:
            - StripPrefix=1

3.核心概念

路由(Route):路由是网关最基础的部分,路由信息由 ID、目标 URI、一组断言和一组过滤器组成。如果断言 路由为真,则说明请求的 URI 和配置匹配。
断言(Predicate):Java8 中的断言函数。Spring Cloud Gateway 中的断言函数输入类型是 Spring 5.0 框架中 的 ServerWebExchange。Spring Cloud Gateway 中的断言函数允许开发者去定义匹配来自于 Http Request 中的任 何信息,比如请求头和参数等。
过滤器(Filter):一个标准的 Spring Web Filter。Spring Cloud Gateway 中的 Filter 分为两种类型,分别是 Gateway Filter 和 Global Filter。过滤器将会对请求和响应进行处理

4.路由规则

Datetime

匹配日期时间之后发生的请求,有After,Before,Between

spring: 
  application:
    name: ruoyi-gateway
  cloud:
    gateway:
      routes:
        - id: ruoyi-system
          uri: http://localhost:9201/
          predicates:
            - After=2021-02-23T14:20:00.000+08:00[Asia/Shanghai]

Cookie

匹配指定名称且其值与正则表达式匹配的cookie

spring: 
  application:
    name: ruoyi-gateway
  cloud:
    gateway:
      routes:
        - id: ruoyi-system
          uri: http://localhost:9201/
          predicates:
            - Cookie=loginname, ruoyi

测试 curl http://localhost:8080/system/config/1 --cookie "loginname=ruoyi"

Header

匹配具有指定名称的请求头,\d+值匹配正则表达式

spring: 
  application:
    name: ruoyi-gateway
  cloud:
    gateway:
      routes:
        - id: ruoyi-system
          uri: http://localhost:9201/
          predicates:
            - Header=X-Request-Id, \d+

Host

匹配主机名的列表

spring: 
  application:
    name: ruoyi-gateway
  cloud:
    gateway:
      routes:
        - id: ruoyi-system
          uri: http://localhost:9201/
          predicates:
            - Host=**.somehost.org,**.anotherhost.org

Method

匹配请求methods的参数,它是一个或多个参数

spring: 
  application:
    name: ruoyi-gateway
  cloud:
    gateway:
      routes:
        - id: ruoyi-system
          uri: http://localhost:9201/
          predicates:
            - Method=GET,POST

Path

匹配请求路径

spring: 
  application:
    name: ruoyi-gateway
  cloud:
    gateway:
      routes:
        - id: ruoyi-system
          uri: http://localhost:9201/
          predicates:
            - Path=/system/**

Query

匹配查询参数

spring: 
  application:
    name: ruoyi-gateway
  cloud:
    gateway:
      routes:
        - id: ruoyi-system
          uri: http://localhost:9201/
          predicates:
            - Query=username, abc.

RemoteAddr

匹配IP地址和子网掩码

spring: 
  application:
    name: ruoyi-gateway
  cloud:
    gateway:
      routes:
        - id: ruoyi-system
          uri: http://localhost:9201/
          predicates:
            - RemoteAddr=192.168.10.1/0

Weight

匹配权重

spring: 
  application:
    name: ruoyi-gateway
  cloud:
    gateway:
      routes:
        - id: ruoyi-system-a
          uri: http://localhost:9201/
          predicates:
            - Weight=group1, 8
        - id: ruoyi-system-b
          uri: http://localhost:9201/
          predicates:
            - Weight=group1, 2

5.路由配置

uri有三种方式

  • websocket配置方式
spring:
  cloud:
    gateway:
      routes:
        - id: ruoyi-api
          uri: ws://localhost:9090/
          predicates:
            - Path=/api/**
  • http地址配置方式
spring:
  cloud:
    gateway:
      routes:
        - id: ruoyi-api
          uri: http://localhost:9090/
          predicates:
            - Path=/api/**
  • 注册中心配置方式
spring:
  cloud:
    gateway:
      routes:
        - id: order-route
          uri: lb://service-order
          predicates:
            - Path=/api/order/**
        - id: product-route
          uri: lb://service-product
          predicates:
            - Path=/api/product/**

6.过滤器

image
image

  1. GatewayFilter(网关过滤器)

    • 作用范围:作用于单个路由。
    • 内置网关过滤器

      Spring Cloud Gateway 提供了许多内置的 GatewayFilter,例如:

      • RetryGatewayFilter:用于请求重试。可以配置重试的条件(如特定的 HTTP 状态码、异常等)、重试次数、重试间隔等。
      • RequestRateLimiterGatewayFilter:结合 Redis 等实现请求限流,通过配置限流的 key(通常基于用户 IP 等)、限流速率等参数来限制请求的频率。
      • ModifyRequestBodyGatewayFilter:修改请求体内容,比如在请求到达目标服务前对请求参数进行调整或补充。
      • ModifyResponseHeaderGatewayFilter:修改响应头信息,例如可以添加自定义的响应头或者修改已有的响应头字段。
  2. GlobalFilter(全局过滤器)

    • 作用范围:作用于所有路由。
    • 特点:不需要在路由配置中显式指定,会自动对所有进入网关的请求生效。
    • 示例场景

      • 权限校验:检查请求中是否包含有效的令牌(如 JWT),验证用户的权限,决定是否允许请求继续转发到下游服务。
      • 日志记录:记录请求的详细信息(如请求 URL、请求方法、请求头、客户端 IP 等),方便进行系统监控和问题排查。
      • 请求转发前的通用处理:例如统一添加请求头(如 X-Request-Id 用于请求追踪)等操作。

也可自定义过滤器

7.跨域配置

spring:
  cloud:
    gateway:
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOriginPatterns: "*"
            allowed-methods: "*"
            allowed-headers: "*"
            allow-credentials: true
            exposedHeaders: "Content-Disposition,Content-Type,Cache-Control"

微服务间的调用经过网关吗?

第一种,是通过openfeign远程调用,比如现在创建订单是通过feignclient,@FeignClient(value = "service-product" , fallback = ProductFeignClientFallback.class),那么就不经过网关,通过注册中心获取可访问的地址后,直接向微服务访问

(没什么意义,多走一遍,不建议使用)第二种,使用网关,修改为@FeignClient(value = "gateway" , fallback = ProductFeignClientFallback.class)

@GetMapping("/api/product/product/{id}")
Product getProductById(@PathVariable("id") Long id);

这样就是先去注册中心找网关的位置,然后通过断言规则,进行微服务间的调用

Seata(分布式事务)

1.使用原因

image
image

一次购买业务,需要在订单服务产生一个订单,库存服务减少一个库存,账户服务减少钱,这样就涉及到了多个服务间的操作,所以要共同控制回滚,保证数据一致性

image
image

image
image

2.启动及导入依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

bin目录下cmd启动:seata-server.bat,然后浏览器的控制台面板 on http://127.0.0.1:7091.默认账号密码:seata

全局事务开启,加在具体的业务方法上:

@GlobalTransactional

3.二阶提交协议

image
image

Transaction Coordinator(TC):事务协调器,它是独立的中间件,需要独立部署运行,它维护全局事务的运行状态,接收TM指令发起的提交与回滚,负责与RM通信协调各各分支事务的提交或回滚。

Transaction Manager(TM):事务管理器,TM需要嵌入和应用程序中工作,它负责开启一个全局事务,并最终向TC发起全局提交或全局回滚的指令。

Resource Manager(RM):控制分支事务,负责分支注册,状态汇报,并接收事务协调器TC的指令,驱动分支(本地)事务的提交和回滚。

seata默认AT模式:

image
image

当业务方法添加@GlobalTransactional注解后,Seata客户端会向TC(事务协调器)注册全局事务,生成唯一的XID(全局事务ID),标识整个分布式事务链路。

  1. 一阶段准备:
  • 分支事务执行SQL前,先记录数据修改前的"前镜像"(before image)
  • 执行SQL修改数据
  • 记录数据修改后的"后镜像"(after image)
  • 将前后镜像作为回滚日志写入undo_log表
  • 向TC注册分支事务,关联XID
  • 提交本地事务(此时数据已真实修改,但处于"待确认"状态)
  • 汇报自己这个分支事务的成功与否的结果
  1. 二阶段提交/回滚:
  • 如果所有分支事务都成功: TC异步通知各分支事务删除对应的undo_log,完成最终提交
  • 如果任一分支事务失败: TC根据XID找到所有相关分支事务 各分支事务根据undo_log中的前镜像生成反向SQL回滚数据 删除undo_log完成回滚

4.四种模式

  1. AT 模式(Auto Transaction,自动事务模式)默认

    • 原理:基于二阶段提交(2PC)优化,通过全局事务ID(XID)+ 本地事务快照(undo_log)实现分布式事务。
    • 特点:

      • 无侵入:业务代码只需加@GlobalTransactional注解。
      • 高性能:一阶段直接提交本地事务,二阶段异步回滚或提交。
      • 依赖undo_log:记录数据修改前后的快照,用于回滚。
    • 适用场景:高并发、短事务的互联网业务(如电商订单、库存扣减)。
  2. TCC 模式(Try-Confirm-Cancel)

    • 原理:基于业务补偿,分为三个阶段:

      • Try:预留资源(如冻结库存)。
      • Confirm:确认提交(如扣减库存)。
      • Cancel:失败回滚(如解冻库存)。
    • 特点:

      • 强一致性:适用于金融、支付等高要求场景。
      • 业务侵入性强:需要手动实现try/confirm/cancel逻辑。
    • 适用场景:对一致性要求严格的业务(如转账、支付)。
  3. SAGA 模式

    • 原理:基于长事务补偿,每个服务执行本地事务,失败时触发逆向补偿操作。
    • 特点:

      • 无锁设计:适合长流程业务(如订单+物流+库存)。
      • 最终一致性:可能短暂不一致,但最终会修复。
      • 需定义正向+逆向SQL(如订单创建+订单取消)。
    • 适用场景:跨多个服务的复杂业务流程(如旅行订票、跨行转账)。
  4. XA 模式(传统2PC)

    • 原理:基于数据库的XA协议,由事务管理器(TM)协调多个数据库的提交/回滚。
    • 特点:

      • 强一致性:所有资源在提交前会加锁,保证ACID。
      • 性能低:同步阻塞,不适合高并发。
    • 适用场景:传统数据库强一致性需求(如银行核心系统)。
模式一致性性能侵入性适用场景
AT最终一致⭐⭐⭐⭐低(仅注解)电商、库存
TCC强一致⭐⭐⭐高(需编码)支付、金融
SAGA最终一致⭐⭐⭐中(需补偿SQL)长流程业务
XA强一致⭐⭐低(数据库支持)传统数据库
微服务组件学习
https://yufornote.xyz/index.php/archives/8/
本文作者 yu
发布时间 2025-07-19
许可协议 CC BY-NC-SA 4.0
发表新评论