PS
:本文为作者学习黑马程序员Springcould视频笔记实际技术参考价值不大,文章将持续更新。
文章目录
- 一. 什么是Sentienl
- 1. 介绍
- 2. 与Hystrix对比
- 3. 主要特性
- 二. Sentienl安装配置
- 1. 下载安装包
- 2. 启动
- 三. Sentienl的使用
- 1. Sentienl的整合
- 2. 簇点链路
- 四. 流量控制
- 1. 流控模式
- 1. 直接模式
- 2. 关联模式
- 3. 链路模式
- 2. 流控效果
- 1. warm up
- 2. 排队等待
- 3. 热点参数限流
- 1. 全局参数限流
- 2. 热点参数限流
- 3. 规则配置注意事项
- 五. 隔离与降级
- 1. FeignClient整合Sentinel
- 1. 雪崩问题
- 2. 线程隔离
- 3. FeignClient整合Sentinel
- 2. 线程隔离(舱壁模式)
- 1. sentinel的线程隔离
- 3. 熔断降级
- 1. 慢调用
- 2. 异常比例、异常数
- 六. 自定义异常处理
- 1. SentinelResource配置
- 1. blockHandler
- 2. fallback
- 3. exceptionsToIgnore
- 2. 全局异常处理
- 1. 异常类型
- 2. 自定义异常处理
- 七. 授权规则
- 1. 授权规则
- 1. 基本规则
- 2. 如何获取origin
- 3. 给网关添加请求头
- 4. 配置授权规则
- 八. 系统规则
- 九. 规则持久化
- 1. 规则管理模式
- 1. pull模式
- 2. push模式
- 2. 实现push模式
- 1. 引入依赖
- 2. 配置nacos地址
- 3. 修改sentinel-dashboard源码
- 4. 修改nacos依赖
- 5. 添加nacos支持
- 6. 配置nacos数据源
- 7. 修改前端页面
- 8. 重新编译、打包项目
- 9.启动
一. 什么是Sentienl
1. 介绍
Sentinel是阿里巴巴的开源项目,提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性,完美的取代了Hystrix在微服务生态中的地位,具有丰富的应用场景,完备的实时监控,广泛的开源生态,以及完善的 SPI 扩展点。官网地址 : 点击跳转
在SpringCloud当中支持多种服务保护技术: Netfix Hystrix,Sentinel,Resilience4J。
2. 与Hystrix对比
详细的区别可以参考 : 点击跳转
Sentinel | Hystrix | |
---|---|---|
隔离策略 | 信号量隔离 | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于慢调用比例或异常比例 | 基于失败比率 |
实时指标实现 | 滑动窗口 | 滑动窗口(基于 RxJava) |
规则配置 | 支持多种数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件的形式 |
基于注解的支持 | 支持 | 支持 |
限流 | 基于 QPS,支持基于调用关系的限流 | 有限的支持 |
流量整形 | 支持慢启动、匀速排队模式 | 不支持 |
系统自适应保护 | 支持 | 不支持 |
控制台 | 开箱即用,可配置规则、查看秒级监控、机器发现等 | 不完善 |
常见框架的适配 | Servlet、Spring Cloud、Dubbo、gRPC 等 | Servlet、Spring Cloud Netflix |
3. 主要特性
二. Sentienl安装配置
1. 下载安装包
在安装Sentienl之前我们需要先准备好Sentienl的安装包,我们可在GitHub下载:点击跳转
可以看到最新的版本是1.8,下载名为 sentinel-dashboard-1.8.1.jar 的jar包
这个jar包其实底层是一个基于Springboot的Web应用。
2. 启动
不论是Windows还是Linux都只要在存放该jar包的非中文目录下执行以下命令就可以启动:
java -jar sentinel-dashboard-1.8.1.jar
如果要修改Sentinel的默认端口、账户、密码,可以通过下列配置:
配置项 | 默认值 | 说明 |
---|---|---|
server.port | 8080 | 服务端口 |
sentinel.dashboard.auth.username | sentinel | 默认用户名 |
sentinel.dashboard.auth.password | sentinel | 默认密码 |
例如,修改端口:
java -Dserver.port=8090 -jar sentinel-dashboard-1.8.1.jar
启动成功后访问: http://[主机地址]:8080,列如http://localhost:8080,就可以看到sentinel的控制台了:
需要输入账号和密码,默认都是:sentinel
登录后,发现一片空白,什么都没有,这是因为我们还没有与微服务整合。
三. Sentienl的使用
1. Sentienl的整合
在安装完Sentienl之后我们就可以开始Sentienl的使用了,首先创建一个工程将下面的依赖添加到项目的POM文件中:
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
修改application.yaml文件,添加下面内容:
server:
port: 8088
spring:
application:
name: orderservice
cloud:
sentinel:
transport:
dashboard: localhost:8080 # Sentienl的服务地址
注意
: Sentienl的后台默认使用了懒加载的技术,本地启动好微服务后,后台并不会立马显示注册的微服务信息,需要我们访问任意该微服务的断点,Sentienl才能识别到注册的微服务。
2. 簇点链路
当请求进入微服务时,首先会访问DispatcherServlet,然后进入Controller、Service、Mapper,这样的一个调用链就叫做簇点链路。簇点链路中被监控的每一个接口就是一个资源。
默认情况下sentinel会监控SpringMVC的每一个端点(Endpoint,也就是controller中的方法),因此SpringMVC的每一个端点(Endpoint)就是调用链路中的一个资源。
流控、熔断等都是针对簇点链路中的资源来设置的,因此我们可以点击对应资源后面的按钮来设置规则:
- 流控:流量控制
- 降级:降级熔断
- 热点:热点参数限流,是限流的一种
- 授权:请求的权限控制
四. 流量控制
1. 流控模式
1. 直接模式
点击资源后面的【流控】按钮,就可以弹出表单,
表单中可以填写限流规则,如下:
列如上面的配置的意思就是: 限制 /order/{orderId}这个资源的单机QPS为1,即每秒只允许1次请求,超出的请求会被拦截并报错。
在添加限流规则时,点击高级选项,可以选择三种流控模式:
- 直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式
- 关联:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
- 链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流
2. 关联模式
关联模式:统计与当前资源相关的另一个资源,另一个资源触发阈值时,对当前资源限流
语法说明:当/write资源访问量触发阈值时,就会对/read资源限流(触发报错信息),避免影响/write资源。
使用场景:比如用户支付时需要修改订单状态,同时用户要查询订单。查询和修改操作会争抢数据库锁,产生竞争。业务需求是优先支付和更新订单的业务,因此当修改订单业务触发阈值时,需要对查询订单业务限流。
3. 链路模式
链路模式:只针对从指定链路访问到本资源的请求做统计,判断是否超过阈值。
配置示例:
例如有两条请求链路:
- /test1 --> /common
- /test2 --> /common
如果只希望统计从/test2进入到/common的请求,则可以这样配置:
当从/test2 资源路径进入到 /common 请求触发阈值时,对当前资源限流。
有些资源是自定义的方法而并非Controller,这时我们自己可以通过@SentinelResource注解来标记要监控的方法列如:
@SentinelResource("goods")
public void queryGoods(){
System.err.println("查询商品");
}
链路模式中,是对不同来源的两个链路做监控。但是sentinel默认会给进入SpringMVC的所有请求设置同一个root资源,会导致链路模式失效。
我们需要关闭这种对SpringMVC的资源聚合,修改order-service服务的application.yml文件:
spring:
cloud:
sentinel:
web-context-unify: false # 关闭context整合
重启微服务,访问资源后,可以查看到sentinel的簇点链路规则中,出现了我们刚刚标记了的资源:
业务情况说明: /order/query 接口中调用了queryGoods方法
点击goods资源后面的流控按钮,就可以配置限流信息了:
2. 流控效果
在流控的高级选项中,还有一个流控效果选项:
流控效果是指请求达到流控阈值时应该采取的措施,包括三种:
-
快速失败:达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。
-
warm up:预热模式,对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值。
-
排队等待:让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长
1. warm up
阈值一般是一个微服务能承担的最大QPS,但是一个服务刚刚启动时,一切资源尚未初始化(冷启动),如果直接将QPS跑到最大值,可能导致服务瞬间宕机。
warm up也叫预热模式,是应对服务冷启动的一种方案。请求阈值初始值是 maxThreshold / coldFactor,持续指定时长后,逐渐提高到maxThreshold值。而coldFactor的默认值是3。
例如,我设置QPS的maxThreshold为10,预热时间为5秒,那么初始阈值就是 10 / 3 ,也就是3,然后在5秒后逐渐增长到10。
同时我们还可以设置预热时长:
2. 排队等待
当请求超过QPS阈值时,快速失败和warm up 会拒绝新的请求并抛出异常。
而排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。
工作原理
例如:QPS = 5,意味着每200ms处理一个队列中的请求;timeout = 2000,意味着预期等待时长超过2000ms的请求会被拒绝并抛出异常。
那什么叫做预期等待时长呢?
比如现在一下子来了12 个请求,因为每200ms执行一个请求,那么:
- 第6个请求的预期等待时长 = 200 * (6 - 1) = 1000ms
- 第12个请求的预期等待时长 = 200 * (12-1) = 2200ms
现在,第1秒同时接收到10个请求,但第2秒只有1个请求,此时QPS的曲线这样的:
如果使用队列模式做流控,所有进入的请求都要排队,以固定的200ms的间隔执行,QPS会变的很平滑:
平滑的QPS曲线,对于服务器来说是更友好的。
3. 热点参数限流
1. 全局参数限流
之前的限流是统计访问某个资源的所有请求,判断是否超过QPS阈值。而热点参数限流是分别统计参数值相同的请求,判断是否超过QPS阈值。
例如,一个根据id查询商品的接口:
访问/goods/{id}的请求中,id参数值会有变化,热点参数限流会根据参数值分别统计QPS,统计结果:
当id=1的请求触发阈值被限流时,id值不为1的请求不受影响。
配置示例:
代表的含义是:对hot这个资源的0号参数(第一个参数)做统计,每1秒相同参数值的请求数不能超过5
2. 热点参数限流
刚才的配置中,对查询商品这个接口的所有商品一视同仁,QPS都限定为5。
而在实际开发中,可能部分商品是热点商品,例如秒杀商品,我们希望这部分商品的QPS限制与其它商品不一样,高一些。那就需要配置热点参数限流的高级选项了:
结合上一个配置,这里的含义是对0号的long类型参数限流,每1秒相同参数的QPS不能超过5,有两个例外:
•如果参数值是100,则每1秒允许的QPS为10
•如果参数值是101,则每1秒允许的QPS为15
3. 规则配置注意事项
注意: 热点参数限流对默认的SpringMVC资源无效,所以资源要使用@SentinelResource注解标记资源
如下:
访问该接口,可以看到我们标记的hot资源出现了:
这里不要点击hot后面的按钮,页面有BUG
点击左侧菜单中热点规则菜单:
点击新增,填写表单:
五. 隔离与降级
1. FeignClient整合Sentinel
限流是一种预防措施,虽然限流可以尽量避免因高并发而引起的服务故障,但服务还会因为其它原因而故障。
而要将这些故障控制在一定范围,避免雪崩,就要靠线程隔离(舱壁模式)和熔断降级手段。
1. 雪崩问题
比如服务A要访问服务D,但是这时服务D出现了故障,那么服务A去调用服务D时必然会造成阻塞,那么服务A的资源就得不到释放,随着这样的请求越来越多服务A里面的资源一定会被耗尽,那么服务A也就出现故障了,这就是雪崩问题。
2. 线程隔离
线程隔离:调用者在调用服务提供者时,给每个调用的请求分配独立线程池,或者限制每个服务在调用的时候能使用的线程数量,出现故障时,最多消耗这个线程池内资源,避免把调用者的所有资源耗尽。
可以看到,不管是线程隔离还是熔断降级,都是对客户端(调用方)的保护。需要在调用方 发起远程调用时做线程隔离、或者服务熔断。
而我们的微服务远程调用都是基于Feign来完成的,因此我们需要将Feign与Sentinel整合,在Feign里面实现线程隔离和服务熔断。
3. FeignClient整合Sentinel
SpringCloud中,微服务调用都是通过Feign来实现的,因此做客户端保护必须整合Feign和Sentinel。
修改application.yml文件,开启Feign的Sentinel功能:
feign:
sentinel:
enabled: true # 开启feign对sentinel的支持
业务失败后,不能直接报错,而应该返回用户一个友好提示或者默认结果,这个就是失败降级逻辑。
给FeignClient编写失败后的降级逻辑
①方式一:FallbackClass,无法对远程调用的异常做处理
②方式二:FallbackFactory,可以对远程调用的异常做处理,我们选择这种
这里我们演示方式二的失败降级处理。
在feing-api项目中定义类,实现FallbackFactory:
@Slf4j
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
@Override
public UserClient create(Throwable throwable) {
return new UserClient() {
@Override
public User findById(Long id) {
log.error("查询用户异常", throwable);
return new User();
}
};
}
}
在feing-api项目中的DefaultFeignConfiguration类中将UserClientFallbackFactory注册为一个Bean:
@Bean
public UserClientFallbackFactory userClientFallbackFactory(){
return new UserClientFallbackFactory();
}
在feing-api项目中的UserClient接口中使用UserClientFallbackFactory:
@FeignClient(value = "userservice", fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
重启后,访问一次微服务接口,然后查看sentinel控制台,可以看到新的簇点链路:
可以给资源添加各种规则,当达到故障触发条件时,就会调用其中的错误逻辑代码,返回一个空的用户。
2. 线程隔离(舱壁模式)
线程隔离有两种方式实现:
- 线程池隔离
- 信号量隔离(Sentinel默认采用)
如图:
线程池隔离:给每个服务调用业务分配一个线程池,利用线程池本身实现隔离效果。
信号量隔离:不创建线程池,而是计数器模式,记录业务使用的线程数量,达到信号量上限时,禁止新的请求。
1. sentinel的线程隔离
点击【流控】按钮在添加限流规则时,可以选择两种阈值类型:
-
QPS:就是每秒的请求数,在快速入门中已经演示过
-
线程数:是该资源能使用用的tomcat线程数的最大值。也就是通过限制线程数量,实现线程隔离(舱壁模式)。
3. 熔断降级
熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。
断路器控制熔断和放行是通过状态机来完成的:
状态机包括三个状态:
- closed:关闭状态,断路器放行所有请求,并开始统计异常比例、慢请求比例。超过阈值则切换到open状态
- open:打开状态,服务调用被熔断,访问被熔断服务的请求会被拒绝,快速失败,直接走降级逻辑。Open状态5(熔断时长)秒后会进入half-open状态
- half-open:半开状态,放行一次请求,根据执行结果来判断接下来的操作。
- 请求成功:则切换到closed状态
- 请求失败:则切换到open状态
断路器熔断策略有三种:慢调用、异常比例、异常数
1. 慢调用
慢调用:业务的响应时长(RT)大于指定时长的请求认定为慢调用请求。在指定时间内,如果请求数量超过设定的最小数量,慢调用比例大于设定的阈值,则触发熔断。
例如:
解读:RT超过500ms的调用是慢调用,统计最近10000ms内的请求,如果请求量超过10次,并且慢调用比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。
2. 异常比例、异常数
异常比例或异常数:统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例阈值(或超过指定异常数),则触发熔断。
例如,一个异常比例设置:
解读:统计最近1000ms内的请求,如果请求量超过10次,并且异常比例不低于0.4,则触发熔断。
一个异常数设置:
解读:统计最近1000ms内的请求,如果请求量超过10次,并且异常比例不低于2次,则触发熔断。
六. 自定义异常处理
1. SentinelResource配置
1. blockHandler
@SentinelResource注解的value属性是一个唯一标识的资源名称,在后台可以个根据这个资源名称配置接口的流控等规则,而blockHandler的值则是一个方法名,当该接口达到Sentienl后台某个阈值触发错误时就会调用blockHandler指定的方法。
默认的异常提示是:Blocked by Sentinel (flow limiting)
@GetMapping("/byResource")
@SentinelResource(value = "byResource", blockHandler = "handleException")
public CommonResult byResource() {
return new CommonResult(200, "按资源名称限流测试OK", new Payment(2020L, "serial001"));
}
public CommonResult handleException(BlockException exception) {
return new CommonResult(444, exception.getClass().getCanonicalName() + "\t 服务不可用");
}
也可以使用类的方式配置错误的处理方法,这样可以解决代码耦合度高膨胀的问题,配置方式如下:
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class, // 兜底方法存放的类
blockHandler = "handlerException2") // 类中要设置为兜底方法的方法名
public CommonResult customerBlockHandler() {
return new CommonResult(200, "按客戶自定义", new Payment(2020L, "serial003"));
}
public class CustomerBlockHandler {
public static CommonResult handlerException(BlockException exception) {
return new CommonResult(4444, "按客戶自定义,global handlerException----1");
}
public static CommonResult handlerException2(BlockException exception) {
return new CommonResult(4444, "按客戶自定义,global handlerException----2");
}
}
值得注意的是
: blockHandler指定的兜底方法只会被触发阈值的时候调用,而java逻辑代码的异常不会调用其指定的方法。
2. fallback
@SentinelResource注解的fallback属性可以指定一个业务逻辑运行异常的处理方法,当借口中有业务逻辑代码发生错误就会触发指定的方法作为兜底方法。
@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback", fallback = "handlerFallback")
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
} else if (result.getData() == null) {
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
public CommonResult handlerFallback(@PathVariable Long id, Throwable e) {
Payment payment = new Payment(id, "null");
return new CommonResult<>(444, "兜底异常handlerFallback,exception内容 " + e.getMessage(), payment);
}
当然为了解决代码的耦合度和冗余问题我们也可以通过类的方式来配置:
@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback", fallbackClass=CustomerBlockHandler.class,fallback = "handlerException2")
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
} else if (result.getData() == null) {
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
public class CustomerBlockHandler {
public static CommonResult handlerException(BlockException exception) {
return new CommonResult(4444, "按客戶自定义,global handlerException----1");
}
public static CommonResult handlerException2(BlockException exception) {
return new CommonResult(4444, "按客戶自定义,global handlerException----2");
}
}
值得注意的是
: fallback指定的兜底方法只有java逻辑代码异常时才会被调用,而Sentienl后台配置的限制触发时并不会调用。
若blockHandler和fallback都进行了配置,则被限流降级而抛出BlockException时只会进blockHandler指定的处理逻辑。
3. exceptionsToIgnore
@SentinelResource注解的exceptionsToIgnore属性可以指定忽略哪些异常:
当触发这些指定的异常时就不会调用fallback或者blockHandler指定的兜底方法。
@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler",
exceptionsToIgnore = {IllegalArgumentException.class} )
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
} else if (result.getData() == null) {
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
2. 全局异常处理
默认情况下,发生限流、降级、授权拦截时,都会抛出异常到调用方。异常结果都是flow limmiting(限流)。这样不够友好,无法得知是限流还是降级还是授权拦截。
除了上面以方法为单位的异常处理,其实我们还可以一次性将所有的异常处理配置好。
1. 异常类型
而如果要自定义异常时的返回结果,需要实现BlockExceptionHandler接口:
public interface BlockExceptionHandler {
/**
* 处理请求被限流、降级、授权拦截时抛出的异常:BlockException
*/
void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception;
}
这个方法有三个参数:
- HttpServletRequest request:request对象
- HttpServletResponse response:response对象
- BlockException e:被sentinel拦截时抛出的异常
这里的BlockException包含多个不同的子类:
异常 | 说明 |
---|---|
FlowException | 限流异常 |
ParamFlowException | 热点参数限流的异常 |
DegradeException | 降级异常 |
AuthorityException | 授权规则异常 |
SystemBlockException | 系统规则异常 |
2. 自定义异常处理
我们可以定义一个异常处理类
@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 + "}");
}
}
配置完成后Sentienl会自动识别到异常的处理逻辑。
重启测试,在不同场景下触发的异常类型不同,会返回不同的异常消息。
七. 授权规则
1. 授权规则
授权规则可以对请求方来源做判断和控制。
1. 基本规则
授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式。
-
白名单:来源(origin)在白名单内的调用者允许访问
-
黑名单:来源(origin)在黑名单内的调用者不允许访问
点击左侧菜单的授权,可以看到授权规则:
-
资源名:就是受保护的资源,例如/order/{orderId}
-
流控应用:是来源者的名单,
- 如果是勾选白名单,则名单中的来源被许可访问。
- 如果是勾选黑名单,则名单中的来源被禁止访问。
比如:
我们允许请求从gateway到order-service,不允许浏览器访问order-service,那么白名单中就要填写网关的来源名称(origin)。
2. 如何获取origin
Sentinel是通过RequestOriginParser这个接口的parseOrigin来获取请求的来源的。
public interface RequestOriginParser {
/**
* 从请求request对象中获取origin,获取方式自定义
*/
String parseOrigin(HttpServletRequest request);
}
这个方法的作用就是从request对象中,获取请求者的origin值并返回。
默认情况下,sentinel不管请求者从哪里来,返回值永远是default,也就是说一切请求的来源都被认为是一样的值default。
因此,我们需要自定义这个接口的实现,让不同的请求,返回不同的origin。
例如order-service服务中,我们定义一个RequestOriginParser的实现类:
@Component
public class HeaderOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
// 1.获取请求头
String origin = request.getHeader("origin");
// 2.非空判断
if (StringUtils.isEmpty(origin)) {
origin = "blank";
}
return origin;
}
}
定义完成后Sentienl会自动识别到里面的逻辑,就可以直接在后台配置了。
我们会尝试从request-header中获取origin值。
这样只要浏览器和网关过来的请求获取的origin头不一样,那么是不是能够得到一个有效的区分。
3. 给网关添加请求头
既然获取请求origin的方式是从reques-header中获取origin值,我们必须让所有从gateway路由到微服务的请求都带上origin头。
这个需要利用之前学习的一个GatewayFilter来实现,AddRequestHeaderGatewayFilter。
修改gateway服务中的application.yml,添加一个defaultFilter:
spring:
cloud:
gateway:
default-filters:
- AddRequestHeader=origin,gateway # 添加一个名为origin的请求头值为gateway
routes:
这样,从gateway路由的所有请求都会带上origin头,值为gateway。而从其它地方到达微服务的请求则没有这个头。
4. 配置授权规则
接下来,我们添加一个授权规则,放行origin值为gateway的请求。
现在,我们直接跳过网关,访问order-service服务:
八. 系统规则
Sentinel系统自适应限流从整体维度对应用入口流量进行控制,结合应用的Load、CPU使用率、总体平均RT、入口QPS和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流景和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
官网 :点击跳转
九. 规则持久化
现在,sentinel的所有规则都是内存存储,重启后所有规则都会丢失。在生产环境下,我们必须确保这些规则的持久化,避免丢失。
1. 规则管理模式
规则是否能持久化,取决于规则管理模式,sentinel支持三种规则管理模式:
- 原始模式:Sentinel的默认模式,将规则保存在内存,重启服务会丢失。
- pull模式
- push模式
1. pull模式
pull模式:控制台将配置的规则推送到Sentinel客户端,而客户端会将配置规则保存在本地文件或数据库中。以后会定时去本地文件或数据库中查询,更新本地规则。
2. push模式
push模式:控制台将配置规则推送到远程配置中心,例如Nacos。Sentinel客户端监听Nacos,获取配置变更的推送消息,完成本地配置更新。
2. 实现push模式
1. 引入依赖
在微服务模块引入sentinel监听nacos的依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2. 配置nacos地址
在微服务模块中的application.yml文件配置nacos地址及监听的配置信息:
spring:
cloud:
sentinel:
datasource:
flow:
nacos:
server-addr: localhost:8848 # nacos地址
dataId: orderservice-flow-rules
groupId: SENTINEL_GROUP
rule-type: flow # 还可以是:degrade、authority、param-flow
3. 修改sentinel-dashboard源码
SentinelDashboard默认不支持nacos的持久化,需要修改源码。
解压sentinel源码包:
然后并用IDEA打开这个项目,结构如下:
4. 修改nacos依赖
在sentinel-dashboard源码的pom文件中,nacos的依赖默认的scope是test,只能在测试时使用,这里要去除:
将sentinel-datasource-nacos依赖的scope去掉:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
5. 添加nacos支持
在sentinel-dashboard的test包下,已经编写了对nacos的支持,我们需要将其拷贝到main下。
修改nacos地址
然后,还需要修改测试代码中的NacosConfig类:
修改其中的nacos地址,让其读取application.properties中的配置:
在sentinel-dashboard的application.properties中添加nacos地址配置:
nacos.addr=localhost:8848
6. 配置nacos数据源
另外,还需要修改com.alibaba.csp.sentinel.dashboard.controller.v2包下的FlowControllerV2类:
让我们添加的Nacos数据源生效:
7. 修改前端页面
接下来,还要修改前端页面,添加一个支持nacos的菜单。
修改src/main/webapp/resources/app/scripts/directives/sidebar/目录下的sidebar.html文件:
将其中的这部分注释打开:
修改其中的文本:
8. 重新编译、打包项目
运行IDEA中的maven插件,编译和打包修改好的Sentinel-Dashboard:
9.启动
启动方式跟官方一样:
java -jar sentinel-dashboard.jar
如果要修改nacos地址,需要添加参数:
java -jar -Dnacos.addr=localhost:8848 sentinel-dashboard.jar
在这个新的流控页签里面添加的流控规则就会进入Nacos