首页 > 其他分享 >Sentinel服务保护 + Seata分布式事务

Sentinel服务保护 + Seata分布式事务

时间:2025-01-10 13:04:41浏览次数:3  
标签:事务 服务 Seata lock seata Sentinel VARCHAR id 分布式

服务保护

雪崩问题】微服务调用链路中某个服务,引起整个链路中所有微服务都不可用。在这里插入图片描述
原因】:

  1. 微服务相互调用,服务提供者出现故障。
  2. 服务调用这没有做好异常处理,导致自身故障。
  3. 调用链中所有服务级联失败,导致整个集群故障。

解决方案】:
请求限流、线程隔离、服务熔断
服务保护技术】:
在这里插入图片描述

Sentinel服务保护

官方文档:Sentinel

使用步骤

1. 使用docker部署sentinel

创建并运行sentinel容器:

docker run -d \
--net=host \
--name sentinel \
--restart=always \
-e AUTH_USERNAME=admin \
-e AUTH_PASSWORD=admin \
bladex/sentinel-dashboard:1.8.6

完成后在浏览器输入:192.168.140.101:8858,用户名admin,密码admin

2.在微服务中连接sentinel控制台

引入sentinel依赖:

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

配置控制台:

spring:
  cloud:
    sentinel:
      transport:
        dashboard: 192.168.140.101:8858 # sentinel的控制台地址
      http-method-specify: true # 开启请求方式前缀

Restful风格的API请求路径一般相同,会导致簇点资源名称重复。所以我们要修改配置,把请求方式 + 请求路径作为簇点资源名称

簇点链路

簇点链路就是单机调用链路,是一次请求进入服务后经过的每一个被Sentinel监控的资源链。默认Sentinel会监控SpringMVC的每一个Endpoint(Http接口)。限流、熔断等都是争对簇点链路中的资源设置的,资源名默认就是接口的请求路径。
在这里插入图片描述

解决方案

1. 请求限流

限制访问微服务的请求的并发量,避免服务因流量激增而出现故障。
在这里插入图片描述
在这里插入图片描述
这个接口每秒钟只能处理6个请求,使用ApiFox进行测试,会有部分请求失败【失败返回状态码429】。
在这里插入图片描述

2. 线程隔离

通过限定每个业务能使用的线程数量而将故障业务隔离,避免故障扩散。
在这里插入图片描述
场景】:假设有大量的查询购物车的请求,通过对查询购物车这个线程做线程隔离,可以保证购物车这个微服务的资源不会被耗尽,不会对修改购物车等其他业务造成影响。
在这里插入图片描述
在这里插入图片描述

线程隔离和请求限流的区别:
请求限流:控制接受请求的速度(每秒访问几次)
线程隔离:控制最多能接收请求的次数(一次访问的线程数)
就算请求限流设置的再慢,如果线程卡住的话,不设置线程隔离,也会导致资源占用。

3. fallback

在这里插入图片描述
一、 将FeignClient作为Sentinel的簇点资源:

feign:
  sentinel:
    enabled: true # 开启流量控制

二、 为FeignClient添加Fallback:

  • 方法1:FallbackClass,无法对远程调用的异常做处理
  • 方法2:FallbackFactory,可以对远程调用的异常做处理
  1. 自定义类,实现FallbackFactory,编写对某个FeignClient的fallback逻辑:
@Slf4j
public class ItemClientFallbackFactory implements FallbackFactory<ItemClient> {
    @Override
    public ItemClient create(Throwable cause) {
    	// 编写失败的处理逻辑(失败后就会走里边的方法)
        return new ItemClient() {
            @Override
            public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
                log.error("查询商品失败,"+ cause);
                return CollUtils.emptyList();
            }

            @Override
            public void deductStock(List<OrderDetailDTO> items) {
                log.error("扣减商品库存失败,"+ cause);
                throw new RuntimeException(cause);
            }
        };
    }
}
  1. 将定义的FallbackFactory注册为一个Bean:
