1、Sentinel是什么
- Sentinel是阿里巴巴开源的,面向分布式服务架构的高可用防护组件。
- 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。 Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
源码地址:https://github.com/alibaba/Sentinel
官方文档:https://github.com/alibaba/Sentinel/wiki
Sentinel 具有以下特征:
- 丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。
- 完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
- 完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。例如定制规则管理、适配数据源等。
阿里云提供了企业级的Sentinel服务,应用高可用服务AHAS
Sentinel和Hystrix对比
Sentinel | Hystrix | |
隔离策略 | 信号量隔离 | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于响应时间或失败比率 | 基于失败比率 |
实时指标实现 | 滑动窗口 | 滑动窗口(基于RxJava) |
规则配置 | 支持多种数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件的形式 |
基于注解的支持 | 支持 | 支持 |
限流 | 基于QPS,支持基于调用关系的限流 | 有限的支持 |
流量整形 | 支持慢启动,匀速器模式 | 不支持 |
系统负载保护 | 支持 | 不支持 |
控制台 | 开箱即用、可配置规则、查看秒级监控、机器发现等 | 不完善 |
常见框架的适配 | servlet、spring cloud、dubbo、grpc等 | servlet、spring cloud netflix |
2、sentinel-流控规则初体验(代码层面设置规则)
Sentinel可以简单的分为
Sentinel核心库
和Dashboard
。核心库不依赖Dashboard,但是结合Dashboard可以取得最好的效果。
流程规则的定义
重要属性:
Field | 说明 | 默认值 |
resource | 资源名,资源名是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 限流阈值类型,QPS 模式(1)或并发线程数模式(0) | QPS 模式 |
limitApp | 流控针对的调用来源 | default ,代表不区分调用来源 |
strategy | 调用关系限流策略:直接、链路、关联 | 根据资源本身(直接) |
controlBehavior | 流控效果(直接拒绝/WarmUp/匀速+排队等待),不支持按调用关系限流 | 直接拒绝 |
clusterMode | 是否集群限流 | 否 |
通过代码自定义流量控制:
1、创建一个springboot项目,并引入sentinel核心库
<!--sentinel核心库--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-core</artifactId> <version>1.8.4</version> </dependency> <!--如果要使用@SentinelResource--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-annotation-aspectj</artifactId> <version>1.8.4</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
编写一个controller
@RestController @Slf4j public class HelloController { private static final String RESOURCE_NAME = "hello"; @RequestMapping("/hello") public String hello(){ Entry entry = null; try { // sentinel针对资源进行限制 entry = SphU.entry(RESOURCE_NAME); // 被保护的业务逻辑 String str = "hello world"; log.info("======="+str+"======="); return str; }catch (BlockException e){ // 资源访问组织,被限流或被降级,进行响应的处理操作 log.info("block"); return "被监控了"; }catch (Exception ex){ // 若需要配置降级规则,则需要通过这种方式记录业务异常 Tracer.traceEntry(ex,entry); }finally { if(entry != null){ entry.exit(); } } return null; } /** * spring的初始化方法 * **/ @PostConstruct private static void initFlowRules(){ // 流控规则 List<FlowRule> rules = new ArrayList<>(); // 流控 FlowRule rule = new FlowRule(); // 为哪个资源设置流控 rule.setResource(RESOURCE_NAME); // 设置流程规则 QPS(每秒的一个访问数) rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // 设置受保护的资源阈值(1秒当中访问一次) rule.setCount(1); // 加入集合 rules.add(rule); // 加载配置好的规则 FlowRuleManager.loadRules(rules); } }
结果描述
浏览器进行访问/hello
接口,每隔一秒点击一次,正常返回hello world
,如果快速点击,返回被监控了
。
像这样的规则还有熔断降级规则
、系统保护规则
、访问控制规则
、热点规则
、查询更改规则
等等,代码使用方法与上述雷同。
@SentimeResource:注解引入
作用:改善接口中资源定义和被流控降级后的处理方法
1、添加依赖(上面已经引入,我再展示一遍这个依赖)
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-annotation-aspectj</artifactId> <version>1.8.4</version> </dependency>
2、配置bean
/** * 注解支持的配置Bean */ @Bean public SentinelResourceAspect sentinelResourceAspect(){ return new SentinelResourceAspect(); }
3、在controller层进行代码实现
@RestController @Slf4j public class HelloController { private static final String RESOURCE_NAME = "hello"; /** * value: 定义的资源 * blockHandler: 设置流控降级后的处理方法(默认该方法必须声明在同一个类) * 如果不想在同一个类中blockHandLerClass但是方法必须是static静态的 * fallback : 当接口出现了异常,可以调用的方法 * 如果不想在同一个类中fallbackClass但是方法必须是static静态的 * 如果blockHandler和fallback同时指定,则blockHandler优先级更高 * exceptionsToIgnore 排除哪些异常不处理 **/ @RequestMapping("/hello") @SentinelResource(value = RESOURCE_NAME,blockHandler = "blockHandlerForHello",fallback = "fallbackHandlerHello",exceptionsToIgnore = {}) public String hello(String id){ // int i = 1/0; return "hello world"; } /** * 注意: * 1、一定要public * 2、返回值一定要和原方法一直,参数也要包含原函数的参数 */ public String blockHandlerForHello(String id , BlockException ex){ ex.printStackTrace(); return "流控"; } /** * 异常会走这个方法 */ public String fallbackHandlerHello(String id , Throwable e){ e.printStackTrace(); return "异常处理"; } /** * spring的初始化方法 * **/ @PostConstruct private static void initFlowRules(){ // 流控规则 List<FlowRule> rules = new ArrayList<>(); // 流控 FlowRule rule = new FlowRule(); // 为哪个资源设置流控 rule.setResource(RESOURCE_NAME); // 设置流程规则 QPS(每秒的一个访问数) rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // 设置受保护的资源阈值(1秒当中访问一次) rule.setCount(1); // 加入集合 rules.add(rule); // 加载配置好的规则 FlowRuleManager.loadRules(rules); } }
3、sentinel控制台部署
根据需要的版本进行下载下载jar包
windows版本启动:
下载完成后,在指定目录下面进行
java -jar 你的jar包名称
浏览器直接访问即可,localhost:8080,默认账户名和密码都是sentinel
修改端口号、以及账户名和密码
#需以这种方式启动即可 java -Dserver.port=8810 -Dsentinel.dashboard.auth.username=lili -Dsentinel.dashboard.auth.password=211314 -jar D:\a-mysource\sentinel\sentinel-dashboard-1.8.1.jar
linux版本启动
不做过多描述,命令与windows一样,
注意:如果你想要外界可以访问的话,jar包需要后台启动,可以参考这篇文章当xshell关闭时如何保持一个jar包程序在后台运行
并且打开相应端口防火墙,以及服务器的安全组放行。
效果展示:
本地集成控制台
<!--整合控制台--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-simple-http</artifactId> <version>1.8.4</version> </dependency>
编辑我们的启动配置,在对应位置加入(主要是为了根据ip和端口号找到我们的控制台)
-Dcsp.sentinel.dashboard.server=127.0.0.1:8010
启动即可!
注意:刚打开我们的控制台是什么都没有的,随便对一个接口进行访问,再次刷新控制台即可。
4、sentinel整合springcloud alibaba
1、添加依赖
<!--sentinel依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
2、yml配置
server: port: 8005 # 服务名称 spring: application: name: order-sentinel cloud: sentinel: transport: #配置sentinel的地址以及端口号 dashboard: 127.0.0.1:8010
3、编写一个controller
@RestController @RequestMapping("/order") public class OrderController { @GetMapping("/orderList") public String getOrder() { return "orderList"; } @GetMapping("/hello") public String getHello() { return "hello"; } }
启动,先对接口进行一个访问,再进入控制台即可
5、Sentinel控制台各个部分讲解
5.1、实时监控
我们可以对两个请求快速进行访问几次,然后点击实时监控,可以看到请求的一些信息
5.2、簇点链路
- 用来显示微服务所监控的API
我们经过请求,发现两次请求都被添加进来了。并且可为每个请求进行流控,降级,热点,授权
5.3、流控规则
流量控制,其原理是监控应用流量的QPS或并发线程等指标,当达到指定的阈值对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性
应用场景:
- 应对洪峰流量:秒杀,大促,下单,订单回流处理
- 消息型场景:削峰填谷,冷热启动
- 付费系统:根据使用流量付费
- API Gateway:精准控制API流量
- 探测应用中运行的慢程序块,进行限制
我们在簇点链路对流控规则进行添加操作,先进行示例QPS方式如下
- QPS 对应阈值: 表示每秒接受几个请求
解释:该请求每秒最多2个请求,否则就会被流控
测试:浏览器快速刷新,结果如下
结论:成功被流控,这是默认显示,如果想修改默认显示,操作如下
@RestController @RequestMapping("/order") public class OrderController { @GetMapping("/hello") @SentinelResource(value = "hello",blockHandler = "helloHandler") public String getHello() { return "hello"; } public String helloHandler(BlockException e) { return "被流控了"; } }
需要对我们刚才自己添加的资源进行流控
再次快速刷新,这个时候就是我们自定义的流控描述了。
再次测试线程数流控:
线程数流控是为了保护业务线程池不被慢调用耗尽
- 线程数对应阈值: 表示每次处理几个线程,其他的线程请求排除在外
实现:添加一个方法
@RequestMapping("/threadHello") @SentinelResource(value = "threadHello",blockHandler = "helloHandler") public String threadHello() throws InterruptedException { TimeUnit.SECONDS.sleep(5); return "hello"; }
打开两个浏览器进行访问(模拟两个线程)
5.3.1、关联流控模式
当两个资源之间具有争抢或者依赖关系的时候,这两个资源便有了关联,比如对数据库同一个字段的读操作和写操作存在争抢,这个时候,我们就可以把读写操作进行关联,当写的操作过于频繁时,读数据的请求就会被限流。
步骤:
1、新建两个接口
@GetMapping("/add") public String add() { return "生成订单"; } @GetMapping("/get") public String get() { return "查询订单"; }
2、生成订单对查询订单限流,当生成订单的次数大于三,则查询订单被限流。
Jmeter工具对/order/add进行每秒四次请求测试,然后浏览器访问/order/get
5.3.2、链路流控模式
下图记录了资源之间的调用链路,这些资源通过调用关系,相互之间构成一颗调用树
解释:有两个入口/order/getUser1
,/order/getUser2
请求都调用了资源User,Sentinel可以选择对其中一个进行资源限制
步骤:
1、定义一个接口和对应的实现类
public interface UserService { String getUser(); }
@Service public class UserServiceImpl implements UserService{ @Override @SentinelResource(value = "getUser",blockHandler = "userHandler") public String getUser() { return "User"; } public String userHandler(BlockException e){ return "被链路流控了"; } }
2、编写controller层
@Autowired UserService userService; @GetMapping("/getUser1") public String getUser1() { return userService.getUser(); } @GetMapping("/getUser2") public String getUser2() { return userService.getUser(); }
3、对getUser2添加链路流控
注意:测试发现功能不生效(高版本不生效)
yml里面加上
spring: cloud: sentinel: web-context-unify: false # 默认将调用链路收敛
再次测试发现
只对order/getUser2
进行了流控,而对order/getUser1
没有进行流控
5.3.3、预热流控效果
针对激增流量处理
warm up
方式,即预热\冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮,通过冷启动让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热时间,避免冷系统被压垮。
冷加载因子:codeFactor默认是3,即请求QPS从threshold/3开始,经预热时长逐渐升至设定的QPS阈值
解释:5s内时间进行预热,直到达到阈值(比如第一秒只能通过三个,第二秒可以通过5个,直到达到阈值)
演示:浏览器快速刷新请求,刚开始会被流控,随着预热,你的刷新速度已经不会被流控了
5.3.4、排队等待流控效果
针对脉冲流量
匀速排队方式会严格控制请求通过的间隔时间,也即是让请求以匀速的速度通过,对应的是漏桶算法
这种方式主要用于处理间隔性突然的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
注意:匀速排队模式暂时不支持QPS>1000的场景
解释:每秒接收5个请求,其他请求进行等待,等待时间为5秒
Jmeter工具对/order/orderList进行每秒10次请求测试,间隔时间为5s,循环5次。
然后观察实施监控
发现:所有的请求都进行了处理,并且还有空闲时间