Sentinel服务熔断和流控

Sentinel服务熔断和流控

简介

Sentinel

​ 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量 为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。

源码地址:https://github.com/alibaba/Sentinel

官方文档:https://github.com/alibaba/Sentinel/wiki

https://sentinelguard.io/zh-cn/docs/circuit-breaking.html

Spring Cloud Alibaba Sentinel 同时兼具了熔断器和流控的功能。

Sentinel具有以下特征:

  • 丰富的应用场景 : Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控

    制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。

  • 完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。

  • 广泛的开源生态 : Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、

    gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。

  • 完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。例如定制规则管理、适配数据源等。

    Spring的spi是通过ClassLoader去META-INF/spring.factories加载class,然后反射实例化返回。像SpringBoot用这种方式去加载一些自动配置类,即引入xx-starter就能够自动向spring容器中注入许多配置好的组件。

阿里云提供了 企业级的 Sentinel 服务,应用高可用服务 AHAS

Sentinel和Hystrix对比

https://github.com/alibaba/Sentinel/wiki/Sentinel-与-Hystrix-的对比

Sentinel Hystrix
隔离策略 信号量隔离 线程池隔离/信号量隔离
熔断降级策略 基于响应时间或失败比率 基于失败比率
实时指标实现 滑动窗口 滑动窗口(基于 RxJava)
规则配置 支持多种数据源 支持多种数据源
扩展性 多个扩展点 插件的形式
基于注解的支持 支持 支持
限流 基于 QPS,支持基于调用关系的限流 有限的支持
流量整形 支持慢启动、匀速器模式 不支持
系统负载保护 支持 不支持
控制台 开箱即用,可配置规则、查看秒级监控、机器发现等 不完善
常见框架的适配 Servlet、Spring Cloud、Dubbo、gRPC 等 Servlet、Spring Cloud Netflix

熔断

微服务架构的系统通常会包含多个微服务,各个微服务可能部署在不同的机器上并通过网络进行通信,那么就不可避免会遇到 "网络请求超时" 、"微服务不可用" 等问题,这就会进一步引起依赖它的微服务不可用,这样不断引发服务故障的现象称为『雪崩效应』,最终的结果是整个应用系统瘫痪。

为了解决上述问题,编程领域(参考现实生活)提出了熔断器

使用熔断器模式,如果请求出现异常,所有请求都会直接返回,而不会等待或阻塞,这样可以减少资源的浪费。

熔断器所造成的这种现象也叫『快速失败(fast fall)』。

流控

限流功能指的是 Sentinel(类似于过滤器、拦截器的效果)在收到请求后,拒绝请求的放行(至 Controller),而是直接返回,从而减少对 Controller,乃至 Service 的触发执行。

熔断和限流的区别在于,熔断是确确实实发生了错误,而限流是人为(根据设置)强行让一部分请求被打回。

sentinel安装

sentinel-dashboard 的下载安装

sentinel-dashboard 是基于 Spring Boot 开发的控制台。打包后可以直接运行,不需要额外的 Tomcat 等应用容器。Sentinel 控制台不仅能展示服务流控、熔断降级相关的数据,还可以通过配置的方式动态的为 Sentinel 客户端下发流量控制的指令

我们需要下载并安装的是 sentinel-dashBoard ,下载地址:https://github.com/alibaba/Sentinel/releases

注意:启动 sentinel-dashboard 需要 JDK 版本为 1.8 及以上版本。

使用如下命令启动控制台:

java 复制代码
java -Dserver.port=8840 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar
  • -Dserver.port=8840

    用于指定 Sentinel 控制台端口为 8840。默认是 8080 。

  • -Dproject.name=sentinel-dashboard

    指定 Sentinel 控制台程序的名称。

说明

如果你有多张网卡的话,你还需要指定使用哪张网卡(IP)来接受各个微服务上报的信息:

-Dcsp.sentinel.heartbeat.client.ip=192.168.xxx.xxx