public class DefaultFeignConfig {
	@Bean
	public ItemClientFallbackFactory itemClientFallbackFactory() {
		return new ItemClientFallbackFactory();
	}
}
  1. 在ItemClient接口中使用FallbackFactory:@FeignClient(value = "item-service", fallbackFactory = ItemClientFallbackFactory.class)
@FeignClient(value = "item-service", fallbackFactory = ItemClientFallbackFactory.class)
public interface ItemClient {
    @GetMapping("/items")
    List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);

    @PutMapping("/items/stock/deduct")
    void deductStock(@RequestBody List<OrderDetailDTO> items);
}

在这里插入图片描述

4. 服务熔断

断路器统计请求的异常比例或慢调用比例,如果超出阈值则会熔断该业务,则拦截改接口的请求。熔断期间,所有请求快速失败,全部走fallback逻辑。当服务恢复时,断路器会放行访问该服务的请求。
在这里插入图片描述

断路器工作原理:

在这里插入图片描述
默认情况:Closed状态
如果失败的比例过高:就会进入Open状态【拦截一切请求,快速失败】
Open状态下会尝试放行一次请求,进入Half-Open状态,如果仍然失败,再次返回Open状态;如果成功,回到Closed状态

配置熔断策略

在这里插入图片描述
在这里插入图片描述

分布式事务

如果一个业务需要多个服务合作完成,而且每个服务都有事务,多个事务必须同时成功或同时失败,这样的事务就是分布式事务。其中每一个服务的事务就是一个分支事务。整个业务称为全局事务
场景】:用户下单后,订单服务首先创建订单,随后调用购物车服务清理购物车,最后调用库存服务扣减商品的库存。如果在调用库存服务的时候商品库存为0,此时扣减库存失败,订单服务和购物车服务应该同时失败。
在这里插入图片描述
出现问题的原因】:各个分支服务不知道对方的情况
解决思路】:让各个分支事务感受到对方的存在,让所有的微服务向事务协调者报告当前的状态。
在这里插入图片描述

Seata架构

  • 事务协调者(TC):维护全局和分支事务的状态,协调全局事务提交或回滚。
  • 事务管理器(TM):定义全局事务的范围、开始全局事务、提交或回滚全局事务。
  • 资源管理器(RM):管理分支事务,与TC交谈以注册分支事务和报告分支事务的状态。
    在这里插入图片描述

1. 部署TC服务

  1. 创建数据库,导入sql文件
    在这里插入图片描述
CREATE DATABASE IF NOT EXISTS `seata`;
USE `seata`;


CREATE TABLE IF NOT EXISTS `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_status_gmt_modified` (`status` , `gmt_modified`),
    KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;


CREATE TABLE IF NOT EXISTS `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 = utf8mb4;


CREATE TABLE IF NOT EXISTS `lock_table`
(
    `row_key`        VARCHAR(128) NOT NULL,
    `xid`            VARCHAR(128),
    `transaction_id` BIGINT,
    `branch_id`      BIGINT       NOT NULL,
    `resource_id`    VARCHAR(256),
    `table_name`     VARCHAR(32),
    `pk`             VARCHAR(36),
    `status`         TINYINT      NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
    `gmt_create`     DATETIME,
    `gmt_modified`   DATETIME,
    PRIMARY KEY (`row_key`),
    KEY `idx_status` (`status`),
    KEY `idx_branch_id` (`branch_id`),
    KEY `idx_xid_and_branch_id` (`xid` , `branch_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

