服务保护和分布式事务

发布于:2025-08-15 ⋅ 阅读:(15) ⋅ 点赞:(0)

一、微服务保护

1、雪崩

雪崩问题:微服务调用链中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。

原因:

  • 微服务相互调用,服务提供者出现故障或阻塞
  • 服务调用者没有做好异常处理,导致自身故障。
  • 调用链中的所有服务级联调失败,导致整个集群故障。

2、服务保护方案

        1)请求限流:限制访问微服务的请求的并发量,避免服务因流量激增出现故障。

        2)线程隔离:也叫做舱壁模式,模拟船舱隔板的防水原理。通过限定每个业务能使用的线程数量而将故障业务隔离,避免故障扩散。

        3)服务熔断:由断路器统计请求的异常比例或慢调用比例,如果超出阈值则会熔断该业务,则拦截该接口的请求。熔断期间,所有请求快速失败,全都走fallback逻辑。

 服务保护技术

 3、Sentinel

①初识Sentinel

是阿里巴巴开源的一款微服务流量控制组件。提供了一个核心库,(可以理解为jar包),包含限流等功能,微服务引入其即可使用,只需要配置规则,用编码实现(比较麻烦)或者使用控制台。

Sentinel 的使用可以分为两个部分:

  • 核心库(Jar包):不依赖任何框架/库,能够运行于 Java 8 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。在项目中引入依赖即可实现服务限流、隔离、熔断等功能。

  • 控制台(Dashboard):Dashboard 主要负责管理推送规则、监控、管理机器信息等。

使用:

        1)下载

将课程资料的jar包放在任意非中文、不包含特殊字符的目录下,重命名为sentinel-dashboard.jar

然后运行如下命令启动控制台:

sentinel-dashboard.jar的位置,进入命令行,然后运行:

java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

访问http://localhost:8090页面,就可以看到sentinel的控制台了:

需要输入账号和密码,默认都是:sentinel

登录后,即可看到控制台,默认会监控sentinel-dashboard服务本身:

         2)微服务整合

我们在cart-service模块中整合sentinel,连接sentinel-dashboard控制台,步骤如下: 1)引入sentinel依赖:

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

 修改application.yaml文件,添加下面内容:

spring:
  cloud: 
    sentinel:
      transport:
        dashboard: localhost:8090

 之后在购物车就可以看到了:

簇点链路

所谓簇点链路,就是单机调用链路,是一次请求进入服务后经过的每一个被Sentinel监控的资源。默认情况下,Sentinel会监控SpringMVC的每一个Endpoint(http接口,就是controller)。

因此,我们看到/carts这个接口路径就是其中一个簇点,我们可以对其进行限流、熔断、隔离等保护措施。

 不过,需要注意的是,我们的SpringMVC接口是按照Restful风格设计,因此购物车的查询、删除、修改等接口全部都是/carts路径。默认情况下Sentinel会把路径作为簇点资源的名称,无法区分路径相同但请求方式不同的接口,查询、删除、修改等都被识别为一个簇点资源,这显然是不合适的。

所以我们可以选择打开Sentinel的请求方式前缀,把请求方式 + 请求路径作为簇点资源名:

首先,在cart-serviceapplication.yml中添加下面的配置:

spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8090
      http-method-specify: true # 开启请求方式前缀

 然后,重启服务,通过页面访问购物车的相关接口,可以看到sentinel控制台的簇点链路发生了变化:

②请求限流

点击流控,选择QPS即每秒钟请求的数量,在单机阈值填写数量

 

测试:

下载Jemeter,资料中提供了压缩包,解压缩即可,在bin目录下 双击其中的jmeter.bat执行程序。命令框不可以关闭。

 设置语言:

添加测试

输入一些数据 ,配置线程

 在线程组右键添加取样器,http请求

 添加数据进行配置

添加监听器可以添加汇总报告以及查看结果树

注:资料中提供了测试,直接拖进来即可。

 选中想要测试的,右键启动即可 ,启动之后就可以点击查看结果树以及汇总报告来查看测试

③线程隔离

同样是点击流控

 打开后选择并发线程数,输入线程数,这里的5是可以用5个线程,比如响应时间是500毫秒,那么一秒就可以处理两个请求,即一个线程每秒可以处理两个请求。

进行测试首先要①对商品服务进行修改进行模拟线程延迟

@ApiOperation("根据id查询商品")
    @GetMapping("{id}")
    public ItemDTO queryItemById(@PathVariable("id") Long id) {
        //模拟业务延迟
        ThreadUtil.sleep(500);
        
        return BeanUtils.copyBean(itemService.getById(id), ItemDTO.class);
    }

②对购物车服务进行修改 

需要注意的是,默认情况下SpringBoot项目的tomcat最大线程数是200,允许的最大连接是8492,单机测试很难打满。所以我们需要配置一下cart-service模块的application.yml文件,修改tomcat连接:

server:
  port: 8082
  tomcat:
    threads:
      max: 50 # 允许的最大线程数
    accept-count: 50 # 最大排队等待数量
    max-connections: 100 # 允许的最大连接

如果没有线程隔离,查询购物车将服务器的资源耗尽,修改购物车也会受到影响,变得很慢。如果添加了线程隔离,查询购物车很慢但不会影响到修改购物车了。

④Fallback

1、将FeignClient作为Sentinel的簇点资源,修改cart-service模块的application.yml文件:

feign:
  sentinel:
    enabled: true # 开启feign对sentinel的支持

 FeignClient的Fallback有两种配置方式:

方式一:FallbackClass,无法对远程调用的异常做处理。

方式二:FallbackFactory,可以对远程调用的异常做处理,通常都会选择这种。

