1.3.1 配置限流策略
下边我们对item-service的商品查询接口进行限流,找到商品查询的簇点链路,点击"流控"
在弹出的菜单中填写QPS单机阈值为5,表示每秒最多处理5个请求
AI:什么是QPS
QPS 是 "Queries Per Second" 的缩写,中文通常称为“每秒查询率”或“每秒请求数”。它是一个衡量系统性能的重要指标,特别是在评估 Web 服务器、数据库系统、API 接口等的负载能力时经常使用。
QPS 表示系统在一秒钟内能够处理的查询或请求的数量。这个指标可以帮助开发者和运维人员了解系统的处理能力和性能瓶颈。例如,如果一个 Web 服务的 QPS 为 100,则意味着该服务在一秒钟内最多可以处理 100 个请求。
你的主打项目必须要提前准备好:QPS、TPS、RT、下载量、日活、最大数据量的表是什么
1.3.2 限流测试
首先去掉item-service服务中查询商品信息接口的休眠代码。然后启动item服务
下边我们用jmeter压力测试工具进行压力测试:Jmeter快速入门
JMeter 是一个广泛使用的开源性能测试工具,主要用于测试 Web 应用程序的负载和性能。它是由 Apache Software Foundation 开发和维护的,完全用 Java 编写,因此可以在任何支持 Java 的平台上运行。
找到课程资料中的JMeter安装包apache-jmeter-5.4.1.zip,解压到非中文的目录,进入bin下双击打开jmeter.bat
打开软件切换到中文
接下来找到课程资料下的“雪崩测试.jmx” 拖到JMeter面板中
最终效果如下(如有不同,及时修改)
设置模拟线程数等参数:
表示:总共发送1000个请求,用100秒发送,循环次数是1即执行10秒结束。
根据设置参数可知,每秒发送10个秒在100秒可完成1000个请求。
在“HTTP请求”界面设置压力测试请求的地址:确保跟你本地端口一致
右键“限流测试”点击“启动”
测试结果可以在“汇总报告”中查看,每次请求的次数在“察看结果树”中查看。
从吞吐量可以看出每秒发送10个请求,异常数是0
通过sentinel的实时监控界面可以看出对商品查询接口的请求,通过的QPS为5,拒绝QPS为5。
因为上边我们配置了对商品查询接口限流QPS为5,所以每秒10个请求查询/carts,远程调用商品查询服务接口的QPS只有5,说明被限流了。
注意:如果实时监控图无法正常显示需要进入虚拟机进行时间同步。
ntpdate ntp1.aliyun.com 如果无法执行上边的命令需要下载ntpdate yum install ntpdate -y
此时,需要耐心等待一下后重试,就可以看到有结果了:通过只有5。
如果你还是没有,也可以调整Jmeter参数,把1000,100改成:100,10,循环次数:永远
再查看每次的请求和响应数据发现,由于编写了商品查询接口远程调用的降级逻辑,当商品查询接口被限流后将会走降级方法,从jmeter测试结果可以看到被限流的请求无法获取商品信息
再查看cart-service的控制台,在降级方法中捕获到了 com.alibaba.csp.sentinel.slots.block.flow.FlowException 异常,这说明接口被限流后sentinel客户端会抛出FlowException 异常。
查看Jmeter也可以看到大量拒绝信息
1.3.3 小节
sentinel限流怎么实现?
我们项目使用Sentinel实现限流控制。
- 首先在服务提供方配置sentinel,引入sentinel的依赖,配置sentinel的地址
- 通过Sentinel控制台配置限流策略,可以配置QPS阈值、并发线程数等。
- 当方法被限流会走直接拒绝。
1.4. sentinel降级
上面讲的是A--Feign/Dubbo->B服务时候,对B的做熔断、限流,可如果我只想对A本身做限流怎么实现?
1.4.1 问题描述
下边我们对/carts接口进行限流测试,找到GET/carts簇点
点击“流控” 设置QPS 单机阈值为6
截止目前,我们一共设置了两个流控规则
用jmeter测试,通过sentinel进行实时监控,通过QPS为6,拒绝QPS为4,符合我们的预期结果
当/carts接口被限流时我们访问此接口(需要在Jmeter压测期间测试),需要连续多点击几次
结果内容:Blocked by Sentinel (flow limiting),从字面内容看是被sentinel限流。
为什么访问item-service的商品查询接口走了降级方法,而访问/carts没有走降级方法呢?
1.4.2 sentinel实现降级
这里我们需要重新梳理下:
前边cart-service远程调用item-service服务的商品查询接口正常走降级方法,这是因为我们编写了ItemClient接口的降级类ItemClientFallbackFactory,并且我们在cart-service服务中集成了sentinel,在cart-service的application.yml配置文件中配置了feign使用sentinel,如下:
feign: sentinel: enabled: true # 开启feign对sentinel的支持
也就说,在cart-service通过openfeign远程调用item-service时通过sentinel进行降级,最终执行ItemClientFallbackFactory类中指定的降级方法。这里我们是对/carts进行限流,并没有针对此接口编写降级方法。
AI:sentinel实现降级
针对feign远程调用我们通过实现FallbackFactory接口去编写远程调用接口对应的降级逻辑,而针对非feign远程调用的降级逻辑我们需要使用@SentinelResource注解去实现
首先使用 @SentinelResource 定义资源,编写降级方法
@ApiOperation("查询购物车列表") @SentinelResource(value = "queryMyCarts", fallback = "queryMyCartsFallback", blockHandler = "queryMyCartsBlockHandler") @GetMapping public List<CartVO> queryMyCarts(){ return cartService.queryMyCarts(); } //当发生非限流非熔断异常走此方法 public List<CartVO> queryMyCartsFallback(Throwable throwable){ log.error("非限流、非熔断异常执行的降级方法,throwable:", throwable); return new ArrayList<>(); } //当发生熔断、限流走此方法 public List<CartVO> queryMyCartsBlockHandler( BlockException blockException){ log.error("触发限流、熔断时执行的降级方法,blockException:", blockException); return new ArrayList<>(); }
然后在sentinel中设置资源的流控
删除针对/carts设置的流控规则
再次使用jmeter进行压力测试
观察cart-service控制台,当/carts限流后正常执行降级方法,日志如下:
18:37:31:767 ERROR 4696 --- [nio-8082-exec-9] c.hmall.cart.controller.CartController : 触发限流、熔断时执行的降级方法,blockException:
在降级方法中可以返回特殊的信息,或指定特殊的状态码,根据特殊值前端可展示为类似“网络忙请稍后重试” 这样的信息。此时限流期间访问购物车接口,发现就走了降级逻辑,返回空集合:
1.4.3 小节
sentienl降级怎么实现?
对于Feign远程调用对每个远程调用接口实现降级方法,通过实现FallbackFactory接口实现降级方法。
对于非Feign远程调用我们使用@SentinelResource注解编写自定义降级方法。
1.5. 线程隔离
1.5.1 方案介绍
首先我们来看下线程隔离功能,无论是Hystix还是Sentinel都支持线程隔离。不过其实现方式不同。
线程隔离有两种方式实现:
- 线程池隔离:给每个服务调用业务分配一个线程池,利用线程池本身实现隔离效果
- 信号量隔离:不创建线程池,而是计数器模式,记录业务使用的线程数量,达到信号量上限时,禁止新的请求
如图:
Sentinel的线程隔离就是基于信号量隔离实现的,而Hystix两种都支持,但默认是基于线程池隔离。
1.5.2 Sentinel线程隔离(练习)
下边我们使用Sentinel实现基于信号量的线程隔离。
点击查询商品的FeignClient对应的簇点资源后面的流控按钮:
在弹出的表单中填写下面内容:
注意,这里勾选的是并发线程数限制,也就是说这个查询功能最多使用5个线程,而不是5QPS。如果查询商品的接口每秒处理2个请求,则5个线程的实际QPS在10左右,而超出的请求自然会被拒绝。
cart-service调用商品查询控制在5个线程内,通过线程隔离即使商品服务出现问题也不会影响cart-service服务。
下边修改商品查询接口的代码,添加休眠500毫秒的代码,模拟一次请求需要500毫秒,一秒则可处理2次请求,5个线程可接收10 QPS左右。
将/carts的限流规则调大以免影响商品查询接口的限流控制。
下边使用jmeter进行测试,更改线程数为10000,时间为100秒,每秒需要发送100个请求。
启用测试,通过sentinel实时监控,符合我们的预期。
不知道细心的你是否发现了一个事情:我们设置的Sentinel通过数量,为什么不那么精准呢?
- 窗口边界:一个请求可能刚好跨越两个窗口,导致单个窗口的统计值略高于设定值。
- 滑动窗口精度:滑动窗口的划分粒度可能无法完全精确,尤其在请求密集时。