CREATE TABLE IF NOT EXISTS `distributed_lock`
(
    `lock_key`       CHAR(20) NOT NULL,
    `lock_value`     VARCHAR(20) NOT NULL,
    `expire`         BIGINT,
    primary key (`lock_key`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);
  1. 准备配置文件
    seata运行时所需的配置文件
    上传前先看看application.yml,里边可能有些配置需要改一下
    把上边的配置文件丢到root根目录下
    在这里插入图片描述
  2. docker部署
    在/root目录下执行以下命令,创建并允许seata容器
docker run --name seata \
-p 8099:8099 \
-p 7099:7099 \
-e SEATA_IP=192.168.140.101 \
-v ./seata:/seata-server/resources \
--privileged=true \
--network hm-net \
--restart=always \
-d \
seataio/seata-server:1.5.2
  1. 以上操作都完成后,在浏览器输入:http://192.168.140.101:7099/后即可登录seata控制台。(初始账号:admin、密码:admin)

2. 微服务集成Seata

  1. 引入Seata依赖
<!--seata-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
  1. 配置TC服务地址,让微服务找到TC服务地址
    在这里插入图片描述
seata:
  registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
    type: nacos # 注册中心类型 nacos
    nacos:
      server-addr: 192.168.140.101:8848 # nacos地址
      namespace: "" # namespace,默认为空
      group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUP
      application: seata-server # seata服务名称
      username: nacos
      password: nacos
  tx-service-group: hmall # 事务组名称
  service:
    vgroup-mapping: # 事务组与tc集群的映射关系
      hmall: "default"

因为很多服务都需要实现分布式事务,所以可以把对于seata的配置抽取成一个共享配置写在nacos里。所以添加依赖的时候,检查一下是否有bootstrapnacos配置管理的依赖。

查看seata的日志文件,可以看到购物车服务、交易服务、商品服务已经全部和seata的TC服务建立连接。
在这里插入图片描述

3. Seata解决分布式事务问题

XA模式—强一致

在这里插入图片描述

  1. 一阶段工作:
  • RM注册分支事务
  • RM执行分支事务sql但不提交
  • RM报告执行状态到TC
  1. 二阶段工作:
  • TC检测各分支事务执行状态:
    • 如果都成功,通知所有RM提交事务
    • 如果有失败,通知所有RM回滚事务
  • RM接收到TC指令,提交或回滚事务

通过“等待”的方式,确保了全局事务的ACID特性。但是一阶段需要锁定数据库的资源,到二阶段才释放,性能差。

实现步骤
  1. 修改(每个参与事务的微服务)application.yml文件,开启XA模式
seata:
  data-source-proxy-mode: XA
  1. 给发起全局事务的入口添加@GlobalTransactional注解
    在这里插入图片描述

AT模式(主推)—最终一致

AT模式弥补了XA模式中资源锁定周期过长的缺陷。
在这里插入图片描述

  1. 一阶段RM的工作:
  • 注册分支事务
  • 记录undo-log(数据快照)
  • 执行业务sql并提交
  • 报告事务状态
  1. 二阶段提交时RM的工作:
  • 删除undo-log即可
  1. 二阶段回滚时RM的工作:
  • 根据undo-log恢复数据到更新前

AT模式相比于XA模式的优点在于:在一阶段不需要等待彼此执行,而是各自提交,这样资源就没有锁定,性能也会好。
但是如果二阶段需要进行回滚,在回滚之前,会出现数据短暂的不一致。
AT模式与XA模式的区别】:

  1. XA模式一阶段不提交事务,锁定资源
    AT模式一阶段直接提交,不锁定资源
  2. XA模式依赖数据库机制实现回滚
    AT模式利用数据快照实现回滚
  3. XA模式强一致
    AT模型最终一致
实现步骤
  1. 创建数据表,导入用来记录数据快照的undo_log表
    】:每个分支事务都需要有自己的undo_log表
-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(
    `branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',
    `xid`           VARCHAR(128) NOT NULL COMMENT 'global transaction id',
    `context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
    `rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',
    `log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',
    `log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',
    `log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',
    UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
  1. 修改application.yml文件,将事务模式修改为AT模式
seata:
  data-source-proxy-mode: AT

在这里插入图片描述
数据快照(undo_log表):
在这里插入图片描述

标签:事务,服务,Seata,lock,seata,Sentinel,VARCHAR,id,分布式
From: https://blog.csdn.net/qq_57882997/article/details/145031176

相关文章

  • 【分布式】优雅实现多系统一致性补偿方案
    前言我们在开发的过程中,如果一个业务操作需要本地写MYSQL数据以及对第三方系统做写操作,那么这种流程就涉及到分布式系统一致性的问题,然而并非所有系统都能使用成熟的分布式事务方案案例说明以一个财务报账业务为例,涉及到的系统如下:系统名作用实现方案单据系统申请......
  • 【待发】【分布式】浅析分布式理论的CAP
    今天让我们来聚焦于分布式系统架构中的重要理论——CAP理论。在分布式系统中,可用性和数据一致性是两个至关重要的因素,而CAP理论就是在这两者之间提供了一种权衡的原则,帮助我们在设计分布式系统时进行决策。同时,CAP理论的出现也深刻影响着分布式系统的发展和设计。但是在当今这个时......
  • 边缘计算和分布式计算区别是什么?
    首先,需要理解什么是边缘计算和分布式计算,简要说明如下:边缘计算:是把数据处理放在网络的边缘,靠近数据源的地方,这样可以减少延迟,节省带宽。分布式计算:是把任务分配到多个节点上处理,然后把结果汇总,这样可以提高计算能力和处理大数据。边缘计算和分布式计算是两种不同的计算模......
  • 分布式锁Redisson详解,Redisson如何解决不可重入,不可重试,超时释放,主从一致问题的分析解
    目录1.Redisson解决不可重入锁导致的死锁问题 2.不可重试问题Pub/Sub的优势锁释放的发布逻辑3.超时释放的问题1.锁的超时释放机制背景2.源码分析2.1锁的获取2.2看门狗机制2.3看门狗续期实现2.4手动设置锁的过期时间总结 4.主从一致性 问题背景......
  • Scala分布式语言二(基础功能搭建、面向对象基础、面向对象高级、异常、集合)
    章节3基础功能搭建46.函数作为值三packagecn.itbaizhan.chapter03//函数作为值,函数也是个对象objectFunctionToTypeValue{defmain(args:Array[String]):Unit={  //Studentstu=newStudent()  /*val......
  • FastAPI 依赖注入、异步任务与分布式调度
    FastAPI依赖注入、异步任务与分布式调度目录......
  • 【分布式技术】Springboot集成zookeeper
    文章目录作为服务发现步骤1:添加依赖步骤2:配置Zookeeperapplication.propertiesapplication.yml步骤3:启用服务发现步骤4:运行你的应用注意事项作为客户端使用场景1.**配置管理**2.**命名服务(NameService)**3.**分布式锁**4.**集群管理**5.**选举主节点(LeaderElecti......
  • Sentinel诞生的背后故事,你知道多少?
    本文已收录在Github,关注我,紧跟本系列专栏文章,咱们下篇再续!作者简介:魔都架构师,多家大厂后端一线研发经验,在分布式系统设计、数据平台架构和AI应用开发等领域都有丰富实践经验。各大技术社区头部专家博主。具有丰富的引领团队经验,深厚业务架构和解决方案的积累。负责:中央/分销......
  • Redis 分布式锁与 Zookeeper 分布式锁的区别及应用
    目录Redis分布式锁与ETCD分布式锁:深入剖析与Go语言实现一、Redis分布式锁二、ETCD分布式锁三、Redis分布式锁与ETCD分布式锁的区别四、总结在分布式系统中,分布式锁是确保多个节点间对共享资源进行互斥访问的关键技术。Redis和ETCD是两种常用的分布式锁实......
  • 招行面试: 分布式调度 设计,要考虑 哪些问题?
    本文原文链接文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录博客园版为您奉上珍贵的学习资源:免费赠送:《尼恩Java面试宝典》持续更新+史上最全+面试必备2000页+面试必备+大厂必备+涨薪必备免费赠送:《尼恩技术圣经+高并发系列PDF》,帮你实现技术自由,完......