雪崩问题

原因分析

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

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

解决思路:

  • 尽量避免服务出现故障或者阻塞。
    √ 保证代码的健壮性;
    √ 保证网络通畅;
    √ 能应对较高的并发请求;
  • 服务调用者做好远程调用异常的后备方案,避免故障扩散

服务保护方案

  1. 请求限流:限制访问微服务的请求的并发量,避免服务因流量激增出现故障
  2. 线程隔离:也叫做舱壁模式,模拟船舱隔板的防水原理。通过限定每个业务能使用的线程数量而将故障业务隔离,避免故障扩散
  3. 服务熔断:由断路器统计请求的异常比例或慢调用比例,如果超出阈值则会熔断该业务,则拦截该接口的请求
  4. 失败处理: 定义fallback逻辑,让业务失败时不再抛出异常,而是返回默认数据或友好提示
    常见的服务保护技术有:Sentinel和Hystrix,毫无疑问这里主要讲述Sentinel

Sentinel

官方网址->home | Sentinel

微服务整合

  1. 引入sentinel依赖

    1
    2
    3
    4
    5
    <!--sentinel-->
    <deependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </deependency>
  2. 配置控制台
    修改application.yaml文件,添加下面内容:

    1
    2
    3
    4
    5
    spring:
    cloud:
    sentinel:
    transport:
    dashboard: localhost:8090
  3. 访问任意接口后,就可以在sentinel的控制台中看见

簇点链路

  • 就是单机调用链路。是一次请求进入服务后经过的每一个被Sentinel监控的资源链。默认Sentinel会监控SpringMVC的每一个Endpoint(http接口)。限流、熔断等都是针对簇点链路中的资源设置的。而资源名默认就是接口的请求路径。
  • Restful风格的API请求路径一般都相同,这会导致簇点资源名称重复。因此我们要修改配置,把请求方式+请求路径作为簇点资源名称:
    1
    2
    3
    4
    5
    6
    spring:
    cloud:
    sentinel:
    transport:
    dashboard: localhost:8090
    http--method-specify: true # 开启请求方式前缀

请求限流

在簇点链路后面点击流控按钮,即可对其做限流配置
QPS:每秒钟限制请求的数量
单机阈值:每秒并发数

线程隔离

与请求限流相同,但选择并发线程数
分析:五个并发线程数的话,如果单线程QPS为2,则5线程QPS为10

Fallback

  1. 将FeignClient作为Sentinel的簇点资源:

    1
    2
    3
    feign:
    sentinel:
    enabled: true
  2. FeignClient的Fallback有两种配置方式

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

添加步骤

  1. 自定义类,实现FallbackFactory,编写对某个FeignClient的fallback逻辑:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Slf4j
    public class UserClientFallbackFactory implements FallbackFactory<UserClient>{
    @Override
    public UserClient create(Throwable throwable){
    //创建UserClient接口实现类,实现其中的方法,编写失败降级的处理逻辑
    return new UserClient(){
    @Override
    public User findById(Long id){
    //记录异常信息,可以返回空或者抛出异常
    log.error("查询用户失败", throwable);
    return null;
    }
    };
    }
    }
  2. 将刚刚定义的UserClientFallbackFactory注册为一个Bean:
    1
    2
    3
    4
    @Bean
    public UserClientFallbackFactory userClientFallback(){
    return new UserClientFallbackFactory();
    }
  3. 在UserClient接口中使用UserClientFallbackFactory:
    1
    2
    3
    4
    5
    6
    @FeignClient(value  = "userservice",fallbackFactory = UserClientFallbackFactory.class)
    public interface UserClient{

    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
    }

服务熔断

点击控制台中簇点资源后的熔断按钮,即可配置熔断策略

分布式事务

在分布式系统中,如果一个业务需要多个服务合作完成,而且每一个服务都有事务,多个事物必须同时成功或失败,这样的事物就是分布式事务。其中的每个服务的事务就是一个分支事务。整个业务称为全局事务
为了解决分布式事务,各个子事务之间必须能感知到彼此的事务状态,才能保证状态一致->事务协调者

Seata架构

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

  • TC(Transaction Coordinator)-事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚
  • TM(Transaction Manager)-事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务。
  • RM(Resource Manager)-资源管理器:管理分支事务,与TC交谈以注册分支事务和报告分支事务的状态

部署TC服务

  1. 准备数据库表
    • Seata支持多种存储模式,但考虑到持久化的需要,我们一般选择基于数据库存储。
      • global_table:全局表
      • branch_table:分支表
      • distributed_lock:分布式锁
      • lock_table:锁
  2. 准备配置文件
  3. docker部署

微服务集成Seata

  1. 引入依赖:
    1
    2
    3
    4
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    </dependency>
  2. 添加配置,让微服务找到TC服务
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    seata:
    registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
    # 参考tc服务自己的registry.conf中的配置
    type: nacos
    nacos: # tc
    server-addr: 127.0.0.1:8848
    namespace: ""
    group: DEFAULT_GROUP
    application: seata-tc-server # tc服务在nacos中的服务名称
    usernamne: nacos
    password: nacos
    tx-service-group: seata-demo # 事务组名称
    service:
    vgroup-mapping: # 事务组与TC服务cluster的映射关系
    seata-demo: "default" # 集群的名称

XA模式

XA规范是X/Open组织定义的分布式事务管理(DTP)标准,XA规范描述了全局的TM与局部的RM之间的接口,几乎所有主流的关系型数据库都对XA规范提供了支持。
一阶段的工作:

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

优点

  • 事务的强一致性,满足ACID原则
  • 常用数据库都支持,实现简单,并且没有代码入侵

缺点

  • 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
  • 依赖关系型数据库实现事务

实现XA模式

Seata的starter已经完成了XA模式的自动装配,实现非常简单,步骤如下:

  1. 修改application.yaml 文件(每个参与事务的微服务),开启XA模式
    1
    2
    seata:
    data-source-proxy-mode: XA # 开启数据源代理的XA模式
  2. 给发起全局事务的入口方法添加@GlobalTransactional注解,eg:
    1
    2
    3
    4
    5
    6
    @Override
    @GlobalTransactional
    public Long CreateOrder(OrderFromDTO oder){
    // 业务逻辑...略
    return order.getId();
    }
  3. 重启服务测试

AT模式

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

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

  • XA依赖数据机制实现回滚,AT通过快照回滚
  • XA强一致,AT最终一致

实现AT模式

  1. 添加sql数据表到微服务对应的数据库中 (undo_log)
  2. 修改application.yaml文件,改为AT模式
    1
    2
    seata:
    data-source-proxy-mode: AT # 开启数据源代理的AT模式