微服务架构的流行使得在分布式系统中保障稳定性变得尤为关键。在前文中,已经讨论了微服务中可能出现的雪崩问题以及相应的解决方案。作为确保系统可用性的关键工具之一,Sentinel 应运而生,它是一款功能强大的流量控制组件,为开发人员提供了多种方式来管理和保护微服务。
在本文中,我将深入探讨 Sentinel 的核心功能,包括流控模式、流控效果和对热点资源的限流策略。深入了解这些概念,并通过示例演示如何在 Spring Cloud 项目中使用 Sentinel,以便更好地应对各种流量控制和限流需求。
在学习 Sentinel 的使用之前,我们有必要首先了解一下簇点链路。所谓的簇点链路就是项目内的调用链路,链路中被监控的每个接口就是一个资源。默认情况下 Sentinel 会监控 Spring MVC 的每一个端点(Endpoint),因此 SpringMVC 的每一个端点(Endpoint)就是调用链路中的一个资源。
流控、熔断等都是针对簇点链路中的资源来设置的,因此我们可以点击对应资源后面的按钮来设置规则:
现在,我们需要对 /order/{orderId}
这个路径进行限流,要求它的 QPS 每秒不能超过 5,设置步骤如下:
设置了20个线程,线程的启动时间是 2s,此时的 QPS 是10。
配置 HTTP 请求:
在这个 HTTP 请求中,访问的资源是 /order/101
。
启动 JMeter 进行测试:
Sentinel 是一款功能强大的流量控制组件,它支持多种流控模式,以帮助开发人员更好地管理和保护微服务。下面将介绍 Sentinel 的三种主要流控模式:直接模式、关联模式和链路模式。
在使用 Sentinel 添加限流规则时,点击高级选项,可以选择三种流控模式:
直接模式是 Sentinel 最基本的流控模式,它通过对资源的访问频率进行限制来控制流量。在直接模式下,可以为每个资源配置允许的 QPS(每秒查询率)限制。如果某个资源的实际流量超过了配置的限制,Sentinel 将拒绝或降级该资源的访问请求。
这种模式适用于需要对某个具体资源进行流控的场景,例如 API 接口、微服务等。通过配置直接模式,您可以保护关键资源免受过多请求的干扰,确保系统的稳定性和可用性。
上文中 Sentinel 的简单使用示例就是直接模式,它是对order/{orderId}
这个具体的请求资源进行限流。
关联模式是 Sentinel 提供的一种更为灵活的流控模式。在关联模式中,可以定义多个资源之间的关联关系,然后基于这些关联关系来控制流量。这使得可以更好地适应多资源之间的复杂交互。
简单来说,关联模式的作用是统计与当前资源相关的另一个资源,当触发资源访问的阈值时,对当前资源限流。
关联模式的使用场景:
比如用户支付时需要修改订单状态,同时用户要查询订单。查询和修改操作会争抢数据库锁,产生竞争。业务需求是有限支付和更新订单的业务,因此当修改订单业务触发阈值时,需要对查询订单业务限流。
例如,下面在 order-service
中新增两个接口,query
表示查询订单,update
表示更新订单:
@GetMapping("/query")
public String queryOrder(){
return "查询订单成功!";
}
@GetMapping("/update")
public String updateOrder(){
return "更新订单成功!";
}
此时,将 query
和 update
两个接口进行关联,当 update
资源被访问的 QPS 超过 5 时,就会对 query
接口进行限流。
使用 JMeter 测试:
设置线程数,此时 QPS 为 10:
设置 HTTP 请求:
启动 JMeter 进行测试:
此时发送可以正常处理 update
接口的请求,但是此时我们通过浏览器访问query
接口,发现该接口被 Sentinel 限流了:
满足下面条件可以使用关联模式:
链路模式:只针对从指定链路访问到本资源的请求做统计,判断是否超过阈值。
例如有两条请求链路test1
和 test2
,从它们都可以服务到 commom
资源:
/test1
-> /common
/test2
-> /common
如果只希望统计从/test2
进入到/common
的请求,则可以使用链路模式进行配置:
现在有一个需求:有查询订单和创建订单业务,两者都需要查询商品。针对从查询订单进入到查询商品的请求统计,并设置限流。设置的步骤如下:
在OrderService
中添加一个queryGoods
方法,不用实现业务:
@SentinelResource("goods") // Sentinel 标记除 Controller 以外的方法
public void queryGoods(){
System.err.println("查询商品!");
}
注意,在默认情况下,Sentinel 只会监视 Controller
中的端点方法,如果要监视其他方法,可以使用 @SentinelResource
注解。
另外,Sentinel 默认会将 Controller
方法做 context
整合,导致链路模式的流控失效,需要修改application.yml
,添加如下配置:
spring:
cloud:
sentinel:
web-context-unify: false # 关闭 Context 上下文整合
在OrderController
中,改造/order/query
端点,调用OrderService
中的queryGoods
方法:
@GetMapping("/query")
public String queryOrder(){
// 查询商品
orderService.queryGoods();
// 查询订单
System.out.println("查询订单");
return "查询订单成功!";
}
在OrderController
中添加一个/order/save
的端点,调用OrderService
的queryGoods
方法:
@GetMapping("/save")
public String saveOrder(){
// 查询商品
orderService.queryGoods();
// 新增订单
System.out.println("新增订单");
return "新增订单成功!";
}
使用 JMeter 进行测试
设置线程,QPS 为 4:
设置 HTTP 请求:
分别设置了对 query
和 save
两个接口的 HTTP 请求。
启动 JMeter:
可以发现,对 query
接口进行限流了:
而对 save
接口没有限流:
在 Sentinel 中,流控效果是指当请求达到流控阈值时应该采取的措施。Sentinel 提供了多种流控效果,包括快速失败、预热模式以及排队等待。每种效果都适用于不同的使用场景,可以根据具体需求选择合适的流控效果。
FlowException
异常。通过前文的演示,可以发现是默认的处理方式。这些模式可以在 Sentinel 控制台的高级选项中进行设置,下面分别是对这些流控效果的演示。
快速失败 是 Sentinel 的默认流控效果。当请求达到流控阈值时,新的请求会被立即拒绝,并抛出 FlowException
异常。这意味着请求将立即失败,不会继续执行后续逻辑。这种效果适用于对系统资源有明确限制的场景,帮助尽早识别并拒绝过多请求,以避免系统超负荷运行。
使用快速失败效果可以保护系统免受过多请求的干扰,确保系统的稳定性和可用性。
预热模式 也被称为 “warm up” 模式。在预热模式下,超出流控阈值的请求同样会被拒绝并抛出异常,但不同的是,流控阈值在一段时间内会逐渐增加到最大阈值。
预热模式是应对服务冷启动的一种方案。请求阈值初始值是 threshold / coldFactor
,持续指定时长后,逐渐提高到threshold
值。而coldFactor
的默认值是3。
例如,设置QPS的threshold
为10,预热时间为 5 秒,那么初始阈值就是 10 / 3
,也就是 3,然后在 5 秒后从 3 逐渐增长到 10。
现在有一个需求:就是给/order/{orderId}
这个资源设置限流,最大 QPS 为 10,利用 warm up
效果,预热时长为 5 秒,步骤如下:
1. 新增流控规则:
启动 JMeter ,查看 Sentinel 控制台实时监控:
可以发现,QPS 的值最近增大,拒绝的请求数量也逐渐减少。
排队等待 流控效果允许所有的请求按照先后次序排队执行,保证请求之间的间隔不小于指定的时长。这意味着请求会进入队列等待处理,直到轮到请求执行。如果请求无法立即执行,它们将排队等待。
排队等待效果适用于需要有序执行请求的场景,例如需要按照请求的到达顺序处理的业务,或者要求请求间保持固定的时间间隔的场景。
例如:QPS = 5,意味着每 200ms 处理一个队列中的请求;timeout = 2000,意味着预期等待超过 2000ms 的请求会被拒绝并抛出异常。
现在有一个需求:给/order/{orderId}
这个资源设置限流,最大 QPS 为10,利用排队的流控效果,超时时长设置为 5s,步骤如下:
1. 添加限流规则:
2. 使用 JMeter 进行测试:
设置 HTTP 请求:
启动 JMeter,查看 Sentinel 的实时监控:
可以发现,在排队等待流控模式下,请求会有序排队执行,这有助于削峰平谷。这种模式对于高并发场景和需要请求按照到达顺序处理的业务非常有用。通过将请求按照时间间隔排队执行,系统可以更好地应对流量的突发增加,降低系统的压力,从而提高系统的稳定性和可用性。
前文的限流都是统计访问某个资源的所有请求,判断是否超过 QPS 阈值。而热点参数限流是分别统计参数值相同的请求,判断是否超过QPS阈值。
例如下图所示,访问同样一个链路,但是这个链路中 id 为 1 的资源访问的次数比其他资源多,那么说明该资源是热点资源,就可以将该资源的 QPS 设置的大一些:
在 Sentinel 控制台中,可以发现有一个热点规则,通过这个规则就可以对热点资源进行流控设置,例如:
代表的含义是:对hot
这个资源的 0 号参数(第一个参数)做统计,每 1 秒相同参数值的请求数不能超过 5。
另外,在热点参数限流的高级选项中,可以对部分参数设置例外配置:
结合上一个配置,这里的含义是对 0 号的 long
类型参数限流,每 1 秒相同参数的QPS不能超过 5,有两个例外:
现在有一个需求,就是给 /order/{orderId}
这个资源添加热点参数限流,规则如下:
热点参数限流对默认的 SpringMVC 资源无效,因此需要使用 @SentinelResource()
注解指定热点资源名称:
2. 使用 JMeter 进行测试:
设置QPS 为 5:
设置 HTTP 请求:
这里分别设置了对 ID 为 101、102、103 三个不同资源的HTTP请求,对应了上述设置的规则的不同情况。
启动 JMeter:
对于 ID 为 101 资源的结果树,发现每秒只能通过 2 个请求: