引言
上篇文章已经讲过 SpringCloud Sentinel集成到微服务项目中,接下来我们继续学习怎么使用sentinel控制台对微服务进行限流,熔断,降级等一系列操作。
控制台
接下来我们单独讲解每一个菜单按钮
实时监控
实时监控: 可以看到微服务中接口的调用情况。
簇点监控
簇点监控: 展示当前微服务中,所有可用的资源,可以对这些资源进行熔断、降级、流控的相关配置,默认是将controller控制层中的接口作为资源显示出来
流控规则
流控规则:对相关的资源设置流量控制规则,一般在高并发流量的场景里面,为了降低系统宕机情况,就会限制访问流量来提高系统的稳定性。
哪些场景中,需要使用流量控制呢???
秒杀、下单、订单回流、消息型场景、API网关限流等情况
QPS流控规则
QPS流控规则: 限制每秒钟访问的请求数量。
访问/demo01
接口,当1秒钟超过2个请求时候,超过的请求将返回错误信息。
当然,也可以自己在代码中通过 @SentinelResource
注解,指定流控、降级之后的回调方法
并发线程数
并发线程数规则: 当请求达到设置的最大线程数时候,在这些线程处理响应结束之前,如果此时还有线程到达,那么这个线程就会被流控。
打开两个浏览器,同时访问 /demo03
接口(注意:这个接口中写了暂停3秒的逻辑),此时第二个访问的会出现流控。
直接流控模式
直接流控模式: 直接对添加流控的资源进行流量限制,默认就是采用这种限流模式
关联流控模式
关联流控模式:两个资源之间存在关联关系,假设A资源和B资源存在关联关系,对A资源进行流控规则,关联了B资源,那么此时如果A资源满足流控规则后,就会限流B资源,这就是关联流控模式。
- 这里模拟一下【关联流控模式】,首先由两个资源:
/demo04
和/demo05
,给/demo04
添加流控规则,并且关联/demo05
资源。 - 使用jemeter访问
/demo05
资源,访问的过程中,浏览器单独访问/demo04
资源,此时/demo04
资源会发生流控。
打开jemeter,模拟大量请求访问/demo05
接口,之后浏览器访问/demo04
接口,此时可以发现/demo04
发生了流控。
注意:关联流控模式下,只有关联资源才会触发目标资源的流控规则。
链路流控模式
链路流控模式:根据接口的调用链路来实现流量控制,假设:A资源被B、C两个资源调用,也就是存在两个链路:B-->A、C-->A;那么就可以采用链路流控模式,对【B-->A】调用链路进行流控限制。
- 这里采用
/demo06
、/demo07
、demo
三个资源,其中/demo06
和/demo07
都会调用demo
资源。 - 在sentinel控制台中,给【demo】资源添加流控规则,入口资源设置成
/demo06
。 - 这个时候,就会对
/demo06
进行限流控制
-
打开浏览器,多次刷新页面访问/demo06
接口,此时会触发流控规则,而多次访问/demo07
接口不会触发流控规则。
注意:sentinel默认是不会维护调用链路的,需要我们自己在
application.yml
配置文件中配置
xml
spring:
# 配置 sentinel 控制台
cloud:
sentinel:
# 让 sentinel 维护调用链路树
web-context-unify: false
快速失败(流控效果)
快速失败:当发生流控的时候,直接快速返回失败信息。
Warm Up(流控效果)
Warm Up是【预热流控】的一种机制,所谓的预热流控,是指:当大量请求到达时候,假设一开始只处理3个请求,其他请求全部失败,之后逐渐增加处理更多的请求,例如:3个、6个、10个依次递增。sentinel默认的冷加载因子是3,它会根据【threshold / 3】开始,经过预热之后,逐渐达到阈值threshold。
使用jemeter访问/demo02
接口,查看监控图如下所示。
预热流控一般适用于积分流量场景下,什么是积分流量呢???
积分流量是指:在某一段时间内可能出现大量的请求,但是过了那段时间,请求有变得平稳了,这种场景下就适用于预热流控
排队等待(流控效果)
排队等待:请求在达到设定的阈值时候,多余的请求会在指定的时间里面排队等待被处理,如果在指定时间里面,还没有被执行,那就直接失败。
使用jemeter每隔5秒钟发起10个请求访问/demo02
接口,运行结束之后,查看监控界面。
从上图中就可以看出,使用【排队等待】方式就已经能够很好的解决多余请求的处理问题,而不会出现快速失败的情况。为了验证排队等待的效果,你可以设置一个快速失败的方式,再次测试接口,查看监控界面。
从上图可以看到,快速失败方式下,会出现5个接口调用成功,5个接口调用失败,并且会有一个5秒钟的时间间隔(自己设置的时间间隔),排队等待就是利用这个时间间隔,处理处于等待中的请求,从而实现流控。
熔断规则
熔断降级规则有三种,分别是:慢调用比例 、异常比例 、异常数。当触发熔断之后,不会去调用微服务接口,是否直接返回快速失败。
慢调用比例
慢调用比例:请求的响应时长超过给定的RT,并且满足【最小请求数】中满调用的占比达到阈值,则触发熔断规则。
打开jemeter,访问 /demo03
接口,此时会出发熔断规则。
异常比例
异常比例:在指定的请求数量里面,出现异常的请求达到指定比例,则触发熔断规则。
接口 /exception
模拟抛出异常即可,然后使用jemeter调用测试。
异常数
异常数:在最小请求数里面,出现异常的次数满足阈值,则发生熔断。
热点规则
热点规则:是针对接口请求中的热点参数进行流量控制,什么是热点参数呢???比如:一个查询接口中,根据商品名称查询,有:【手机】、【电脑】,假设用户大部分都查询【手机】,所以【手机】就相当于是一个热点参数,那就可以对针对这个查询参数的接口,进行流量控制。注意:热点参数规则,必须是使用 @SentinelResource
注解定义的资源。
- 编写测试方法。
java
@SentinelResource(value = "/api/sentinel/dashboard/parameter", blockHandler = "blockMethod")
@GetMapping("/parameter")
public String parameter(@RequestParam("id") String id, @RequestParam("name") String name) {
System.out.println("parameter接口调用成功.id=" + id + ", name=" + name);
return "parameter接口调用成功.id=" + id + ", name=" + name;
}
public String blockMethod(String id, String name, BlockException e) {
return "【" + name + "】触发热点参数流控规则";
}
- 配置热点参数规则。
- 浏览器访问
localhost:8899/api/sentinel/dashboard/parameter?id=1&name=手机
,多次刷新,此时就会触发热点参数流控规则。
以上就是热点参数规则的配置。
系统规则
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性
Load 自适应(仅对 Linux/Unix-like 机器生效)
系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5;
CPU usage(1.5.0+ 版本)
当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏;
平均 RT
当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒;
并发线程数
当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护;
入口 QPS
当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
- 配置系统规则
- 使用jemeter压测
- 测试结果
授权规则
很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。
来源访问控制根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
来源访问控制规则(AuthorityRule)
非常简单,主要有以下配置项:
- resource:资源名,即限流规则的作用对象。
- limitApp:对应的黑名单/白名单,不同 origin 用 , 分隔,如 appA,appB。
- strategy :限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名单模式。
配置授权规则:
- 代码编写
实现com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser接口,在parseOrigin
方法中区分来源,并交给spring管理
注意:如果引入CommonFilter
,此处会多出一个
java
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class MyRequestOriginParser implements RequestOriginParser {
/**
* 通过request获取来源标识,交给授权规则进行匹配
* @param request
* @return
*/
@Override
public String parseOrigin(HttpServletRequest request) {
// 标识字段名称可以自定义
String origin = request.getParameter("serviceName");
// if (StringUtil.isBlank(origin)){
// throw new IllegalArgumentException("serviceName参数未指定");
// }
return origin;
}
}
- 测试:origin是order的请求不通过。
集群流控
为什么要使用集群流控呢?假设我们希望给某个用户限制调用某个 API 的总 QPS 为 50,但机器数可能很多(比如有 100 台)。这时候我们很自然地就想到,找一个 server 来专门来统计总的调用量,其它的实例都与这台 server 通信来判断是否可以调用。这就是最基础的集群流控的方式。
另外集群流控还可以解决流量不均匀导致总体限流效果不佳的问题。假设集群中有 10 台机器,我们给每台机器设置单机限流阈值为 10 QPS,理想情况下整个集群的限流阈值就为 100 QPS。不过实际情况下流量到每台机器可能会不均匀,会导致总量没有到的情况下某些机器就开始限流。因此仅靠单机维度去限制的话会无法精确地限制总体流量。而集群流控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果
集群流控中共有两种身份:
- Token Client:集群流控客户端,用于向所属 Token Server 通信请求token。集群限流服务端会返回给客户端结果,决定是否限流
- Token Server :即集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则
判断是否应该发放 token(是否允许通过)
Sentinel 集群流控支持限流规则和热点规则两种规则,并支持两种形式的阈值计算方式:
- 集群总体模式 :
即限制整个集群内的某个资源的总体 qps 不超过此阈值
- 单机均摊模式 :
单机均摊模式下配置的阈值等同于单机能够承受的限额
,token server 会根据连接数来计算总的阈值(比如独立模式下有 3个 client 连接到了 token server,然后配的单机均摊阈值为 10,则计算出的集群总量就为30),按照计算出的总的阈值来进行限制。这种方式根据当前的连接数实时计算总的阈值,对于机器经常进行变更的环境非常适合
启动方式
Sentinel 集群限流服务端有两种启动方式:
- 独立模式(Alone) ,即作为独立的 token server 进程启动,独立部署,隔离性好 ,但是需要额外的部署操作。
独立模式适合作为 Global Rate Limiter 给集群提供流控服务
- 嵌入模式(Embedded) ,即作为内置的 token server 与服务在同一进程中启动。在此模式下,集群中各个实例都是对等的,token server 和 client 可以随时进行转变,因此无需单独部署,灵活性比较好。但是隔离性不佳 ,需要限制 token server 的总QPS,防止影响应用本身。
嵌入模式适合某个应用集群内部的流控
云上版本 AHAS Sentinel 提供开箱即用的全自动托管集群流控能力,无需手动指定/分配 token server 以及管理连接状态,同时支持分钟小时级别流控、大流量低延时场景流控场景,同时支持 Istio/Envoy 场景的 Mesh 流控能力
扩展知识点
统一异常处理BlockException
自定义BlockException
异常处理,可以针对不同的异常情况,响应不同的结果,当然你也可以不使用统一的异常处理,那就是需要在每一个方法中使用 @SentinelResource
注解定义异常处理方法。
java
package com.gitcode.demo.exception;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
/**
* @version 1.0.0
* @Date: 2023/4/25 20:32
* @Copyright (C) ZhuYouBin
* @Description: 统一异常处理【BlockException】
*/
@Component
public class CustomBlockExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
// TODO 在这里就可以自定义返回响应数据
String msg = "";
if (e instanceof FlowException) {
msg = "流控异常";
} else if (e instanceof DegradeException) {
msg = "服务降级异常";
} else if (e instanceof ParamFlowException) {
msg = "热点参数流控异常";
} else if (e instanceof SystemBlockException) {
msg = "触发系统保护规则异常";
} else if (e instanceof AuthorityException) {
msg = "授权规则不通过异常";
}
// 响应结果
response.setStatus(500);
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
// 返回JSON数据
new ObjectMapper().writeValue(response.getWriter(), msg);
}
}
到此,Sentinel控制台配置相关规则就介绍完啦。