目录
Sentinel 的组成部分:
- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
官方文档:https://sentinelguard.io/zh-cn/index.html
源码地址:https://github.com/alibaba/Sentinel
安装sentinel控制台
sentinel官方提供了UI控制台,方便我们对系统做限流设置。
1.将其拷贝到一个你能记住的非中文目录,然后运行jar包
java -jar sentinel-dashboard-1.8.1.jar
2.然后访问localhost:8080即可看到控制台页面,默认的账户和密码都是sentinel
微服务整合sentinel
1.引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2.配置控制台地址
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080 #sentinel控制台地址
实时监控
访问微服务的任意端点,触发sentinel监控
簇点链路
簇点链路︰就是项目内的调用链路,链路中被监控的每个接口就是一个资源。默认情况下sentinel会监控SpringMVC的每一个端点(Endpoint),因此Springivc的每一个端点(Endpoint)就是调用链路中的一个资源。
簇点链路释:请求进入服务中的controller的莫个方法,然后方法调用service,service调用mapper形成的链路
Endpoint:可以理解为controller的方法
流控规则
流控模式
- 直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式
- 关联:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
- 链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流
流控模式_直接
每秒单机阀值超过1时就对它限流
一秒快速多次访问后
流控模式_关联
满足下面条件可以使用关联模式:
- 两个有竞争关系的资源
- 一个优先级较高,一个优先级较低
当update的每秒单机阀值超过1时就对query做限流
流控模式_链路
Sentinel默认只标记Controller中的方法为资源,如果要标记其它方法(例:service),需要利用@SentinelResource注解,例:
@SentinelResource("goods")
public void queryGoods() {
System.err.println("查询商品");
}
Sentinel默认会将Controller方法做context整合,导致链路模式的流控失效,需要修改application.yml
spring:
cloud:
sentinel:
web-context-unify: false #关闭context整合
例:有save和query两个请求都需要要访问goods业务,针对从query进去的请求做统计并做限流
流控效果
流控效果是指请求达到流控阈值时应该采取的措施,包括三种:
- 快速失败:达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。
- warm up:预热模式,对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值。
- 排队等待:让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长
流控效果_快速失败
快速失败方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝并抛出FlowException异常。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
流控效果_预热模式
预热模式是应对服务冷启动的一种方案。请求阈值初始值是threshold / coldFactor,持续指定时长后,逐渐提高到threshold值。避免了启动时请求过高而到值服务故障
threshold:最大预值
coldFactor:冷启动的值,默认值是3(也就意味着刚启动服务时只用承担1/3的请求)
例:设置QPs的threshold为10,预热时间为5秒,那么初始阈值就是10/3,也就是3,然后在5秒后逐渐增长到10
实时监控结果(启动时能处理的值为3)
流控效果_排队等待
当请求超过QPs阈值时,快速失败和warm up会拒绝新的请求并抛出异常。而排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。(起到了流量削峰效果)
例:QPS=5,意味着每200ms处理一个队列中的请求; timeout = 2000,意味着预期等待超过2000
ms的请求会被拒绝并抛出异常
实时监控结果(每秒平缓的处理5个QPS)
热点规则
热点参数限流
热点参数限流对默认的SpringMVc资源无效,只有使用@SentinelResource注解去声明的资源才有效
热点参数限流是分别统计参数值相同的请求,判断是否超过QPs阈值。
配置示例:
代表的含义是:对hot这个资源的0号参数(第一个参数)做统计,每1秒相同参数值的请求数不能超过5
参数例外项
设置第一个参数的相同值每秒只能处理两个请求,例外项101可以处理5个,102可以处理10个
授权规则
授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式。
- 白名单:来源( origin)在白名兽内的调用者允许访问
- 黑名单:来源(origin)在黑名单内的调用者不允许访问
释:需要根据调用来源来判断该次请求是否允许放行,这时候就可以使用 Sentinel 的来源访问控制的授权规则功能。
Sentinel是通过RequestOriginParser这个接口的parseOrigin来获取请求的来源的。默认返回结果为default
例:现在需要对gateway过来的请求做放行,其它请求一律拦截
1.在gateway中使用过滤器自定义一个过滤器工厂(这里使用AddRequestHeader为例添加origin的请求头)
spring:
cloud:
gateway:
routes:
- id: order_service
uri: lb://orderservice
predicates:
- Path=/order/**
filters:
#添加名为origin的请求头,值为gateway
- AddRequestHeader=origin, gateway #sentinel白名单权限
2.可以尝试从request中获取一个名为origin的请求头,作为origin的值
@Component
public class HeaderOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
//获取请求头
String origin = httpServletRequest.getHeader("origin");
//非空判断,是空则不是网关过来的请求
if (StringUtils.isEmpty(origin)) {
origin = "ROUTE";
}
return origin;
}
}
白名单规则测试
直接访问服务方式
通过gateway访问方式
黑名单规则测试
结果如上面的白名单一样
自定义异常结果
默认情况下,发生限流、降级、授权拦截时,都会抛出异常到调用方。如果要自定义异常时的返回结果,需要实现BlockExceptionHandler接口:
而BlockException包含很多个子类,分别对应不同的场景:
例:
@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(
HttpServletRequest request,
HttpServletResponse response, BlockException e) throws Exception {
String msg = "未知异常";
int status = 429;
if (e instanceof FlowException) {
msg = "请求被限流了";
} else if (e instanceof ParamFlowException) {
msg = "请求被热点参数限流";
} else if (e instanceof DegradeException) {
msg = "请求被降级了";
} else if (e instanceof AuthorityException) {
msg = "没有权限访问";
status = 401;
}
//设置响应类型,编码
response.setContentType("application/json;charset=utf-8");
//设置响应状态码
response.setStatus(status);
response.getWriter().println("{\"msg\": " + msg + ", \"status\": " + status + "}");
}
}
随便设置一个限流规则查看返回值的效果