基于Sentinel的服务保护方案的三种方式(请求限流、线程隔离、服务熔断)超详细讲解
目录
1、三种方式介绍
1.1请求限流
1.2 线程隔离方案
1.3 服务熔断
2、基于sentinel实现
2.1 启动sentinel
2.2 基于springboot整合sentinel
2.2.1请求限流
2.2.2请求隔离
2.2.2.1 OpenFeign整合Sentinel
2.2.3 服务熔断
2.2.3.1 编写降级代码
2.2.3.2 服务熔断
1、三种方式介绍
微服务保护的方案有很多,比如:
-
请求限流
-
线程隔离
-
服务熔断
这些方案或多或少都会导致服务的体验上略有下降,比如请求限流,降低了并发上限;
线程隔离,降低了可用资源数量;
服务熔断,降低了服务的完整度,部分服务变的不可用或弱可用。因此这些方案都属于服务降级的方案。但通过这些方案,服务的健壮性得到了提升,
接下来,我们就逐一了解这些方案的原理。
1.1请求限流
服务故障最重要原因,就是并发太高!解决了这个问题,就能避免大部分故障。当然,接口的并发不是一直很高,而是突发的。因此请求限流,就是限制或控制接口访问的并发流量,避免服务因流量激增而出现故障。
请求限流往往会有一个限流器,数量高低起伏的并发请求曲线,经过限流器就变的非常平稳。这就像是水电站的大坝,起到蓄水的作用,可以通过开关控制水流出的大小,让下游水流始终维持在一个平稳的量。
PS:QPS 指的是服务器每秒可以处理的请求数量。
1.2 线程隔离方案
当一个业务接口响应时间长,而且并发高时,就可能耗尽服务器的线程资源,导致服务内的
其它接口受到影响。所以我们必须把这种影响降低,或者缩减影响的范围。线程隔离正是解决这个
问题的好办法。
- 业务1:服务A到服务B,限制10线程数
- 业务2:服务A到服务C,限制10线程数
此时,服务C故障,最大损失10个线程,不会长期占用其他线程,如此,服务A到服务B仍可正常访问,阻止了故障的传递
1.3 服务熔断
线程隔离虽然避免了雪崩问题,但故障服务(商品服务)依然会拖慢购物车服务(服务调用方)的接口响应速度。而且商品查询的故障依然会导致查询购物车功能出现故障,购物车业务也变的不可用了。
所以,我们要做两件事情:
-
编写服务降级逻辑:就是服务调用失败后的处理逻辑,根据业务场景,可以抛出异常,也可以返回友好提示或默认数据。
-
异常统计和熔断:统计服务提供方的异常比例,当比例过高表明该接口会影响到其它服务,应该拒绝调用该接口,而是直接走降级逻辑。
2、基于sentinel实现
2.1 启动sentinel
Sentinel 是阿里巴巴开源的一款服务保护框架,目前已经加入 SpringCloudAlibaba 中。官
方网站:home | Sentinel
我们可以在这里进行sentinel的一个下载以及查看如何操作和使用,在下载完成后就可以进行我们的操作了
在这里进行jar包下载,Releases · alibaba/Sentinel · GitHub
下载完成后将他放在一个无中文路径的文件夹
在地址栏输入cmd,进入命令操作栏
进来后,输入这段指令,这段指令的作用是用来启动sentinel的
java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
出现这种状况,表示我们的sentinel启动成功了,并且端口号是8090,我们现在可以去浏览器访问他了,并且不要关闭命令操作符
输入localhost:8090,表示我们从本地去访问8090端口号
localhost:8090
出现此页面表示sentinel启动成功了,进入了sentinel的登录页面,这里的账号密码都是sentinel
这里就是我们sentinel真正的首页,进入后就可以开始操作了
2.2 基于springboot整合sentinel
首先添加依赖:
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
在pom.xml文件中添加此依赖
修改application.yaml文件,添加以下代码
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090
重启项目之后,可以在添加了此依赖及代码的地方随机进行一个操作,比如说我在这个商城项目中的购物车cart-service模块中添加了此配置及依赖,那么我就可以对他操作,用来测试
购物车测试
这里我一直刷新购物车这个页面,也就代表着我需要一直访问查看的controller,所以说这样每次访问都被会检测到,再来看看sentinel会是怎么样的
这里可以看到,在这个时间段,不断的有QPS通过,也就是代表着不断有请求被检测到了,虽然看起来很多,但实际上还是很小的,因为我只是手动刷新,达不到那么快的速度,但如果是在双十一这种高并发的情况,那么请求次数就很恐怖了,这里我们就可以做出相应的解决方案
点击《簇点链路》,进入这个页面
所谓簇点链路,就是单机调用链路,是一次请求进入服务后经过的每一个被 Sentinel 监控的
资源。默认情况下,Sentinel 会监控 SpringMVC 的每一个 Endpoint(接口)。
因此,我们看到 /carts 这个接口路径就是其中一个簇点,我们可以对其进行限流、熔断、隔
离等保护措施。
打开 Sentinel 的请求方式前缀,把请求方式 + 请求路径作为簇点资源名:
添加以下配置:
由于我已经提前添加好了,所以我这里就可以直接看到请求了
http-method-specify: true # 开启请求方式前缀
而我的请求就是这个,可以看到他在一分钟内通过了8次,所以说刚刚我们的请求都发送到他这里
2.2.1请求限流
现在我们就可以对他进行限流了
在弹出的框中这样选择
这样就把查询购物车列表这个簇点资源的流量限制在了每秒 6 个,也就是最大 QPS 为 6 。c
这里为大家推荐一个软件,可以用来帮助我们测试高并发的
jmeter5.4.1安装步骤-CSDN博客
根据此链接的博主来操作即可
这里发送1000个请求,在100秒内完成,也就是说每秒至少发送10个请求
右击选择启动即可
回到sentinel中进行查看,这里每次通过的都不超过6次,因为我们设置的最大通过数就是6,而不通过数有3有4甚至有5,这是因为虽然每秒10条请求,但也只是平均值,所以说又说会快有时会慢
2.2.2请求隔离
限流可以降低服务器压力,尽量减少因并发流量引起的服务故障的概率,但并不能完全避免
服务故障。一旦某个服务出现故障,我们必须隔离对这个服务的调用,避免发生雪崩。
比如,查询购物车的时候需要查询商品,为了避免因商品服务出现故障导致购物车服务级联失败,我们可以把购物车业务中查询商品的部分隔离起来,限制可用的线程资源:
2.2.2.1 OpenFeign整合Sentinel
修改模块的application.yml文件,开启Feign的sentinel功能:
feign:
sentinel:
enabled: true # 开启feign对sentinel的支持
再修改一下tomcat的配置
server:
port: 8082
tomcat:
threads:
max: 50 # 允许的最大线程数
accept-count: 50 # 最大排队等待数量
max-connections: 100 # 允许的最大连接
然后重启服务,可以看到查询商品的FeignClient自动变成了一个簇点资源:
这里勾选的是并发线程数限制,也就是说这个查询功能最多使用 5 个线程,而不是 5 QPS。
如果查询商品的接口每秒处理 2 个请求,则 5 个线程的实际 QPS 在 10 左右,而超出的请求自然
会被拒绝。
接着测试一下:利用 Jemeter 测试,每秒发送 100 个请求:
进入查询购物车的请求每秒大概在 100,而在查询商品时却只剩下每秒 10 左右,符合我们的
预期。
当访问其他正常的接口的时候,响应时间非常短,这就证明线程隔离起到了作用,尽管查询购物车这个接口并发很高,但是它能使用的线程资源被限制了,因此不会影响到其它接口。
2.2.3 服务熔断
2.2.3.1 编写降级代码
触发限流或熔断后的请求不一定要直接报错,也可以返回一些默认数据或者友好提示,用户体验会更好。
给FeignClient编写失败后的降级逻辑有两种方式:
-
方式一:FallbackClass,无法对远程调用的异常做处理
-
方式二:FallbackFactory,可以对远程调用的异常做处理,我们一般选择这种方式。
这里我们演示方式二的失败降级处理。
(1)在模块中给ItemClientFallbackFactory定义降级处理类,实现 FallbackFactory:
代码如下:
package com.hmall.api.client.fallback;
import com.hmall.api.client.ItemClient;
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import com.hmall.common.utils.CollUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import java.util.Collection;
import java.util.List;
@Slf4j
public class ItemClientFallbackFactory implements FallbackFactory<ItemClient> {
@Override
public ItemClient create(Throwable cause) {
return new ItemClient() {
@Override
public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
log.error("查询商品信息失败", cause);
return CollUtils.emptyList();
}
@Override
public void deductStock(List<OrderDetailDTO> items) {
log.error("扣减商品库存失败", cause);
throw new RuntimeException(cause);
}
};
}
}
(2)ItemClientFallbackFactory 注册为 Bean 对象:
@Bean
public ItemClientFallbackFactory itemClientFallbackFactory(){
return new ItemClientFallbackFactory();
}
在模块中此接口中添加此属性
重启后,再次测试,发现被限流的请求不再报错,走了降级逻辑:
不会直接报异常错误,而是走 Fallback 逻辑:
当请求被拒绝之后会来到 Fallback 逻辑,我们写的 Fallback 逻辑就是打印日志:
2.2.3.2 服务熔断
Sentinel 中的断路器不仅可以统计某个接口的慢请求比例,还可以统计异常请求比例。当这
些比例超出阈值时,就会熔断该接口,即拦截访问该接口的一切请求,降级处理;当该接口恢复正
常时,再放行对于该接口的请求。
断路器的工作状态切换有一个状态机来控制:
状态机包括三个状态:
1)closed:关闭状态,断路器放行所有请求,并开始统计异常比例、慢请求比例。超过阈值
则切换到 open 状态。
2)open:打开状态,服务调用被熔断,访问被熔断服务的请求会被拒绝,快速失败,直接
走降级逻辑。Open 状态持续一段时间后会进入 half-open 状态。
3)half-open:半开状态,放行一次请求,根据执行结果来判断接下来的操作。
请求成功:则切换到 closed 状态
请求失败:则切换到 open 状态
我们可以在控制台通过点击簇点后的熔断按钮来配置熔断策略:
1、点击熔断
这种是按照慢调用比例来做熔断,上述配置的含义是:
1)RT 超过 200 毫秒的请求调用就是慢调用。
2)统计最近 1000ms 内的最少 5 次请求,如果慢调用比例不低于 0.5,则触发熔断。
3)熔断持续时长 20 s 。配置完成后,再次利用 Jemeter 测试,可以发现:
在一开始一段时间是允许访问的,后来触发熔断后,查询商品服务的接口通过 QPS 直接为
0,所有请求都被熔断了。而查询购物车的本身并没有受到影响。
此时整个购物车查询服务的平均 RT 影响不大:
至此,三种方式都展示完毕