给feignClient编写Fallback逻辑

        1)自定义类,实现FallbackFactory,编写对某个FeignClient的fallback逻辑:

@Slf4j
public class ItemClientFallback implements FallbackFactory<ItemClient> {

//出现限流等情况,就会执行下面的方法
    @Override
    public ItemClient create(Throwable cause) {
        return new ItemClient() {
            @Override
            public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
                log.error("远程调用ItemClient#queryItemByIds方法出现异常,参数:{}", ids, cause);
                // 查询购物车允许失败,查询失败,返回空集合
                return CollUtils.emptyList();
            }

            @Override
            public void deductStock(List<OrderDetailDTO> items) {
                // 库存扣减业务需要触发事务回滚,查询失败,抛出异常
                throw new BizIllegalException(cause);
            }
        };
    }
}

        2)将刚刚定义的ItemClientFallbackFactory注册为一个bean:

    @Bean
    public ItemClientFallbackFactory itemClientFallbackFactory() {
        return new ItemClientFallbackFactory();
    }

        3)在ItemClient中的@FeignClient注解上添加上新建的类

@FeignClient(value = "item-service",fallbackFactory = ItemClientFallbackFactory.class)
⑤服务熔断

熔断是解决雪崩问题的重要手段。思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。

在sentinel控制台中点击熔断

填写相关的规则

二、分布式事务

在分布式系统中,如果一个业务需要多个服务合作完成,而且每个服务都有事务,多个事务必须同时成功或失败,这样的事务就是分布式事务,其中每个服务的事务就是一个分支事务,整个业务称为全局事务。

解决思路:添加一个事务协调者,让它跟每个每个事务都建立联系。

1、认识Seata

在Seata的事务管理中有三个重要的角色:

  • TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚。

  • TM (Transaction Manager) - 事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务。

  • RM (Resource Manager) - 资源管理器:管理分支事务,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

TC作为事务协调者是独立于微服务之外的,所以应搭建TC服务,在微服务中引入Seata依赖,会提供TM,RM的功能。

Seata架构

2、部署TC服务

1、准备数据库表

Seata支持多种存储模式,但考虑到持久化的需要,我们一般选择基于数据库存储。执行课前资料提供的《seata-tc.sql》,导入数据库表。

2、准备配置文件

课前资料准备了一个seata目录,其中包含了seata运行时所需要的配置文件:

将整个seata文件夹拷贝到虚拟机的/root目录,将tar包也传进去(使用docker load -i seata.tar 将tar包转为镜像)

3、Docker部署

需要注意,要确保nacos、mysql都在hm-net网络中。如果某个容器不再hm-net网络,可以参考下面的命令将某容器加入指定网络:

docker network connect [网络名] [容器名]

在虚拟机的/root目录执行下面的命令:

docker run --name seata \
-p 8099:8099 \
-p 7099:7099 \
-e SEATA_IP=192.168.100.128 \
-v ./seata:/seata-server/resources \
--privileged=true \
--network hm-net \
-d \
seataio/seata-server:1.5.2

3、微服务集成Seata

首先,在项目中引入Seata依赖:

<!--统一配置管理-->
  <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  </dependency>
  <!--读取bootstrap文件-->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-bootstrap</artifactId>
  </dependency>
  <!--seata-->
  <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
  </dependency>

然后,首先在nacos上添加一个共享的seata配置,命名为shared-seata.yaml

内容如下:

seata:
  registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
    type: nacos # 注册中心类型 nacos
    nacos:
      server-addr: 192.168.100.128: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"

4、XA模式

XA规范是x/Open组织定义的分布式事务处理标准,XA规范描述了全局的TM与局部的RM之间的接口,几乎所有主流的关系型数据库都对XA规范提供了支持。Seata的XA模式如下:

一阶段的工作:

  1. RM注册分支事务到TC

  2. RM执行分支业务sql但不提交

  3. RM报告执行状态到TC

二阶段的工作:

  1. TC检测各分支事务执行状态

    1. 如果都成功,通知所有RM提交事务

    2. 如果有失败,通知所有RM回滚事务

      2. RM接收TC指令,提交或回滚事务

XA模式的优点是什么?

  • 事务的强一致性,满足ACID原则

  • 常用数据库都支持,实现简单,并且没有代码侵入

XA模式的缺点是什么?

  • 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差

  • 依赖关系型数据库实现事务

实现步骤:

首先,我们要在配置文件中指定要采用的分布式事务模式。我们可以在Nacos中的共享shared-seata.yaml配置文件中设置:

seata:
  data-source-proxy-mode: XA

其次,给发起全局事务的入口方法添加@GlobalTransactional注解。

5、AT模式

AT模式同样是分阶段提交的事务模型,不过缺弥补了XA模型中资源锁定周期过长的缺陷。

阶段一RM的工作:

  • 注册分支事务

  • 记录undo-log(数据快照)

  • 执行业务sql并提交

  • 报告事务状态

阶段二提交时RM的工作:

  • 删除undo-log即可

阶段二回滚时RM的工作:

  • 根据undo-log恢复数据到更新前

实现AT模式

首先,添加资料中的seata-at.sql到微服务对应的数据库中(每个微服务都要有自己的表)

然后修改,application.yml文件,将事务模式修改为AT模式:

seata:
  data-source-proxy-mode: AT

简述AT模式与XA模式最大的区别是什么?

  • XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。

  • XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。

  • XA模式强一致;AT模式最终一致

可见,AT模式使用起来更加简单,无业务侵入,性能更好。因此企业90%的分布式事务都可以用AT模式来解决。


网站公告

今日签到

点亮在社区的每一天
去签到