雪崩问题
由于微服务中的某个服务出现故障无法完成任务,导致依赖于它的服务阻塞在对它的请求上,不释放连接资源,最终连接堆积,它也无法处理新的任务,这样的情况按照层级不断传递,最终使得微服务集群中的很多节点都出现相同的故障,这就是雪崩
雪崩问题的原因:
- 瞬时高并发使得服务处理的速率跟不上请求速率
- 服务或网络故障
雪崩问题的解决办法:
- 超时处理:它能一定程度上缓解雪崩问题,但如果超时时间没有连接进入的时间快,还是会出现雪崩问题
- 舱壁模式(线程隔离):限定每个微服务中的每个业务能使用的最大线程数,避免耗尽连接资源
该模式还是没有完全解决问题,假设服务C已经宕机,该模式的业务2还是会不断的去访问服务C,实际上这些访问只是白白浪费资源。而且,依赖于服务A的业务2的微服务不还是会阻塞嘛,虽然整体没事,但对于这条服务调用路径来说是不是可以认为依然发生了雪崩呢? - 熔断降级:断路器会统计对于某个服务调用的失败比例,若比例达到一定阈值,拦截访问该业务的一切请求
- 流量控制(预防):限制业务访问的QPS,将大量请求按照服务所能承受的QPS传递到服务中(有点类似MQ所带来的流量削峰)
Sentinel Dashboard安装的一个坑
我想替换Sentinel默认的用户名和密码,由于Sentinel控制台是一个SpringBoot项目,所以我打算在jar包目录放置一个application.yml
文件,在里面按照官方文档给的参数来配置用户名和密码,官方给的配置如下:
sentinel:
dashboard:
auth:
username: xxx
password: xxx
无论我是用这还是用命令行参数都没用,后来我把这个jar包解开了,看了下官方的配置文件里是咋写的,结果哔了个狗:
所以,我把我的配置文件改成:
auth:
username: xxx
password: xxx
现在好了...
QuicStart
在需要被监控的微服务中引入Sentinel依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
在配置文件中配置:
spring:
cloud:
sentinel:
transport:
dashboard: localhost:10101
访问该服务的任意端点,然后去Sentinel控制台中查看被监控下来的数据:
同时我们可以通过簇点链路中的每个资源后面的流控按钮来控制该端口允许的最大流量
比如,这里我们把QPS设置为1,那么一秒钟内该资源只能被访问1次
一秒钟内尝试访问多次被Sentinel拦截
簇点链路
簇点链路是项目中的调用链路,链路中的每一个被监控的接口就是一个资源,默认情况下,Sentinel会监控SpringMVC中的每个Controller
方法。
流控模式
在Sentinel流控的高级选项中,有一个流控模式选项
- 直接:当当前资源的请求到达阈值时,对当前资源进行限流
- 关联:当与当前相关的另一个资源的请求到达阈值时,对当前资源进行限流
- 链路:统计从指定链路到当前资源的请求,当触发阈值时,对指定链路限流
关联模式的示例
假设/query
资源是对商品进行查询的资源,/update
是支付后对商品库存进行修改的资源,我们期望/update
资源的优先级更高,因为这里的用户在花钱。
所以我们可以在/update
的QPS到达阈值时对/query
的部分访问进行拒绝,这样也就让出了部分处理资源给/update
关联模式用在两个具有关联的,但一个比另一个优先级要更高的情况下
链路模式的示例
系统中有三个资源:
- 假设
/order/query
是查询订单的接口 - 假设
/order/update
是插入订单的接口 - 假设查询和插入订单都需要访问
OrderService.queryGoods
方法
还是一样的思路,我们想尽可能保证插入订单的请求被服务,因为人家花钱了。所以,我们可以针对queryGoods
这个资源进行链路限流,当它是从/order/query
来的,就给它一个较小的QPS,当它是从/order/update
来的,就给它一个较大的QPS。
开始前的设置
Sentinel默认只会监控Controller方法作为资源,你还要将对应的Service方法声明为
Sentinel
资源
@SentinelResource(value = "queryGoods")
public void queryGoods() {
System.out.println("queryGoods");
}
Sentinel默认还会开启Controller方法的context整合,虽然不知道是啥,但它会导致链路模式流控失败,可以使用以下方式关闭
spring:
cloud:
sentinel:
web-context-unify: false
开启链路模式限流并测试
流控效果
高级设置中还有一个流控效果,有三种选项
- 快速失败:到达阈值,直接抛出FlowException,请求宣告失败
- warm up:效果同上,但阈值是从一个较小的值慢慢增长到最大阈值
- 排队等待:所有请求按照到达先后到队列中排队,新来的请求当预期等待时间超出最大时长时就会被拒绝
warm up的示例
程序刚刚启动时肯定达不到最大的QPS,随着散落在程序中、框架中、系统中的各种缓存技术、JIT优化技术的一点一点的应用,程序才能慢慢的到达最大QPS。
warm up即设定一个时间,请求阈值从threshold / coldFactor
(默认为3)开始慢慢提高,等到时间到达,实际阈值才变成设定好的最大threshold
排队等待的示例
所有到达的请求到队列中排队,后面的请求必须等待前面的请求完成后才能执行。
排队等待中有一个预期等待时间的概念,当QPS=5时,意味着预期每个任务的执行时间是200ms,这时,新来的任务的预期等待时间就是队列大小 * 1000 / QPS
ms,而排队等待可以设置这个预期等待时间的最大值,如果新任务的预期等待时间超出这个最大值,它就会被拒绝。
实际上,排队等待会起到一个流量整形的作用,无论瞬时的并发量有多大,都会排到队列中慢慢处理(当超时时间够大时)。
下面是我们使用排队等待(QPS=10, timeout=10000)流控效果时,以15的QPS来发送20秒请求,但由于有队列的存在,QPS被限制在10,而且没有请求被拒绝,这都是队列的功劳。
当然,如果你将这个测试过程持续长些,始终以15的QPS进行发起请求,以10QPS的速度进行处理,那么队列始终会满的,然后就会出现请求被拒绝的情况,比如在当前设置下,队列最多能容纳100个请求,因为\(最大请求数 = timeout/(1000/qps)=10000/(1000/10)=100\),如果我们每秒发送15个请求,那么每秒会多出5个请求没被处理,积攒在队列中,那么,第21秒也许就有请求失败了。
所以,排队等待只是在瞬时高并发涌入时进行流量削峰填谷,如果长时间的请求QPS大于你能处理的QPS,还是多增加一些服务吧
热点参数限流
比如在微博突然发生热点事件的情况,某个博主或者某个帖子的请求访问量肯定比其它人的高很多,所以这种情况,这个博主或帖子的QPS可能需要加大,此时根据这个博主或帖子的id提供例外的限流规则比直接将博主查询和帖子查询的整个资源都应用特定的限流更加人性化。
下面是针对热点订单(看起来有些愚蠢就这样吧)的限流设置,默认情况下所有订单的QPS都是2,而2和3是比较热点的订单,它们的QPS是例外项,被设置成了4和10。
这时,需要去代码中给指定的Controller方法添加resource,这是必须的,因为Sentinel的热点参数限流对Controller
那个默认被识别的资源不生效,你要想用热点参数限流就必须使用@SentinelResource
重新声明它。
@GetMapping("/{id}")
@SentinelResource("hotorders")
public Order getOrderById(@PathVariable("id") Integer id) {
return orderService.getOrderById(id);
}
标签:服务,请求,保护,限流,链路,Sentinel,QPS,资源
From: https://www.cnblogs.com/lilpig/p/16609676.html