访问网址:[http://127.0.0.1:8840]

从 1.6.0 起,sentinel-dashboard 引入基本的登录功能,默认用户名和密码都是 sentinel 。当然也可以通过 JVM 参数的方式进行修改

  • -Dsentinel.dashboard.auth.username=sentinel

    用于指定控制台的登录用户名为 sentinel ;

  • -Dsentinel.dashboard.auth.password=123456

    用于指定控制台的登录密码为 123456;如果省略这两个参数,默认用户和密码均为 sentinel;

  • -Dserver.servlet.session.timeout=7200

    用于指定 Spring Boot 服务端 session 的过期时间,如 7200 表示 7200 秒;60m 表示 60 分钟,默认为 30 分钟;

Sentinel 本身就是一个 Spring Boot 应用,所以jar 包内部的 application.properties 文件也是可以修改配置的。

Sentinel实现限流

Spring Cloud Alibaba Sentinel 可以分别用在服务的 "请求发起方" 和 "请求被调方"

  • 请求发起方使用的是 OpenFeign ,因此这种情况下 Sentinel 是和 OpenFeign 进行整合;
  • 请求被调用使用的是 Spring MVC,因此这种情况下 Sentinel 是和 Spring MVC 进行整合

同时又由于 Sentinel 兼具熔断和流控两个功能,因此这里就有 4 种情况:

  • 在服务发起方项目中,整合 OpenFeign 进行实现熔断功能;
  • 在服务发起方项目中,整合 OpenFeign 进行实现限流功能;
  • 在服务被调方项目中,整合 Spring MVC 进行实现熔断功能;
  • 在服务被调方项目中,整合 Spring MVC 进行实现限流功能。

这样以来功能上就出现了重叠冗余,因此在实际使用中我们是这样安排的:

  • 在服务发起方,Sentinel 整合 OpenFeign 实现熔断功能;
  • 在服务被调方,Sentinel 整合 Spring MVC 实现限流功能。

总结:流控针对provider,熔断降级针对consumer

sentinel实现限流

回顾前面笔记中的 "关于 Sentinel 的使用方式" 章节,在这里,我们在服务的 "被调方" 使用 Sentinel 整合 Spring MVC 进行流量控制。

在这里,Sentinel 借助 Spring MVC 框架的 "拦截器" 机制整合进入 Spring MVC ,"抢先" 在 Controller 执行之前进行流控(和熔断)的判断,从而决定当前请求是否被放行至 Controller

sentinel整合mvc

1.引入相关依赖

xml 复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- 其实真正起作用的是被关联引入的 sentinel-spring-webmvc-adapter 包 -->

2.添加配置(连接到 sentinel-dashboard)

yaml 复制代码
spring:
  cloud:
    sentinel:
      transport:
        dashboard: 127.0.0.1:8080
        port: 8719

feign:
  sentinel:
    enabled: true

3.访问 sentinel-dashboard

只需要完成上述的配置,代码不需要有任何的调整,我们就可以通过实时监控查看服务内的流量 QPS(每秒查询率) 以及平均响应时长等信息。

只有服务接口被访问的情况下,在 sentinel 里面才可以看到监控信息。这可能会让你『等』一段时间。

相关概念

  • 上下文( Context )和 context-name

Context 代表调用链路上下文。是一个根节点,在整个调用链路的开始处,Sentinel 会创建上下文 Context 对象,并且为它指定一个 name ,相当于根资源。在 Sentinel 中,不同的调用链路可能使用同一个上下文 Context 对象(共一个根节点)。在这里( 和 Spring MVC 整合 ),我们的调用链路都是在 sentinel_spring_web_context 中:

  • 资源(Resource)和 resource-name

在 Sentinel 中,对于每一份资源,Sentinel 会为赋予一个 name(或者你手动指定),和 Spring MVC 整合时,Sentinel 使用的是 URI 来作为 Controller 方法的资源名( 在这里,Controller 方法就是资源 )

流控规则

在菜单左侧的 簇点链路和流控规则都可以针对 服务接口添加流控规则:

当我们的服务接口资源被访问的时候,就会出现在 簇点链路 列表中,我们可以针对该服务接口资源配置流程控制规则

说明:

  • 资源名:表示我们针对哪个接口资源进行流控规则配置,如:/test2/{id}

  • 针对来源:表示针对哪一个服务访问当前接口资源的时候进行限流,default 表示不区分访问来源。如填写服务名称:xxx-service,表示 xxx-service 访问前接口资源的时候进行限流,其他服务访问该接口资源的时候不限流,一般就是默认为default即可。

  • 阈值类型/单机阈值:QPS,每秒钟请求数量。上图配置表示每秒钟超过2次请求的时候进行限流;当然我们可以设置线程数,表示开启 n 个线程处理资源请求,这个不是只每秒2个线程,对服务端 /test1请求,资源接口的 2 个线程都被占用的时候,其他访问失败!一般用的都是QPS

  • 是否集群:默认情况下我们的限流策略都是针对单个服务的,当然sentinel 提供了集群限流的功能。

    除非你的微服务规模特别大,一般不要使用集群模式。集群模式需要各节点与 token server 交互才可以,会增加网络交互次数,一定程度上会拖慢你的服务响应时间。

上面的限流规则用一句话说:对于任何来源的请求,当超过每秒 2 次的标准之后就直接限流,访问失败抛出 BlockException 异常!

流控规则高级选项

1.流控模式
  • 直接:当前资源达到限流标准时就直接限流,默认值
  • 关联:/important接口的重要程度要高于 /normal接口,如果,/important接口的访问压力很大,那么,可以『牺牲』掉 /normal` 接口,全力保证 /important 接口的正常运行

例如:我们在resttemplate-a微服务中,创建两个接口

java 复制代码
@GetMapping("/query")
public String query(){
    return "ok:query";
}

@GetMapping("/add")
public String add(){
    return "ok:add";
}

先访问:http://localhost:9527/add和http://localhost:9527/query

我们对/query接口进行限流,这个配置的意思就是,当每秒对/add接口的请求超过2次时,就对/query接口进行限流,要注意这样是优先保证/add不限流,牺牲的是/query接口。10s内对/add接口超过20次请求,那么就对/query接口限流

测试:

  • 链路

链路限流和关联限流的思路很像,假设我们要去请求某个微服务,该微服务有2个接口(/query和/add),而这两个接口又调用了同一个service层的方法(如:doSomething()方法),那么,我们可以『站在 doSomething的方法』的角度上进行设置:如果是 /query接口在调用service层的 doSomething方法,那么就进行限流,而 /add接口的调用就不限流,或设置为更宽松一些的流控

1.配置设置:通过配置关闭 sentinel 的 URL 收敛功能

yaml 复制代码
spring:
  application:
    name: resttemplate-a
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8840
      web-context-unify: false  #默认是开启上下文整合,所有链路在根节点下,链路监控就是将请求分开统计

2.代码设计

java 复制代码
controller层
@GetMapping("/query")
public String query(){
    return userService.doSomething();
}
@GetMapping("/add")
public String add(){
    return userService.doSomething();
}
//service层
@Component
public class UserService {
    @SentinelResource("doSomething")
    public String doSomething() {
        return "hello world";
    }
}

3.重新运行,先在界面发送请求 http://localhost:9527/query和http://localhost:9527/add,然后sentinel配置

链路配置:

4.测试:在jmeter对/add测试时不限流(如10秒钟对/add请求80次),但是同时在浏览器对/query测试时,每秒只能是2次请求。

2.流控效果
  • 快速失败:很简单的说就是达到限流标准后,请求就被拦截,直接失败。(HTTP状态码:429 too many request),默认值
  • Warm up:预热模式,也有叫冷启动,主要是为系统启动时设置预热时间,底层有预热因子是3,在系统刚启动时,使用的阈值不再是每秒多少个请求,而是设置的阈值除以预热因子,在预热的时间内,逐渐提升阈值,最后达到设置的阈值(也就是每秒多少个请求),好处是预防系统刚启动时,突发大量的请求,服务容易宕机。

1.代码设计

java 复制代码
@GetMapping("/test/{id}")
public ResponseEntity<String> test1(@PathVariable Integer id){
    return new ResponseEntity<>("ok", HttpStatus.OK);
}

2.配置设置

在10秒内,每秒允许的请求数会逐渐增加,也就是每秒的阈值逐渐提高

3.测试

  • 排队等待:也叫流量整形,它让请求以均匀的速度通过,单机阈值为每秒通过数量,其余的在队列排队等待一段时间,(即我们设置的时间,单位是毫秒),没有超过这个时间都能被及时处理,如果超过了这个等待时间针对请求的接口没有线程来处理,则抛出异常

测试:每秒50个请求

Sentinel 和 SpringMVC 整合原理

Sentinel 和 Spring MVC 的整合利用了 Spring MVC 的拦截器机制,Sentinel 实现了一个名为 SentinelWebInterceptor 的拦截器,其逻辑伪代码如下

java 复制代码
public SentinelWebInterceptor implements HandlerInterceptor {

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
        try {
            1. 初始化上下文;
            2. 熔断、流控逻辑的判断,判断当前请求是否能继续执行;
            return true; // 此时 Controller 方法会被调用。Controller 方法就是 3 。
        } catch (BlockException e) {
            4. 上述第 2 步未能通过,会抛出 BlockException ,表示请求被拒绝
            return false;
        }
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {

    } 
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
      if (发生了异常) {
          5. 业务异常。记录、统计异常信息
      }
      6. 收尾工作:曾经创建的资源该回收的回收,该清除的清除
    }

自定义限流异常信息返回

Sentinel 返回的默认信息是 Blocked by Sentinel (flow limiting),如果你对默认响应信息不满意,你可以自定义限流返回信息。

Sentinel 提供了 BlockExceptionHandler 接口。不管什么原因触发了 Sentinel 阻断用户的正常请求,Sentinel 都将『进入』到用户自定义的 BlockExceptionHandler 接口的实现类中,执行 handle 方法,并传入当前的请求、响应对象以及异常对象,并以 handle 方法的执行结果作为返回,回传给用户。

java 复制代码
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws Exception {
        String msg = null;
        if (ex instanceof FlowException) {
            msg = "该请求限流了,请稍后重试";
        } else if (ex instanceof DegradeException) {
            msg = "被熔断了";
        } else {
            msg = "其它原因";
            // ParamFlowException   "热点参数限流";
            // SystemBlockException "系统规则(负载/...不满足要求)";
            // AuthorityException   "授权规则不通过";
        }
        // http 状态码
        response.setStatus(500);
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Type", "application/json;charset=utf-8");
        response.setContentType("application/json;charset=utf-8");
        new ObjectMapper().writeValue(response.getWriter(), msg);
    }
}

需要说明的是:不仅仅是因为限流和熔断这一个原因会导致 BlockExceptionhandlerhandle 方法的执行,还有其它的原因也会调用这个handler方法,因此,需要对 handle 方法的 BlockException 参数对象进行 instanceof 判断

sentinel服务熔断降级

我们在服务的 "请求发起方" 使用 Sentinel 整合 OpenFeign 进行熔断降级

sentinel整合feign

1.添加依赖

xml 复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2.添加配置(连接到 sentinel-dashboard)

yaml 复制代码
spring:
  cloud:
    sentinel:
      transport:
        dashboard: 127.0.0.1:8840
feign:
  sentinel:
    enabled: true   ##启用 Sentinel 与 OpenFeign 整合适配器

3.代码实现spring-service-a-feign微服务,调用spring-service-b微服务

java 复制代码
@FeignClient(value = "spring-service-b",qualifier = "feignclient",
        fallback = PermFeignClientImpl.class )
public interface PermFeignClient {
	@GetMapping("/perms/{id}")
    public Perms getPermsById(@PathVariable("id") Integer id);
}
=========降级代码==============
@Component
public class PermFeignClientImpl implements PermFeignClient {
    @Override
    public Perms getPermsById(Integer id) {
        Perms perms = new Perms();
        perms.setName("服务器忙,请稍后再试");
        return perms;
    }
}
===========controller=======================
@RestController
public class PermController {
    @Autowired @Qualifier("feignclient")
    private PermFeignClient permFeignClient;
    @GetMapping("/perm")
    public Perms getPermsById(Integer id) {
        Perms perms = permFeignClient.getPermsById(id);
        return perms;
    }
}

spring-service-b微服务

java 复制代码
@GetMapping("/perms/{id}")
public Perms getPermsById(@PathVariable("id") Integer id) {
    if(id==1){
        try {
            Thread.sleep(800);   //当id为1时,800毫秒才有相应
        }catch (Exception ex){ }
    }
    if(id==2){
        throw new RuntimeException();
    }
    return permService.getPermsById(id);
}

4.熔断规则

在 sentinel-dashboard 上你可以看到有 降级规则,它指的就是『设置当满足什么条件时,对服务进行降级

慢调用比例

如下配置:在一秒内,发5次请求,如果每次请求的响应时间超过500毫秒,这种比例达到0.5(50%),就进行熔断,熔断时长就是10秒。如:1秒内有5次请求,其中有3次请求响应时间超过了500毫秒,那么这个比例就是60%,大于50%,此时就熔断,然后降级。

用jmeter测试,程序中当id=1时,每次响应都是800毫秒。所以每次的请求都大于500毫秒,失败率100%,这个时候去请求id=4的资源也是无法请求的,因为熔断了,所以也是直接降级。10s后再次请求id=4的就正常了。

异常数

如下配置:一秒内发送5次请求,如果有3次失败(异常),则直接熔断,然后降级

测试:localhost:9527/perm?id=2,结果5次都失败了,此时熔断时长是9秒,在这9秒内,如果去请求id=4的依然是降级,9s之后试着去发送id=4的请求,如果通过则进入闭合正常状态。

异常比例

如下配置:1秒钟发送5次请求,如果调用接口最终失败的比例超过了20%,则熔断9s。

yaml 复制代码
#配置
spring:
	cloud:
		sentinel:
			eager: true #取消Sentinel控制台懒加载

blockhandler fallback,blockhandler 优先级高

    }catch (Exception ex){ }
}
if(id==2){
    throw new RuntimeException();
}
return permService.getPermsById(id);

}

4.熔断规则

在 sentinel-dashboard 上你可以看到有 降级规则,它指的就是『**设置当满足什么条件时,对服务进行降级**』

[外链图片转存中...(img-QwfGPW1A-1694490781354)]

#### 慢调用比例

如下配置:在一秒内,发5次请求,如果每次请求的响应时间超过500毫秒,这种比例达到0.5(50%),就进行熔断,熔断时长就是10秒。如:1秒内有5次请求,其中有3次请求响应时间超过了500毫秒,那么这个比例就是60%,大于50%,此时就熔断,然后降级。

[外链图片转存中...(img-d74m5bKk-1694490781357)] 

用jmeter测试,程序中当id=1时,每次响应都是800毫秒。所以每次的请求都大于500毫秒,失败率100%,这个时候去请求id=4的资源也是无法请求的,因为熔断了,所以也是直接降级。10s后再次请求id=4的就正常了。

#### 异常数

如下配置:一秒内发送5次请求,如果有3次失败(异常),则直接熔断,然后降级

[外链图片转存中...(img-URgCSfH7-1694490781358)] 

测试:localhost:9527/perm?id=2,结果5次都失败了,此时熔断时长是9秒,在这9秒内,如果去请求id=4的依然是降级,9s之后试着去发送id=4的请求,如果通过则进入闭合正常状态。

#### 异常比例

如下配置:1秒钟发送5次请求,如果调用接口最终失败的比例超过了20%,则熔断9s。

[外链图片转存中...(img-bYCdjt00-1694490781358)] 





```yaml
#配置
spring:
	cloud:
		sentinel:
			eager: true #取消Sentinel控制台懒加载
```



blockhandler   fallback,blockhandler   优先级高
相关推荐
bohu831 天前
sentinel学习笔记7-熔断降级
笔记·sentinel·熔断降级·degradeslot·circuitbreaker
suweijie7681 天前
SpringCloudAlibaba | Sentinel从基础到进阶
java·大数据·sentinel
向阳12182 天前
sentinel来源访问控制(黑白名单)
java·sentinel
bohu832 天前
sentinel学习笔记1-为什么需要服务降级
笔记·学习·sentinel·滑动窗口
一个儒雅随和的男子2 天前
微服务详细教程之nacos和sentinel实战
微服务·架构·sentinel
bohu832 天前
sentinel学习笔记5-资源指标数据统计
笔记·sentinel·statisticslot
bohu833 天前
Sentinel 学习笔记3-责任链与工作流程
笔记·sentinel·责任链·processorslot
mengml_smile5 天前
Sentinel一分钟
java·开发语言·sentinel
LightOfNight12 天前
【Sentinel Go】新手指南、流量控制、熔断降级和并发隔离控制
开发语言·golang·sentinel
XMYX-012 天前
使用 Kubernetes 部署 Redis 主从及 Sentinel 高可用架构(未做共享存储版)
redis·kubernetes·sentinel