Sentinel服务哨兵简介
中文文档:https://github.com/alibaba/Sentinel/wiki
下载地址:https://github.com/alibaba/Sentinel/releases
小结:
Nacos:注册中心,解决服务的注册与发现
Ribbon:客户端的负载均衡器,服务集群的负载均衡
OpenFeign:声明式的HTTP客户端,服务远程调用
nacos-config:配置中心,配置文件的中心化管理
1.0:服务雪崩效应
级联失效、级联故障、cascading failure
解决办法
1:限流
2:仓壁模式 --- 隔离
3:断路器
1.1:Sentinel是什么
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
1.1.1:Sentinel 具有以下特征
丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
1.1.2:Sentinel 分为两个部分:
- **核心库(Java 客户端)**不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- **控制台(Dashboard)**基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
1.2:Sentinel与Hystrix对比
Sentinel是阿里对hystrix的替换
Sentinel | Hystrix | |
---|---|---|
隔离策略 | 信号量隔离(并发线程数) | 线程池隔离/信号量隔离 |
熔断降级策略 | 慢调用比例、异常比率、异常数 | 异常比率 |
实时指标实现 | 滑动窗口 | 滑动窗口(基于 RxJava) |
规则配置(rule) | 支持多种数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件的形式 |
基于注解的支持 | 支持 | 支持 |
调用链路信息 | 支持同步调用 | 不支持 |
限流QPS | 基于 QPS / 并发数,支持基于调用关系的限流 | 不支持 |
流量整形 | 支持慢启动、匀速器模式 | 不支持 |
系统负载保护 | 支持 | 不支持 |
实时监控 API | 各式各样 | 较为简单 |
控制台 | 开箱即用,可配置规则、查看秒级监控、机器发现等 | 不完善 |
常见框架的适配 | Servlet、Spring Cloud、Dubbo、gRPC 等 | Servlet、Spring Cloud Netflix |
Hystrix已经停更说明
1.3.专业术语
什么是熔断?
A 服务调用B服务的某个功能,由于网络不稳定问题、B服务卡机等 导致功能时间超长,如果这样的情况次数太多,我们就可以直接将B断路了(A不在请求B接口) 凡是调用B的直接返回降级数据 ,不必等待B的超长执行。这样B的故障问题 就不会级联影响到A 。 某些被调用的服务故障,触发系统保护规则 一种被动保护
什么是降级?
整个网站都处于流量高峰期,服务器压力剧增,根据当前业务情况及流量,对一些服务和页面进行有策略的降级(停止正常服务,所有的调用直接返回降级数据) 以此缓解服务器资源的压力,以保证核心业务的正常运行,同时也保持了客户或大部分客户得到正确的响应。由于服务器资源紧张,主动去关闭某些服务,降级某些服务 一种 主动保护
异同:
相同点 :1.为了保证集群大部分服务的可用性和可靠性,防止崩溃 牺牲小我。
2.用户最终都是体验到某个功能不可用
不同点:1.熔断是被调用方故障 触发的系统的主动规则
2.降级是基于全局考虑,停止一些正常服务,释放资源。
什么是限流?
对打入服务的请求 流量进行控制 使服务能够承担不超过自己能力的流量压力 对入口流量进行控制
1.4使用sentinel的三个步骤
1.定义资源 ------ 定义哪些资源需要保护
2.定义规则
3.检验规则是否生效
先把可能需要保护的资源定义好,之后再配置规则,只要有了资源,我们就可以在任何时候灵活的定义各种流量控制规则,在编码的时候只需要考虑这个代码是否需要保护,如果需要保护 ,就将之定义为一个资源。
1.4.1 定义资源的方式
参考官网:如何使用 · alibaba/Sentinel Wiki · GitHub
方式1 主流框架的默认适配
为了减少开发的复杂程度,我们对大部分的主流框架,例如 Web Servlet、Dubbo、Spring Cloud、gRPC、Spring WebFlux、Reactor 等都做了适配。您只需要引入对应的依赖即可方便地整合 Sentinel。可以参见: 主流框架的适配。 和这些框架默认适配, 默认所有的请求都是要受保护的资源。
方式2 抛出异常的方式定义资源
SphU
包含了 try-catch 风格的 API。用这种方式,当资源发生了限流之后会抛出 BlockException
。这个时候可以捕捉异常,进行限流之后的逻辑处理。示例代码如下:
// 1.5.0 版本开始可以利用 try-with-resources 特性(使用有限制)
// 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
try (Entry entry = SphU.entry("resourceName")) {
// 被保护的业务逻辑
// do something here...
} catch (BlockException ex) {
// 资源访问阻止,被限流或被降级
// 在此处进行相应的处理操作
}
特别地,若 entry 的时候传入了热点参数,那么 exit 的时候也一定要带上对应的参数(exit(count, args)
),否则可能会有统计错误。这个时候不能使用 try-with-resources 的方式。另外通过 Tracer.trace(ex)
来统计异常信息时,由于 try-with-resources 语法中 catch 调用顺序的问题,会导致无法正确统计异常数,因此统计异常信息时也不能在 try-with-resources 的 catch 块中调用 Tracer.trace(ex)
。
手动 exit 示例:
Entry entry = null;
// 务必保证 finally 会被执行
try {
// 资源名可使用任意有业务语义的字符串,注意数目不能太多(超过 1K),超出几千请作为参数传入而不要直接作为资源名
// EntryType 代表流量类型(inbound/outbound),其中系统规则只对 IN 类型的埋点生效
entry = SphU.entry("自定义资源名");
// 被保护的业务逻辑
// do something...
} catch (BlockException ex) {
// 资源访问阻止,被限流或被降级
// 进行相应的处理操作
} catch (Exception ex) {
// 若需要配置降级规则,需要通过这种方式记录业务异常
Tracer.traceEntry(ex, entry);
} finally {
// 务必保证 exit,务必保证每个 entry 与 exit 配对
if (entry != null) {
entry.exit();
}
}
热点参数埋点示例:
Entry entry = null;
try {
// 若需要配置例外项,则传入的参数只支持基本类型。
// EntryType 代表流量类型,其中系统规则只对 IN 类型的埋点生效
// count 大多数情况都填 1,代表统计为一次调用。
entry = SphU.entry(resourceName, EntryType.IN, 1, paramA, paramB);
// Your logic here.
} catch (BlockException ex) {
// Handle request rejection.
} finally {
// 注意:exit 的时候也一定要带上对应的参数,否则可能会有统计错误。
if (entry != null) {
entry.exit(1, paramA, paramB);
}
}
SphU.entry()
的参数描述:
方式3 返回布尔值方式定义资源
方式4 注解方式定义资源
方式5 异步调用支持
.........TODO
常用的是 方式 1 2 4 ,特别是方式2 可以任意指定受保护的资源
1.4.2 规则的种类
流量控制规则 flowrule
熔断降级规则 degraderule
系统保护规则
访问控制规则
热点规则
1.5:Sentinel架构
#接口概览
http://127.0.0.1:8720/api
#获取资源的metrics(指标)信息
http://127.0.0.1:8720/cnode?id=/jifen/{jifenId}
#获取流控规则接口
http://127.0.0.1:8720/getRules?type=flow
#设置规则接口
http://127.0.0.1:8720/setRules
1.6:微服务整合Sentinel
查看主流框架的适配 https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E6%B5%81%E6%A1%86%E6%9E%B6%E7%9A%84%E9%80%82%E9%85%8D
找到SpringBoot/SpringCloud
https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel
第一步:添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
查看sentinel 的依赖版本,咱们当前依赖 是 1.8.0版本 所以下载 1.8.0 版本的dashboard
第二步:配置
#默认是8719,如果8719被占用 +1,一直到没有被占用
spring:
cloud:
sentinel:
transport:
port: 8719 # 当前服务 和 sentinel 控制台 通讯的端口
dashboard: localhost:8777 # 控制台的地址
#开启饥额加载,当项目启动立即完成初始化,默认是懒加载
spring.cloud.sentinel.eager=true
1.7:Sentinel控制台安装
https://github.com/alibaba/Sentinel/releases
找到咱们依赖对应的版本 下载
注意:启动 Sentinel 控制台需要 JDK 版本为 1.8 及以上版本。
功能
1:实时观看每个资源的访问情况(qps)
2:通过控制台,设置流控规则(qps、线程数)、还可以设置熔断降级的规则(慢调用比例、异常比例、异常数)
java -jar sentinel-dashboard-1.8.0.jar --server.port=8777
访问 localhost:8777
默认 用户 密码 sentinel
每个服务 都需要配置 连接sentinel 控制台的信息
打开控制台 ,看到如下界面,在 簇点链路中 可以看到 咱们定义的资源,当然 咱们暂时 没有 自己定义资源,在这看到的是 sentinel 和主流框架 适配, 默认的资源
1.8 Sentinel服务流控
Sentinel通过流量控制(flow control)以及熔断降级来保护系统资源
1.8.1 QPS超过阈值直接失败
流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
QPS:Queries-per-second,每秒资源被访问的次数
当被访问资源的QPS超过阈值,直接进行限流(限制访问),从而保护资源,
触发条件:/order/test1当qps>1时,会触发流控
![image-20211224105849402](https://gllspictures.oss-cn-beijing.aliyuncs.com/img/20230609114615.png)
效果是 访问 /order/test1 超出qps的阈值 就会被限流 默认返回
线程数
![image-20211224110026290](https://gllspictures.oss-cn-beijing.aliyuncs.com/img/20230609114626.png)
服务端允许一个线程访问,超出一个线程 会被限流
QPS超过阈值关联失败
![image-20211224110140545](https://gllspictures.oss-cn-beijing.aliyuncs.com/img/20230609114631.png)
访问 /order/test2 超出 1 qps ,则 /order/test1 会被限流, 注意 不是限流 /order/test2
线程超过阈值关联失败
qps超过阈值链路失败
spring:
cloud:
sentinel:
transport:
port: 8719
dashboard: localhost:8777
eager: true
web-context-unify: false # 开启链路失败的效果
feign:
sentinel:
enabled: true # 开启链路失败的效果
//OrderController
@RequestMapping("/test3")
public String test3(String name){
// 演示 流控模式 链路
String result = orderService.test3(name);
return "cloud-order-test3:"+result;
}
@RequestMapping("/test4")
public String test4(String name){
String result = orderService.test3(name);
return "cloud-order-test4:"+result;
}
//OrderServiceImpl
@Override
public String test3(String name) {
if(name!=null&&name.equals("stock")){
return stockApi.testStock();
}else if(name!=null&&name.equals("account")){
return accountApi.testAccount();
}else if(name!=null&&name.equals("ex")){
int i = 5/0;
System.out.println("ex");
}
return "order-service-test3";
}
这里 咱们得场景是 从 订单服务的 test3 接口 和 test4 接口 作为 入口 都去调用 库存服务的接口, 在 控制台 需要在 订单服务中 针对 远程的资源 指定 链路流控规则, 在 库存服务指定 没有效果
![image-20230613172353324](https://gllspictures.oss-cn-beijing.aliyuncs.com/img/20230613172355.png)
触发条件:GET:http://cloud-stock/stock/testStock 有两个入口,分别为/order/test3和/order/test4
当/order/test3访问qps>1 那么/order/test3就不能访问受保护资源,其他入口照常访问
线程超过阈值链路失败
流控效果:warm up
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP
)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。详细文档可以参考 流量控制 - Warm Up 文档,具体的例子可以参见 WarmUpFlowDemo。
![image-20230613173549012](https://gllspictures.oss-cn-beijing.aliyuncs.com/img/20230613173554.png)
预热 为什么要预热? 因为服务刚启动,或者 不稳定的时候 可能有别的行为,比如初始化工作要做,会占用系统资源,导致无法一开始就满足大的并发,但是系统启动一段时间之后,稳定之后,就有足够多的资源来承担高并发高负载 ,所以 有预热或者叫冷启动这种 流控效果。
不好演示 ,了解一下
流控效果:匀速排队
匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER
)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细文档可以参考 流量控制 - 匀速器模式,具体的例子可以参见 PaceFlowDemo。
该方式的作用如下图所示:
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
注意:匀速排队模式暂时不支持 QPS > 1000 的场景。
之前的流控效果都是超出阈值了 就不处理了,不让进来,而某些场景 每个请求都必须要处理,就算超出系统负载也得处理,此时 就需要用到 匀速排队这种流控效果了,超出了我的负载,我就时间换空间, 匀速排队处理。但是 这个也是有上限的 ,比如
![image-20211225202711743](https://gllspictures.oss-cn-beijing.aliyuncs.com/img/20230609114747.png)
比如 每秒2000 个请求,除以 超时时间 10秒 ,每秒200 个请求,没有超出阈值1000 则 可以正常匀速处理,如果每秒100000个请求,除以10 超时时间,则 是10000 ,10000大于阈值1000 ,那多出来的请求 还是会丢
1.9Sentinel服务熔断降级
慢调用比例
慢调用比例 (
SLOW_REQUEST_RATIO
):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。断路器的工作流程:
一旦熔断,断路器的状态是Open(所有的请求都不能进来)
当熔断时长结束,断路器的状态是half-Open(可以允许一个请求进来)
如果接下来的请求正常,断路器的状态是close(资源就自恢复)
如果接下来的请求不正常,断路器的状态是open
触发条件
1:1s内最小请求数必须大于5
2:man请求/总请求 > 0.6
异常比例
- 异常比例 (
ERROR_RATIO
):当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是[0.0, 1.0]
,代表 0% - 100%。
异常数
2.0Sentinel-热点key规则
注意:默认的请求路径资源 不生效 需要手动指定 资源 比如 通过 @SentinelResource 注解指定
触发条件
1:必须有一个参数,参数的值可以任意 例如:http://localhost:9002/order/test7?name=jack
2:接口访问qps>1
触发条件
1:第一个参数的值如果为jack,那么qps阈值为1
2:第一个参数的值如果为rose,那么qps阈值为5
3:第一个参数的值如果为其他,那么qps阈值为1
注意事项:
2.1 授权规则
![image-20211225213547492](https://gllspictures.oss-cn-beijing.aliyuncs.com/img/20230609114755.png)
把cloud-order设置为黑名单,就不能调用/stock/testStock这个资源了,注意 这里的 cloud-order 指的不是服务名 而是请求头
演示案例:
cloud-order 远程调用cloud-stock 服务,
在 cloud-order 服务配置 下面的代码 利用feign 的拦截器 统一设置请求头
package com.glls.order.feign;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;
/**
* @ClassName : CustomerRequestInterceptor
* @Author : glls
* @Date: 2021/12/25 21:20
* @Description :
*/
@Component
public class CustomerRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
//统⼀设置请求头
requestTemplate.header("source","cloud-order"); //order 服务 发送feign 远程调用携带这个头
}
}
在 cloud-stock服务 获取请求头 ,这里 用到sentinel 提供的接口 RequestOriginParser,这里获取到的 头信息 就是上文 白名单 或者黑名单的内容
package com.glls.stock.config;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* @ClassName : CustomerRequestOriginParser
* @Author : glls
* @Date: 2021/12/25 21:19
* @Description :
*/
@Component
public class CustomerRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
System.out.println("come in");
//获取请求头的source
String source1 = request.getHeader("source"); //获取调用方发过来的头 ,然后在授权规则 配置
System.out.println(source1);
return source1;
}
}
测试 把cloud-order 设置为黑名单,发现 无法调用cloud-stock 服务了,注意 这个授权规则 需要在 cloud-stock 设置
其实 在流控那里配置的 针对来源 也是这样配置的通过在调用方 和 被调用方配置两个 配置类,指定 来源的内容。不用在演示了吧,你懂得。
2.2 系统规则
- Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的
maxQps * minRt
计算得出。设定参考值一般是CPU cores * 2.5
。 - CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0)。
- RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
- 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
2.3 定义资源的方式
2.3.1 适配主流框架
controller 层的接口 会被当做 默认资源,
跟 主流框架 默认适配的资源 全局异常处理器 无法处理,又想返回自定义的响应 ,可以使用 BlockExceptionHandler
package com.glls.order.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 org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @date 2023/6/14
* @desc
*/
@Service
public class CustomerBlockHandler 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 if (ex instanceof ParamFlowException) {
msg = "热点参数限流";
} else if (ex instanceof SystemBlockException) {
msg = "系统规则(负载/...不满足要求)";
} else if (ex instanceof AuthorityException) {
msg = "授权规则不通过";
}
// http状态码
response.setStatus(500);
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Type", "application/json;charset=utf-8");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"error\":\"自定义流控响应\"}");
}
}
2.3.2 @SentinelResource 注解
参考资料
咱们可以 借助 这个注解的 属性 blockHandler/
blockHandlerClass ,fallback/
fallbackClass
资源定义 流控自定义异常处理 熔断降级自定义处理
以抛出异常的方式定义资源
@Override
public String test3(String testName) {
// 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
try (Entry entry = SphU.entry("resourceName")) {
if (testName != null && testName.equals("stock")) {
return stockFeignApi.testStock();
} else if (testName != null && testName.equals("account")) {
return accountFeignApi.testAccount();
} else if (testName != null && testName.equals("ex")) {
int i = 5 / 0;
} else {
return "other";
}
} catch (BlockException ex) {
// 资源访问阻止,被限流或被降级
// 在此处进行相应的处理操作
log.error("资源触发规则");
}
return "other";
}
以注解的形式定义资源
@Override
@SentinelResource(value = "test3resource",fallback="test3Fallback",blockHandler = "test3BlockHandler",blockHandlerClass = CustomBlockHandler.class)
public String test3(String testName) {
if (testName != null && testName.equals("stock")) {
return stockFeignApi.testStock();
} else if (testName != null && testName.equals("account")) {
return accountFeignApi.testAccount();
} else if (testName != null && testName.equals("ex")) {
int i = 5 / 0;
} else {
return "other";
}
return "other";
}
注意 资源触发规则 则 走 blockHandler , 资源出现 其他异常 ,则走 fallback
2.3.3 捕获异常的方式
// 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
try (Entry entry = SphU.entry("resourceName")) {
if (testName != null && testName.equals("stock")) {
return stockApi.testStock();
} else if (testName != null && testName.equals("account")) {
return accountApi.testAccount();
} else if (testName != null && testName.equals("ex")) {
int i = 5 / 0;
} else {
return "other";
}
} catch (BlockException ex) {
// 资源访问阻止,被限流或被降级
// 在此处进行相应的处理操作
log.error("资源触发规则");
}
2.4 规则持久化
原始模式
如果不做任何修改,Dashboard 的推送规则⽅式是通过 API 将规则推送⾄客户端并直接更新 到内存中: 这种做法的好处是简单,⽆依赖;坏处是应⽤重启规则就会消失,仅⽤于简单测试,不能⽤ 于⽣产环境。
Pull模式
FileRefreshableDataSource 定时从指定⽂件中读取规则JSON⽂件【图中的本地⽂件】,如 果发现⽂件发⽣变化,就更新规则缓存。 FileWritableDataSource 接收控制台规则推送,并根据配置,修改规则JSON⽂件【图中的本 地⽂件】。
为什么说是 pull 模式,拉模式 ,因为 最终 生效的规则 是在内存中配置的,咱们需要把持久化到本地文件的规则 拉倒内存中去,所以称为 拉模式 ,咱们可以在 dashboard 和 本地文件 修改规则,然后在内存中生效,这里的拉 主要就是通过定时任务 拉去本地json 文件中的规则。
实现步骤:
在 微服务端 添加依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
</dependency>
核心代码:
Push模式 推荐
动态规则扩展 · alibaba/Sentinel Wiki · GitHub
修改 sentinel-dashboard 源码 实现 dashboard 推送数据到 nacos
注意 push 模式的 流控规则 持久化 可能会推送不到 nacos 配置中心 ,原因可能是 浏览器 缓存导致的 可以尝试清除缓存 再进行流控规则制定
生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(ZooKeeper, Nacos, Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是 配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。这样的流程就非常清晰了:
9.3.1:控制台改造
改动 sentinel 的源码
懒人包
注意namespace 需要提前创建好
9.3.2:微服务端
<!-- sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- sentinel的nacos持久化 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
9.3.3:添加配置
spring:
cloud:
sentinel:
transport:
port: 8721
dashboard: localhost:8777
eager: true #饥饿加载
web-context-unify: false
datasource:
flow:
nacos:
server-addr: ${nacos.server-addr}
username: ${nacos.username}
password: ${nacos.password}
namespace: ${nacos.namespace}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-flow-rules
rule-type: flow
degrade:
nacos:
server-addr: ${nacos.server-addr}
username: ${nacos.username}
password: ${nacos.password}
namespace: ${nacos.namespace}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-degrade-rules
rule-type: degrade
param-flow:
nacos:
server-addr: ${nacos.server-addr}
username: ${nacos.username}
password: ${nacos.password}
namespace: ${nacos.namespace}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-param-rules
rule-type: param-flow
system:
nacos:
server-addr: ${nacos.server-addr}
username: ${nacos.username}
password: ${nacos.password}
namespace: ${nacos.namespace}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-system-rules
rule-type: system
authority:
nacos:
server-addr: ${nacos.server-addr}
username: ${nacos.username}
password: ${nacos.password}
namespace: ${nacos.namespace}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-authority-rules
rule-type: authority
nacos:
server-addr: localhost:8848
username: nacos
password: nacos
namespace: sentinel
切记 一定要把 浏览器的缓存清掉
2.5 sentinel 对网关进行保护
1.添加依赖
<!--整合 sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
2.修改配置文件
在网关服务添加配置类
javapackage com.glls.gateway.config; import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter; import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler; import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager; import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler; import org.springframework.beans.factory.ObjectProvider; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.reactive.result.view.ViewResolver; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import javax.annotation.PostConstruct; import java.util.Collections; import java.util.List; /** * @ClassName : GatewayConfiguration * @Author : glls * @Date: 2021/12/27 14:46 * @Description : */ @Configuration public class GatewayConfiguration { private final List<ViewResolver> viewResolvers; private final ServerCodecConfigurer serverCodecConfigurer; public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) { this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList); this.serverCodecConfigurer = serverCodecConfigurer; } @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() { // Register the block exception handler for Spring Cloud Gateway. return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer); } @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public GlobalFilter sentinelGatewayFilter() { return new SentinelGatewayFilter(); } @PostConstruct public void init(){ // 自定义 流控后的 降级数据 GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() { // 网关限流了 就会调用这个 回调方法 // Mono Flux spring5 特性 响应式编程 后面可以研究下 @Override public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) { Mono<ServerResponse> body = ServerResponse.ok().body(Mono.just("{\"error\":\"gateway流控返回\"}"), String.class); return body; } }); } }
4.网关流控控制台
-Dcsp.sentinel.app.type=1
5.演示 网关 流控效果