一学一做演讲视频网站,周末游做的好的网站,北京地铁建设管理公司网站,中国建设工程质量安全管理协会网站从零开始 Spring Cloud 12#xff1a;Sentinel
1.初识 Sentinel
1.1雪崩问题
1.1.1什么是雪崩问题
微服务中#xff0c;服务间调用关系错综复杂#xff0c;一个微服务往往依赖于多个其它微服务。 如图#xff0c;如果服务提供者I发生了故障#xff0c;当前的应用的部分…从零开始 Spring Cloud 12Sentinel
1.初识 Sentinel
1.1雪崩问题
1.1.1什么是雪崩问题
微服务中服务间调用关系错综复杂一个微服务往往依赖于多个其它微服务。 如图如果服务提供者I发生了故障当前的应用的部分业务因为依赖于服务I因此也会被阻塞。此时其它不依赖于服务I的业务似乎不受影响。 但是依赖服务I的业务请求被阻塞用户不会得到响应则tomcat的这个线程不会释放于是越来越多的用户请求到来越来越多的线程会阻塞 服务器支持的线程和并发数有限请求一直阻塞会导致服务器资源耗尽从而导致所有其它服务都不可用那么当前服务也就不可用了。
那么依赖于当前服务的其它服务随着时间的推移最终也都会变的不可用形成级联失败雪崩就发生了 简单来说雪崩就是当调用链上的某个微服务出现故障导致整个调用链上的微服务不可访问。
1.1.2.超时处理
可以在访问服务时设置请求的响应超时时长超过响应时长的请求中断并返回错误信息这样可以缓解雪崩发生的概率。 如果请求产生的速度快于响应等待超时并关闭连接的速度依然会可能发生雪崩。 1.1.3.舱壁模式
仓壁模式来源于船舱的设计 船舱都会被隔板分离为多个独立空间当船体破损时只会导致部分空间进入将故障控制在一定范围内避免整个船体都被淹没。
于此类似我们可以限定每个业务能使用的线程数避免耗尽整个tomcat的资源因此也叫线程隔离。 1.1.4.降级熔断
断路器模式由断路器统计业务执行的异常比例如果超出阈值则会熔断该业务拦截访问该业务的一切请求。
断路器会统计访问某个服务的请求数量异常比例 当发现访问服务D的请求异常比例过高时认为服务D有导致雪崩的风险会拦截访问服务D的一切请求形成熔断 1.1.5.限流
流量控制限制业务访问的QPS避免服务因流量的突增而故障。 限流只能避免因请求过多导致的服务故障以及因此产生的雪崩但现实中服务产生故障的原因是多种多样的。 1.1.6.总结
限流是对服务的保护避免因瞬间高并发流量而导致服务故障进而避免雪崩。是一种预防措施。
超时处理、线程隔离、降级熔断是在部分服务故障时将故障控制在一定范围避免雪崩。是一种补救措施。 关于以上内容的详细说明可以观看这个视频。 1.2.服务保护技术对比
在SpringCloud当中支持多种服务保护技术
Netfix HystrixSentinelResilience4J
早期比较流行的是Hystrix框架但目前国内实用最广泛的还是阿里巴巴的Sentinel框架这里我们做下对比
SentinelHystrix隔离策略信号量隔离线程池隔离/信号量隔离熔断降级策略基于慢调用比例或异常比例基于失败比率实时指标实现滑动窗口滑动窗口基于 RxJava规则配置支持多种数据源支持多种数据源扩展性多个扩展点插件的形式基于注解的支持支持支持限流基于 QPS支持基于调用关系的限流有限的支持流量整形支持慢启动、匀速排队模式不支持系统自适应保护支持不支持控制台开箱即用可配置规则、查看秒级监控、机器发现等不完善常见框架的适配Servlet、Spring Cloud、Dubbo、gRPC 等Servlet、Spring Cloud Netflix
1.3.Sentinel 介绍和安装
1.3.1.初识 Sentinel
Sentinel是阿里巴巴开源的一款微服务流量控制组件。
Sentinel 具有以下特征:
•丰富的应用场景Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景例如秒杀即突发流量控制在系统容量可以承受的范围、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
•完备的实时监控Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据甚至 500 台以下规模的集群的汇总运行情况。
•广泛的开源生态Sentinel 提供开箱即用的与其它开源框架/库的整合模块例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
•完善的 SPI 扩展点Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
1.3.2.安装
可以从 Github 的 Release 页面下载。
这里提供一个百度云的下载。
程序本体是一个基于 Spring Boot 开发的 jar 包通过相关 java 命令即可运行
java -jar sentinel-dashboard-1.8.1.jar如果要修改Sentinel的默认端口、账户、密码可以通过下列配置
配置项默认值说明server.port8080服务端口sentinel.dashboard.auth.usernamesentinel默认用户名sentinel.dashboard.auth.passwordsentinel默认密码
例如修改端口
java -Dserver.port8090 -jar sentinel-dashboard-1.8.1.jar启动后访问对应的端口就能看到 Sentinel 的控制台我这里是 http://localhost:8080/默认账户和密码都是sentinel。
1.4.微服务整合 Sentinel
这里我们使用之前学习创建的示例项目 cloud-demo 进行学习和演示。
加载项目并运行。 可能需要修改项目关联的 MySQL 信息以及 Nacos 信息。该项目使用 Nacos 用于服务发现需要提前运行 Nacos。 启动项目中的两个微服务 order-service 和 user-service成功启动后访问接口 http://localhost:8088/order/101会看到返回内容。
为需要整合 Sentinel 的微服务这里是 order-service添加依赖
dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-sentinel/artifactId
/dependency在微服务的配置文件中添加 Sentinel 控制台的相关配置信息
spring:cloud:sentinel:transport:dashboard: localhost:8080添加好后重启微服务。
再次请求微服务的接口这里是 http://localhost:8088/order/101然后观察 Sentinel 的控制台 可以看到微服务接口的访问状况。
2.流量控制
可以通过控制对微服务的访问流量来预防雪崩的发生。
2.1.簇点链路
当请求进入微服务时首先会访问DispatcherServlet然后进入Controller、Service、Mapper这样的一个调用链就叫做簇点链路。簇点链路中被监控的每一个接口就是一个资源。
默认情况下sentinel会监控SpringMVC的每一个端点Endpoint也就是controller中的方法因此SpringMVC的每一个端点Endpoint就是调用链路中的一个资源。 如果要对簇点链路上的其它节点进行监控就需要使用 Sentinel 的注解。 例如我们刚才访问的order-service中的OrderController中的端点/order/{orderId} 流控、熔断等都是针对簇点链路中的资源来设置的因此我们可以点击对应资源后面的按钮来设置规则
流控流量控制降级降级熔断热点热点参数限流是限流的一种授权请求的权限控制
2.2.1.示例
这里通过示例说明如何用 Sentinel 对微服务接口实现限流。
首先对要限流的接口对应的资源设置流控规则 针对来源default说明对任意来源施加限制阈值设置为QPS5说明对接口施加的限制是每秒访问次数不能超过5次。
可以使用测试工具 Jmeter 模拟对接口的访问。
打开 Jmeter加载测试样例 选择流控入门这个方案在2秒内会请求接口20次即QPS10。右键启动以执行该测试方案。
可以在结果中看到对接口请求5次成功后出现5次失败。 对示例步骤有疑惑的可以查看这个视频 2.2.流控模式
在添加限流规则时点击高级选项可以选择三种流控模式
直接统计当前资源的请求触发阈值时对当前资源直接限流也是默认的模式关联统计与当前资源相关的另一个资源触发阈值时对当前资源限流链路统计从指定链路访问到本资源的请求触发阈值时对指定链路限流 之前演示的就是直接模式。
2.2.1.关联模式
关联模式统计与当前资源相关的另一个资源触发阈值时对当前资源限流
关联模式的应用场景是如果有2个资源存在竞争关系其中一个存在大量请求时会影响另一个的性能且这两个资源有优先级区别我们需要在某一个高优先级的资源存在大量访问时确保其不会请求失败此时就可以创建一个关联模式在高优先级资源的 QPS 超过某个阈值时通过限制另一个关联资源的访问来确保高优先级资源的可用性。
下面用具体案例来说明。
假设项目中存在两个接口一个用于更新订单另一个用于查询订单。显然这两个接口都会访问数据库中的订单表如果访问的是同一个订单数据就会触发表级锁对数据的修改会影响到数据的读取反之亦然。
显然这两个接口存在优先级关系相比之下我们要尽可能让更新订单的接口不要失败。因此在这里可以使用关联模式对读取订单的接口进行限制。
先创建两个用于模拟的接口
// ...
RestController
RequestMapping(order)
public class OrderController {// ...GetMapping(/query)public String getOrderInfo(){return 订单内容;}GetMapping(/update)public String updateOrder(){return 订单已修改;}
}重启微服务 order-service。
访问新添加的两个接口以使 Sentinel 有相应的资源信息 为资源/order/query添加流控规则 这里添加的规则意味着如果每秒内访问/order/update接口的次数超过5/order/query接口就无法被访问。
可以用导入的测试案例中的 流控模式-关联 进行测试该测试会在100秒内请求/order/update接口1000次即每秒10次。理论上因为关联模式的限制在这10秒内/order/query接口会不可用。
实际执行后和理论结果相符在执行测试案例的过程中请求/order/query接口会返回Blocked by Sentinel (flow limiting)。
2.2.2.链路模式
链路模式只针对从指定链路访问到本资源的请求做统计判断是否超过阈值。如果超过对请求来源限制访问。
下面用实际案例进行说明。
假设有两个接口查询订单和创建订单它们都需要调用查询商品的 Service 方法。
// ...
public class OrderService {// ...SentinelResource(/service/order/query-goods)public String queryGoods(){return 商品信息;}
}// ...
public class OrderController {// ...GetMapping(/query)public String getOrderInfo(){orderService.queryGoods();return 订单内容;}// ...GetMapping(/save)public String saveOrder(){orderService.queryGoods();return 订单已保存;}
}注意默认情况下 Sentinel 只会监控 Controller 中的资源所以 Service 中的方法是不会出现在簇点链路中的资源列表里的。要将 Service 中的方法注册为资源需要使用SentinelResource注解。
只这样做还不够默认情况下 Sentinel 会自动将资源整合到sentinel_spring_web_context这个默认链路下在这里我们需要创建两个链路并对其中的一个进行限制所以必须关闭这种默认行为。
修改配置
spring:cloud:sentinel:web-context-unify: false # 关闭context整合重启子模块并访问两个接口。
Sentinel 控制台已经可以看到两个链路
/order/save - /service/order/query-goods/order/query - /service/order/query-goods
选择任意一个/service/order/query-goods资源添加规则 现在 Sentinel 会统计从 /order/query 到 /service/order/query-goods 的这条链路如果其 QPS 大于2就会限制对/order/query的请求。
执行测试案例中的 流控模式-链路 进行验证这个案例会分别对两个接口请求QPS 都是 4可以看到其中对/order/query的请求在每秒中只有2次成功另外2次会失败说明链路模式生效。而对/order/save的请求则都成功。
2.3.流控效果
在流控的高级选项中还有一个流控效果选项 流控效果是指请求达到流控阈值时应该采取的措施包括三种
快速失败达到阈值后新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。warm up预热模式对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化从一个较小值逐渐增加到最大阈值。排队等待让所有的请求按照先后次序排队执行两个请求的间隔不能小于指定时长
2.3.1.warm up
warm up也叫预热模式是应对服务冷启动的一种方案。请求阈值初始值是 maxThreshold / coldFactor持续指定时长后逐渐提高到maxThreshold值。而coldFactor的默认值是3.
下面用实际案例说明。
需求给/order/{orderId}这个资源设置限流最大QPS为10利用warm up效果预热时长为5秒。
流控规则设置如下 运行测试案例 流控效果warm up该测试的 QPS 是10执行时长是20秒。可以看到一开始每秒只有3条请求成功随着时间推移成功的请求会主逐渐增多。
查看 Sentinel 的实时监控会看到类似下面的图形 可以看到通过的 QPS 主键增多直到最大值10而被拒绝的 QPS 逐渐减少。
使用 warm up 这种流控效果可以让服务器的负载逐渐上升起到一个“预热的效果以避免突然的高负载导致服务器宕机。
2.3.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 曲线整形成平滑的 QPS 曲线。而平滑的QPS曲线对于服务器来说是更友好的。
下面用实际案例说明。
需求给/order/{orderId}这个资源设置限流最大QPS为10利用排队的流控效果超时时长设置为5s。
编辑流控规则 启动测试案例 流控效果队列 进行测试这个测试的 QPS 是15持续20秒可以看到实时监控图形如下 可以看到一开始的几秒内是没有请求失败的 QPS 的因为接口会处理 QPS10 的请求并将多余的请求放入队列只有队列被填满预期处理时长超过5秒新的请求才会被丢弃即出现请求失败的 QPS。
2.4.热点参数限流
如果需要对同一个资源的不同参数进行限流就需要使用热点参数限流。
比如对/goods/{id}接口的请求按照不同的参数进行 QPS 统计 我们就可以根据不同 id 值的 QPS 进行分别统计和限流。
下面用实际案例说明。
案例需求给/order/{orderId}这个资源添加热点参数限流规则如下
•默认的热点参数规则是每1秒请求量不超过2
•给102这个参数设置例外每1秒请求量不超过4
•给103这个参数设置例外每1秒请求量不超过10
需要注意的是Sentinel 的热点参数限流对默认添加的 SpringMVC 资源无效所以这里需要先对资源使用SentinelResource注解以添加 Sentinel 资源
// ...
public class OrderController {// ...SentinelResource(hot)GetMapping({orderId})public Order queryOrderByUserId(PathVariable(orderId) Long orderId) {// 根据id查询订单并返回return orderService.queryOrderById(orderId);}// ...
}重启微服务。
请求接口http://localhost:8088/order/101让资源注册。
在 热点规则 菜单页面中新增 热点规则限流 这里的参数索引指所要添加限制的参数位于接口路径上的参数位置从0开始。单机阈值设置为2统计窗口时长设置为1实际上就是 QPS2。
这里为资源 hot 设置的默认参数限流的 QPS 是2另外添加了2个参数例外其中参数值是 102 时 QPS 上限是4参数值是 103 时 QPS 上限是10。 从簇点链路菜单页面添加热点规则无法使用高级选项参数例外。 执行测试案例中的 热点参数限流 QPS1 进行测试。该案例的 QPS5且使用不同的参数值分别请求接口
/order/101/order/102/order/103
参数值是101时受热点规则中的默认设置限制即最大QPS 是2所以每秒只有2个请求成功。参数值是102时受例外规则限制最大QPS是4所以每秒有4个请求成功。参数值是103时最大 QPS 是10所以所有请求都成功。
3.隔离和降级
可以用 Sentinel 对分布式架构中接口的调用方进行保护具体包含两种方式
线程隔离之前讲到过调用者在调用服务提供者时给每个调用的请求分配独立线程池出现故障时最多消耗这个线程池内资源避免把调用者的所有资源耗尽。 熔断降级是在调用方这边加入断路器统计对服务提供者的调用如果调用的失败比例过高则熔断该业务不允许访问该服务的提供者了。 3.1.FeignClient 整合 Sentinel
因为目前我们 Spring Cloud 中微服务之间的远程调用都是用 Feign 实现的所以要想实现上边的功能就必须在服务调用方的 Feign 客户端中整合 Sentinel。
下面具体说明如何实现 Feign 客户端和 Sentinel 的整合。
3.1.1.开启 Sentinel 功能
首先需要修改配置以启用 Feign 对 Sentinel 的支持
feign:sentinel:enabled: true # 开启feign对sentinel的支持这里的示例中我们需要保护的是微服务 order-service它的订单信息接口会远程调用 user-service所以对 order-service 中的 Feign 客户端整合 Sentinel也就是要修改 order-service 的配置文件。 实际上完成上边的步骤后 Sentinel 已经会将通过 Feign 进行的远程调用纳入资源监控可以在 Sentinel 控制台看到相应的资源 但如果直接设置降级熔断或者隔离触发限制后远程调用会直接报错这样对调用方的感知不会太好因此一般我们需要添加失败降级后的处理逻辑。
3.1.2.添加失败降级逻辑
可以利用两个接口实现 Feign 客户端调用失败后的降级逻辑 FallbackClass无法对远程调用的异常做处理 FallbackFactory可以对远程调用的异常做处理我们选择这种
一般使用 FallbackFactory。
package cn.itcast.feign.clients.fallbackfactory;
// ...
Log4j2
public class UserClientFallbackFactory implements FallbackFactoryUserClient {Overridepublic UserClient create(Throwable throwable) {return new UserClient() {Overridepublic User findById(Long id) {//将错误信息打印到日志log.error(Feign 远程调用出错, throwable);//远程调用失败时返回一个空的用户对象return new User();}};}
}FallbackFactory要写在 FeignClient 定义的地方在这个示例项目中就是子模块 feign-api。 FallbackFactory是一个泛型接口在实现时泛型参数应当指定为要定义错误处理的 FeignClient。
FallbackFactory的实现类必须定义为 Spring Bean
package cn.itcast.feign.config;
// ...
public class DefaultFeignConfiguration {// ...Beanpublic UserClientFallbackFactory userClientFallbackFactory(){return new UserClientFallbackFactory();}
}feign-api 只是一个用于定义 Feign 客户端的子模块并不会真正运行。所以要让配置类DefaultFeignConfiguration生效还需要在使用 feign-api 的微服务中添加
package cn.itcast.order;
// ...
SpringBootApplication
EnableFeignClients(clients UserClient.class,defaultConfiguration DefaultFeignConfiguration.class)
public class OrderApplication {// ...
}要让定义的FallbackFactory生效还需要在FeignClient注解中添加fallbackFactory属性
FeignClient(value userservice, fallbackFactory UserClientFallbackFactory.class)
public interface UserClient {// ...
}在实际使用中失败降级逻辑通常可以用两种思路实现以保证用户友好
返回特定的错误信息。返回特定的业务数据比如一些特定的商品信息等。
3.2.线程隔离
3.2.1.线程隔离的实现方式
线程隔离有两种方式实现
线程池隔离信号量隔离Sentinel默认采用 线程池隔离给每个服务调用业务分配一个线程池利用线程池本身实现隔离效果
信号量隔离不创建线程池而是计数器模式记录业务使用的线程数量达到信号量上限时禁止新的请求。
两者的优缺点 3.2.2.Sentinel 的线程隔离
这里用示例进行说明如何用 Sentinel 实现线程隔离。
为资源GET:http://userservice/user/{id}添加线程隔离 现在/order/{orderId} - /userservice/user/{id} 这个调用链被设置了信号量隔离阈值为2。也就是说同时只能有2个并发的线程被允许访问接口/userservice/user/{id}其它的并发线程会被拒绝执行定义好的失败降级逻辑如果有的话。
可以执行 JMeter 中的 阈值类型-线程数2 来进行测试。该测试会模拟同时请求10次接口。
可以观察到虽然10次请求都成功了但有8次返回的结果中用户信息是null这符合我们实现的失败处理逻辑。
3.3.熔断降级
熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求而当服务恢复时断路器会放行访问该服务的请求。
断路器控制熔断和放行是通过状态机来完成的 状态机包括三个状态
closed关闭状态断路器放行所有请求并开始统计异常比例、慢请求比例。超过阈值则切换到open状态open打开状态服务调用被熔断访问被熔断服务的请求会被拒绝快速失败直接走降级逻辑。Open状态5秒后会进入half-open状态half-open半开状态放行一次请求根据执行结果来判断接下来的操作。 请求成功则切换到closed状态请求失败则切换到open状态 关于断路器状态的详细讲解可以观看这个视频相关内容位于 24:00 3.3.1.慢调用
慢调用业务的响应时长RT大于指定时长的请求认定为慢调用请求。在指定时间内如果请求数量超过设定的最小数量慢调用比例大于设定的阈值则触发熔断。
例如 解读RT超过500ms的调用是慢调用统计最近10000ms内的请求如果请求量超过10次并且慢调用比例不低于0.5则触发熔断熔断时长为5秒。然后进入half-open状态放行一次请求做测试。
下面用实际示例进行说明。
需求给 UserClient的查询用户接口设置降级规则慢调用的RT阈值为50ms统计时间为1秒最小请求数量为5失败阈值比例为0.4熔断时长为5
为了模拟接口调用超过 50ms在接口中添加休眠代码
package cn.itcast.user.web;
// ...
public class UserController {// ...GetMapping(/{id})public User queryById(PathVariable(id) Long id,RequestHeader(value Truth, required false) String truth) throws InterruptedException {if (id.equals(1L)) {Thread.sleep(60);}return userService.queryById(id);}
}给资源GET:http://userservice/user/{id}添加降级规则 也就是说上面配置的规则只要在1秒内有至少5次请求发生且其中2次以上调用时长超过50ms就会熔断5s。
请求接口 http://localhost:8088/order/101 5次后发生熔断
{id: 101,price: 699900,name: Apple 苹果 iPhone 12 ,num: 1,userId: 1,user: {id: null,username: null,address: null}
}此时请求接口 http://localhost:8088/order/102 同样无法获取正确的用户信息
{id: 102,price: 209900,name: 雅迪 yadea 新国标电动车,num: 1,userId: 2,user: {id: null,username: null,address: null}
}等待5秒后两个接口都恢复为正常访问。 如果熔断降级规则配置后进行测试发现没有生效可以等一会实际测试发现规则没有立即生效。可以手动刷新浏览器进行测试也可以选择使用 JMeter。 3.3.2.异常比例、异常数
异常比例或异常数统计指定时间内的调用如果调用次数超过指定请求数并且出现异常的比例达到设定的比例阈值或超过指定异常数则触发熔断。
例如一个异常比例设置 解读统计最近1000ms内的请求如果请求量超过10次并且异常比例不低于0.4则触发熔断。
一个异常数设置 解读统计最近1000ms内的请求如果请求量超过10次并且异常比例不低于2次则触发熔断。
这里同样用实际示例进行说明。
目标给 UserClient的查询用户接口设置降级规则统计时间为1秒最小请求数量为5失败阈值比例为0.4熔断时长为5s。
为了模拟接口调用出现异常在接口中抛出一个异常
GetMapping(/{id})
public User queryById(PathVariable(id) Long id,RequestHeader(value Truth, required false) String truth) throws InterruptedException {if (id.equals(1L)) {System.out.println(程序休眠以满足降级要求);Thread.sleep(60);}else if (id.equals(2L)){throw new RuntimeException(模拟调用出现异常);}return userService.queryById(id);
}重启微服务。
修改之前添加的降级熔断规则 和之前类似只要在1s内至少请求5次且其中有2个异常请求出现异常就会触发熔断。
测试过程与之前类似不再赘述。
除了按照异常比例熔断以外还可以按照异常数进行统计和熔断比如 这个设置和上边的设置效果是类似的。
4.授权规则
授权规则可以对请求方来源做判断和控制。
4.1.授权规则
授权规则可以对调用方的来源做控制有白名单和黑名单两种方式。 白名单来源origin在白名单内的调用者允许访问 黑名单来源origin在黑名单内的调用者不允许访问
点击左侧菜单的授权可以看到授权规则 资源名就是受保护的资源例如/order/{orderId} 流控应用是来源者的名单 如果是勾选白名单则名单中的来源被许可访问。如果是勾选黑名单则名单中的来源被禁止访问。
比如 我们允许请求从gateway到order-service不允许浏览器访问order-service那么白名单中就要填写网关的来源名称origin。
Sentinel是通过RequestOriginParser这个接口的parseOrigin来获取请求的来源的。
public interface RequestOriginParser {/*** 从请求request对象中获取origin获取方式自定义*/String parseOrigin(HttpServletRequest request);
}这个方法的作用就是从request对象中获取请求者的origin值并返回。
默认情况下sentinel不管请求者从哪里来返回值永远是default也就是说一切请求的来源都被认为是一样的值default。
换言之我们需要添加一个自定义 bean 并实现该接口来定义一个我们自己的区分 origin 的规则。
作为示例我们这里定义一个 HTTP 请求头 origin 来进行区分
package cn.itcast.order.sentinel;
// ...
Component
public class HeaderOriginParser implements RequestOriginParser {Overridepublic String parseOrigin(HttpServletRequest httpServletRequest) {String origin httpServletRequest.getHeader(origin);if (StringUtils.isEmpty(origin)) {origin blank;}return origin;}
}修改 gateway 中的配置让所有经过 gateway 过来的 HTTP 请求都添加一个 origin 请求头
spring:cloud:gateway:default-filters:- AddRequestHeaderorigin,gateway重启 gateway 和 order-service。
新增一个授权规则 现在直接请求接口 http://localhost:8088/order/101 会返回
Blocked by Sentinel (flow limiting)但经过 gateway 进行请求http://localhost:10010/order/101?authorizationadmin可以成功返回
{id: 101,price: 699900,name: Apple 苹果 iPhone 12 ,num: 1,userId: 1,user: {id: 1,username: 柳岩,address: 湖南省衡阳市}
}4.2.自定义异常结果
上面的示例存在一个问题虽然触发的是授权规则进行的熔断但实际上返回的错误信息却是限流Flow Limiting。实际上默认情况下Sentinel 发生限流、降级、授权拦截时都会抛出异常BlockException到调用方。异常结果都是flow limmiting限流。
可以实现一个接口BlockExceptionHandler来改变这种默认行为
public interface BlockExceptionHandler {/*** 处理请求被限流、降级、授权拦截时抛出的异常BlockException*/void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception;
}这个方法有三个参数
HttpServletRequest requestrequest对象HttpServletResponse responseresponse对象BlockException e被sentinel拦截时抛出的异常
可以在实现方法中根据 Sentinel 抛出异常的具体类型来分别处理通常是在 Response 中返回不同的错误提示。
BlockException的子类有
异常说明FlowException限流异常ParamFlowException热点参数限流的异常DegradeException降级异常AuthorityException授权规则异常SystemBlockException系统规则异常
下面是一个 BlockExceptionHandler 的实现示例
package cn.itcast.order.sentinel;
// ...
Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, 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;}httpServletResponse.setContentType(application/json;charsetutf-8);httpServletResponse.setStatus(status);httpServletResponse.getWriter().println({\msg\: msg , \status\: status });}
}重启微服务后添加限流规则并访问接口比如 http://localhost:8088/order/101将会显示自定义的错误信息
{msg: 请求被限流了, status: 429}5.规则持久化
现在sentinel的所有规则都是内存存储重启后所有规则都会丢失。在生产环境下我们必须确保这些规则的持久化避免丢失。
5.1.规则管理模式
规则是否能持久化取决于规则管理模式sentinel支持三种规则管理模式
原始模式Sentinel的默认模式将规则保存在内存重启服务会丢失。pull模式push模式
5.1.1.pull模式
pull模式控制台将配置的规则推送到Sentinel客户端而客户端会将配置规则保存在本地文件或数据库中。以后会定时去本地文件或数据库中查询更新本地规则。 5.1.2.push模式
push模式控制台将配置规则推送到远程配置中心例如Nacos。Sentinel客户端监听Nacos获取配置变更的推送消息完成本地配置更新。 5.2.实现push模式
Sentinel 可以借助 Nacos 的远程配置功能实现规则的持久化但阿里开源的 Sentinel 不支持该功能只有收费版本支持。要让开源的免费版本支持需要修改 Sentinel-dashboard 的源码后重新打包运行。
具体方式可以参考这篇文章。
源码的修改过程倒不是很复杂但是该项目引用的依赖非常多下载依赖要花费很长时间大概一天左右所以这里提供一个我修改好的 jar 包。
运行的时候只要指定 Nacos 服务的地址就行
java -jar -Dnacos.addr192.168.0.88:8848 sentinel-dashboard.jarThe End谢谢阅读。
参考资料
https://www.bilibili.com/video/BV1LQ4y127n4?p143