[Spring] Sentinel详解

🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343

🏵️热门专栏:

🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482

🍕 Collection与数据结构 (93平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482

🧀线程与网络(97平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482

🍭MySql数据库(95平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482

🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482

🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482

🎃Redis(97平均质量分)https://blog.csdn.net/2301_80050796/category_12777129.html?spm=1001.2014.3001.5482

🐰RabbitMQ(97平均质量分) https://blog.csdn.net/2301_80050796/category_12792900.html?spm=1001.2014.3001.5482

感谢点赞与关注~~~

目录

  • [1. 服务雪崩](#1. 服务雪崩)
  • [2. 微服务保护](#2. 微服务保护)
    • [2.1 服务保护方案](#2.1 服务保护方案)
      • [2.1.1 请求限流](#2.1.1 请求限流)
      • [1.1.2 线程隔离](#1.1.2 线程隔离)
      • [1.1.3 服务熔断](#1.1.3 服务熔断)
    • [1.2 Sentinel](#1.2 Sentinel)
      • [1.2.1 介绍和安装](#1.2.1 介绍和安装)
      • [1.2.2 微服务整合](#1.2.2 微服务整合)
    • [1.3 请求限流](#1.3 请求限流)
    • [1.4 线程隔离](#1.4 线程隔离)
      • [1.4.1 OpenFeign整合Sentinel](#1.4.1 OpenFeign整合Sentinel)
      • [1.4.2 配置线程隔离](#1.4.2 配置线程隔离)
    • [1.5 服务熔断](#1.5 服务熔断)
      • [1.5.1 编写降级逻辑](#1.5.1 编写降级逻辑)
      • [1.5.2 服务熔断l](#1.5.2 服务熔断l)

1. 服务雪崩

在微服务远程调用的过程中,购物车服务需要查询最新的商品信息,与购物车数据做对比,提醒用户.如果此时商品服务查询发生故障,查询购物车列表在调用商品服务时,也会发生异常,从而导致购物车查询失败.

还有级联失败 问题:

还是查询购物车的业务,加入商品服务业务并发较高,占用过多的Tomcat连接,可能会导致商品服务的所有接口响应时间增加,延迟变高,甚至是长时间的阻塞至查询失败.

此时查询购物车业务需要查询并等待商品查询结果,从而导致查询购物车列表业务的响应时间也变长,甚至也阻塞直至无法访.。而此时如果查询购物车的请求较多,可能导致购物车服务的Tomcat连接占用较多,所有接口的响应时间都会增加,整个服务性能很差, 甚至不可用.

以此类推,整个微服务中与购物车服务,商品服务等有调用关系的服务可能都会出现问题,最终导致整个集群不可用.

这就是级联失败问题,或者叫雪崩问题.那么如何解决上述问题呢?我们接下来就给出解决的方案.

2. 微服务保护

保证服务运行的健壮性,避免级联失败导致的雪崩问题,就属于微服务保护,这章我们就一起来学习一下微服务保护的常见方案已经应对技术.

2.1 服务保护方案

微服务保护的方案有很多,比如:

  • 请求限流
  • 线程隔离
  • 服务熔断

这些方案或多或少都会导致服务的体验略有下降,比如请求限流,降低了并发上限,线程隔离,降低了可用资源的数量,服务熔断,降低了服务的完整度,部分服务变得不可用或者弱可用 .因此这些方案都属于降级 的方案,但通过这些方案,服务的健壮性得到了提升.

接下来,我们就逐一了解这些方案的原理.

2.1.1 请求限流

服务故障的最重要原因就是并发太高,为了解决这个问题,就能避免大部分的故障,当然,接口的并发不是一直很高,而是突发的.因此请求限流,就是限制或控制接口的并发访问量,避免服务因为流量激增而出现故障.

请求限流往往会有一个限流器,数量高低起伏的并发请求曲线,经过限流器就变得非常平稳.

1.1.2 线程隔离

当一个业务接口响应时间过长,而且并发高时,就可能耗尽服务的线程资源,导致服务内的其他接口受到影响,所以我们必须把这种影响降低,或者缩减影响的范围,线程隔离正是解决这个问题的好办法.

线程隔离的思想来自轮船的舱壁模式:

轮船的船舱会被隔板分割为N个相互隔离的密闭舱,加入轮船触礁进水,只有损坏的部分密闭仓会进水,而其他仓由于相互隔离,并不会进水,这样就把进水控制在了部分船体,避免了整个船仓进水而整体沉没.

为了避免某个接口故障或者压力过大导致导致了整个服务不可用,我们可以限定每个接口可以使用的资源范围,也就是将其"隔离"起来.

如图所示,我们给查询购物车业务限定可用线程数量上限为20,这样即便查询购物车的请求因为查询商品服务而出现故障,也不会导致服务器的资源被耗尽,不会影响到其他的接口.

1.1.3 服务熔断

线程隔离虽然避免了雪崩问题,但是故障服务(商品服务)依然会拖慢购物车服务(服务调用方)的接口响应速度,而且商品查询依然会导致查询购物车功能出现故障,购物车业务也变得不可用了.

所以我们需要做两件事情:

  • 编写服务降级逻辑: 就是服务方调用失败后的处理逻辑,根据业务场景,可以抛出异常,也可以返回友好提示或者默认数据.
  • 异常统计和熔断: 统计服务提供方的异常比例,当比例过高表明该接口会影响到其他服务,应该拒绝调用该接口,而是直接走降级逻辑.

1.2 Sentinel

微服务保护的技术有很多,但是在国内使用较多的还是Sentinel,所以接下来我们学习Sentinel的使用.

1.2.1 介绍和安装

Sentinel是阿里巴巴开源的一款服务保护框架,目前已经加入SpringCloudAlibaba中。官方网站:
https://sentinelguard.io/zh-cn/,为了方便监控微服务,我们需要先把Sentinel的控制台搭建出来.

  1. 首先下载jar包:
    https://github.com/alibaba/Sentinel/releases

  2. 启动: 将jar包放在任意中文,不包含特殊字符的目录之下,重命名为sentinel-dashboard.jar

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

复制代码
java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
  1. 访问
    访问http://localhost:8090/

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

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

1.2.2 微服务整合

  1. 在想要引入限流的微服务某块引入Sentinel依赖
xml 复制代码
<!--sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId> 
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
  1. 配置控制台
    修改application.yml文件,添加下面的内容:
yml 复制代码
spring:
  cloud: 
    sentinel:
      transport:
        dashboard: localhost:8090
  1. 访问引入该微服务的任意端点
    Sentinel的客户端会将服务访问的信息提交到Sentinel-dashboard控制台,并展示出统计信息:

    点击簇点链路菜单,就是单机调用链路,是一次请求进入服务后经过的每一个被Sentinel监控的的资源,默认情况下,Sentinel会监控SpringMVC的每一个接口.
    因此,我们看到/carts这个接口路径就是其中一个簇点,我们可以对其进行限流,熔断,隔离等保护措施.
    不过需要注意的是,我们的SpringMVC接口是按照Restful风格设计,因此购物车的查询,删除,修改等接口全部都是/carts路径:

    默认情况下Sentinel会把路径作物簇点资源的名称,无法区分区分路径相同但是请求方式不同的接口 ,查询,删除,修改等都被识别为一个簇点资源,这显然是不合适的.
    首先,在cart-service的application.yml``中添加下面的配置:
yml 复制代码
spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8090
      http-method-specify: true # 开启请求方式前缀

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

1.3 请求限流

在簇点链路后面点击留空按钮,可以对其做限流配置:

在弹出的菜单中可以这样填写:

这样就把查询购物车列表这个簇点资源的流量限制在了每秒6个,也就是QPS最大为6.

我们利用了Jemeter做限流测试,我们每秒发出10个请求:

最终监控结果如下:

可以看得出get:/carts这个接口的通过QPS稳定在6附近,而拒绝的QPS稳定在4附近,符合我们的预期.

1.4 线程隔离

限流可以降低服务器的压力,减少引并发流量引起的服务故障的概率,但是不能完全避免服务故障,但某个服务出现故障,我们必须隔离对这个服务的调用,避免发生雪崩.

比如,查询购物车的时候需要查询商品,为了避免因商品服务出现故障导致导致购物车服务级联失败,我们可以把购物车业务中查询商品的部分隔离起来,限制可用的线程资源:

这样即便商品服务出现了故障,最多导致查询购物车的业务故障,并且可用的线程资源也被限定在一定的范围之内,不会导致整个购物车服务崩溃.

所以我们要对查询商品的FeignClient接口做线程隔离.

1.4.1 OpenFeign整合Sentinel

修改cart-service模块的application.yml文件,开启Feign的Sentinel功能:

yml 复制代码
feign:
  sentinel:
    enabled: true # 开启feign对sentinel的支持

需要注意的是,默认情况之下SpringBoot项目的Tomcat最大线程数是200,允许的最大连接是8492,单机测试很难打满.

所以我们需要配置一下cart-service模块的application.yml文件,修改Tomcat连接:

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

然后重启car-service服务,可以看到查询商品的FeignClient自动变成了一个簇点资源:

1.4.2 配置线程隔离

接下来,点击查询商品的FeignClient对应的簇点资源后面的流控按钮:

在弹出的表单中填写下面内容:

注意,这里勾选的时并发线程数限制,也就是说这个查询功能最多使用5个线程,而不是5QPS.如果查询商品的接口每秒处理2个请求,则5个线程的实际QPS在10左右,而超出的请求自然会被拒绝.

我们使用Jemeter测试,每秒发送100个请求:

最终测试接口如下:

进入查询购物车的请求每秒在100,而在查询商品时却只剩下每秒100左右,符合我们的预期.

此时如果我们通过页面访问购物车的其他接口,例如添加购物车,修改购物车商品数量,发现不受影响:

响应时间非常短,这就证明线程隔离起到了作用,尽管查询购物车这个接口并发很高,但是它能使用的线程资源被限制了,因此会不会影响到其他的接口.

1.5 服务熔断

在上一个章节,我们利用了线程隔离对查询购物车业务进行了隔离,保护了购物车服务的其他接口,由于查询商品的功能耗时较高,在加上线程隔离限定了线程数为5,导致接口吞吐能力有限,最终QPS只有10 左右,这就导致了几个问题.

第一,超出的QPS上限的请求只能抛出异常,从而导致购物车的查询失败,但是从业务角度来说,即便没有查询到最新的商品信息,购物车也应该展示给用户,用户体验更好,也就是给查询失败设置一个降级处理逻辑 .

第二,由于查询商品的延迟较高,从而导致 查询购物车的响应时间也变长,这样不仅拖慢了购物车服务,消耗了购物车服务的更多资源,而且用户体验也很差,对于商品服务这种不太健康的接口,我们应该直接停止调用,直接走降级逻辑,避免影响到当前服务,也就是将商品接口熔断.

1.5.1 编写降级逻辑

触发限流或熔断后的请求不一定要直接报错,也可以返回一些默认数据或者友好提示 ,用户体验会更好.

给FeignClient编写失败后的降级有两种方式:

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

这里我们演示方式二的降级处理:
步骤一 : 在hm-api模块中给ItemClient定义降级处理类,实现FallbackFactory:

代码如下:

java 复制代码
package com.hmall.api.client.fallback;

import com.hmall.api.client.ItemClient;
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import com.hmall.common.exception.BizIllegalException;
import com.hmall.common.utils.CollUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;

import java.util.Collection;
import java.util.List;

@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);
            }
        };
    }
}

步骤二: 在hm-api模块中的com.hmall.api.config.DefaultFeignConfig类中将ItemClientFallback注册为一个Bean.

步骤三: 在hm-api模块中的ItemClient接口中使用ItemClientFallbackFactory:

启动后,再次测试,发现限流的请求不再报错,走了降级逻辑:

但是未被限流的请求延时依然很高:

1.5.2 服务熔断l

查询商品的RT较高,从而导致查询购物车的RT事件也变得很长,这样不仅拖慢了购物车服务,消耗了购物车很多的资源,而且用户体验也很差,对于商品服务这种不太健康的接口,我们应该停止调用,直接走降级逻辑,避免影响到当前服务,这也就是将商品查询接口熔断 ,当商品服务接口恢复正常之后,再允许调用,这其实就是熔断器的工作模式了.

Sentinel中的断路器不仅仅可以统计某个接口的慢请求比例,还可使统计异常请求比例,当这些比例超出阈值时,就会熔断 该接口,即拦截访问接口的一切请求,降级处理,当该接口恢复正常时,再放行对于该接口的请求,断路器的工作状态切换有一个状态机来控制:

状态机包括是三个状态:

  • closed: 关闭状态,断路器放行所有请求,并开始统计异常比例,慢请求比例,超过阈值则切换到open状态
  • open: 打开状态,服务调用被熔断,访问被熔断的服务会拒绝访问,并快速失败,直接走降级逻辑,Open状态持续一段时间后进入half-open状态.
  • half-open: 半开状态,放行一次请求,根据执行结果来判断接下来的操作.
    • 请求成功: 则切换到closed状态
    • 请求失败: 则切换到open状态

我们可以在控制台通过点击簇点后的"熔断"按钮来配置熔断策略:

在弹出的表格中这样填写:

这种是按照慢调用比例来做熔断,上述配置的含义是:

  • RT超过200毫秒的请求就是慢调用
  • 统计最近的1000ms内的最少5次请求,如果慢调用比例不超过0.5,则触发熔断
  • 熔断持续时长20s

配置完成之后,再次利用jemeter进行测试,可以发现:

在一开始一段时间是允许访问的,后来触发熔断之后,查询商品服务的接口通过QPS直接为0,所有请求都被熔断了,而查询购物车的本身没有受到影响.

此时整个购物车查询服务的平均RT影响不大:

相关推荐
编程大师哥2 分钟前
vxe-table 透视表分组汇总及排序基础配置
java
8***848216 分钟前
spring security 超详细使用教程(接入springboot、前后端分离)
java·spring boot·spring
9***J62818 分钟前
Spring Boot项目集成Redisson 原始依赖与 Spring Boot Starter 的流程
java·spring boot·后端
M***Z21029 分钟前
SQL 建表语句详解
java·数据库·sql
v***79430 分钟前
Spring Boot 热部署
java·spring boot·后端
执笔论英雄30 分钟前
【RL】python协程
java·网络·人工智能·python·设计模式
galaxyffang41 分钟前
认证、会话管理、授权的区别
java
未名编程1 小时前
Windows 下如何部署 Nacos 并导入配置文件
java·windows
boonya1 小时前
Java中Plugin设计模式的规范应用
java·spring·设计模式·插件模式
杰克尼1 小时前
3. 分巧克力
java·数据结构·算法