流控规则

流控规则是用于完成服务流控的。服务流控即对访问流量的控制,也称为服务限流。Sentine实现流控的原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的值时对再到来的请求进行进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。

流量控制有两种阈值统计类型,QPS和并发线程数

QPS

设置流控规则

代码设置流控调用的方法,@SentinelResource(value = "get", fallback = "getFallBack", blockHandler = "getFlowFallBack")

package com.zjw.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.zjw.domain.Depart;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate; import java.util.List; /**
* <p>
* 部门表 前端控制器
* </p>
*
* @author zjw
* @since 2023-11-20
*/
@RestController
@RequestMapping("/depart")
@Slf4j
public class DepartController { @Resource
private RestTemplate restTemplate; private static final String PROVIDER_URL = "http://depart-provider/depart"; /**
* 根据id查询部门
*/
// 发生异常,跳到500空白页
// @SentinelResource(value = "get")
// 发生异常会降级,调用getFallBack方法
// @SentinelResource(value = "get", fallback = "getFallBack")
//发生异常会降级,调用getFallBack方法, 触发流控,会调用流控的getFlowFallBack方法
@SentinelResource(value = "get", fallback = "getFallBack", blockHandler = "getFlowFallBack")
// 发生异常会降级,调用getFallBack方法,
@GetMapping("/get/{id}")
public Depart get(@PathVariable Long id) {
return restTemplate.getForObject(PROVIDER_URL + "/get/" + id, Depart.class);
} /**
* 服务流控使用的方法.
* 需要指定BlockException参数,否则调用降级方法
*/
public Depart getFlowFallBack(Long id, BlockException e) {
log.info("id = " + id);
log.info("exception = " + e.getMessage());
Depart depart = new Depart();
depart.setId(id);
depart.setName("flow fall back");
return depart;
} /**
* 服务降级使用的方法
*/
public Depart getFallBack(Long id, Throwable t) {
log.info("id = " + id);
log.info("throwable = " + t.getMessage());
Depart depart = new Depart();
depart.setId(id);
depart.setName("no this depart");
return depart;
}

控制台设置流控的规则:

如果调用的服务停止了,会触发降级,调用getFallBack方法。

当请求QPS超过3时会触发流控规则,调用getFlowFallBack方法。

api设置流控规则

通过控制台设置的流控规则会在服务重启后失效,可以通过sentinel的api在代码中指定规则

package com.zjw;

import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import java.util.ArrayList;
import java.util.List; @SpringBootApplication
public class ConsumerFlowRule8080Application { public static void main(String[] args) {
SpringApplication.run(ConsumerFlowRule8080Application.class, args);
//初始化流控规则
initFlowRule();
} private static void initFlowRule() {
FlowRuleManager.loadRules(configFlowRule());
} private static List<FlowRule> configFlowRule() {
List<FlowRule> flowRuleList = new ArrayList<>();
FlowRule rule = new FlowRule();
// 资源名
rule.setResource("get");
// 针对来源
rule.setLimitApp("default");
// 阈值类型 (并发线程数 0: thread count, 1: QPS).
// RuleConstant.FLOW_GRADE_THREAD 0
// RuleConstant.FLOW_GRADE_QPS 1
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 单机阈值
rule.setCount(3);
flowRuleList.add(rule);
return flowRuleList;
}
}

资源实体指定流控规则

package com.zjw.controller;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.zjw.domain.Depart;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate; import java.util.List; /**
* <p>
* 部门表 前端控制器
* </p>
*
* @author zjw
* @since 2023-11-20
*/
@RestController
@RequestMapping("/depart")
@Slf4j
public class DepartController { @Resource
private RestTemplate restTemplate; private static final String PROVIDER_URL = "http://depart-provider/depart"; @GetMapping("/get2/{id}")
public Depart get2(@PathVariable Long id) {
Entry entry = null;
try {
entry = SphU.entry("xxx");
return restTemplate.getForObject(PROVIDER_URL + "/get/" + id, Depart.class);
} catch (BlockException e) {
log.info("id = " + id);
log.info("exception = " + e.getMessage());
Depart depart = new Depart();
depart.setId(id);
depart.setName("entry flow fall back");
return depart;
} finally {
if (entry != null)
entry.exit();
}
}
}

控制台设置

触发流控

并发线程数

隔离方案:

A 线程池隔离

系统为不同的提供者资源设置不同的线程池来隔离业务自身之间的资源争抢。该方案隔离性较好,但需要创建的线程池及线程数量太多,系统消耗较大。当请求线程到达后,会从线程池中获取到一个新的执行线程去完成提供者的调用。由请求线程到执行线程的上下文切换时间开销较大。特别是对低延时的调用有比较大的影响。

B 信号量隔离

系统为不同的提供者资源设置不同的计数器。每增加一个该资源的调用请求,计数器就变化一次。当达到该计数器阙值时,再来的请求将被限流。该方式的执行线程与请求线程是同一个线程,不存在线程上下文切换的问题,更不存在很多的线程池创建与线程创建问题。“也正因为请求线程与执行线程没有分离,所以,其对于提供者的调用无法实现异步,执行效率降低,且对于依赖资源的执行超时控制不方便。

Sentinel 隔离方案

Sentinel 并发线程数控制也属于隔离方案,但不同于以上两种隔离方式,是对以上两种方案的综合与改进,或者说更像是线程池隔离。其也将请求线程与执行线程进行了分离,但 Sentinel 不负责创建和管理线程池,而仅仅是简单统计当前资源请求占用的线程数目。如果对该资源的请求占用的线程数量超出了阙值,则可以立即拒绝再新进入的请求。

流控模式-关联



当关联的资源触(主业务)发流控,如list资源QPS大于3,会将get资源也进行流控。

流控模式-链路

当对一个资源有多种访问路径时,可以对某一路劲的访问进行限流,而其他访问路径不限流。

application.yml配置

server:
port: 8081 spring:
datasource:
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/sc?serverTimezone=Asia/Shanghai&useSSL=false&characterEncoding=utf-8&rewriteBatchedStatements=true
application:
name: depart-provider # 微服务名称
cloud:
nacos:
discovery:
server-addr: nacos-local:8848 # nacos注册中心地址
username: nacos # 用户名密码
password: nacos
sentinel: # sentinel配置
eager: true # 饥饿加载,默认false
# 默认true,会收敛所有URL入口,即对于同一资源的请求URL到Sentinel后会
# 统一为一个URL。即Sentinel只关心被访问资源,不需要区分请求的来源
# 设置为false后,就可以区分请求来源了
web-context-unify: false
transport:
port: 8719 # sentinel内部启动的http服务
dashboard: localhost:8888 # sentinel服务
mybatis-plus:
# 别名包扫描路径
type-aliases-package: com.zjw.domain
global-config:
db-config:
# 设置id字段为自增长
id-type: auto
logic-delete-field: deleted #逻辑删除的字段
logic-delete-value: 1 # 已经逻辑删除的记录该字段值
logic-not-delete-value: 0 # 未被逻辑删除的记录该字段值
configuration:
# mybatis二级缓存,默认为true,开启
cache-enabled: false
# 是否开启自动驼峰命名规则,默认为true,开启
map-underscore-to-camel-case: true
# 设置控制台日志打印,默认不显示SQL语句
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 枚举字段加上了@EnumValue注解后默认使用的是MybatisEnumTypeHandler,否则默认为mybatis EnumTypeHandler
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
# Mapper 所对应的 XML 文件位置,默认为“classpath*:/mapper/**/*.xml”
mapper-locations:
- classpath*:/mapper/**/*.xml

现在service有一个查询所有的服务list,我们加上流控@SentinelResource

package com.zjw.service.impl;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zjw.domain.Depart;
import com.zjw.mapper.DepartMapper;
import com.zjw.service.IDepartService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import java.util.ArrayList;
import java.util.List; /**
* <p>
* 部门表 服务实现类
* </p>
* @since 2023-11-20
*/
@Service
@Slf4j
public class DepartServiceImpl extends ServiceImpl<DepartMapper, Depart> implements IDepartService { @SentinelResource(value = "list", blockHandler = "listFlowFallBack")
@Override
public List<Depart> list() {
return super.list();
} public List<Depart> listFlowFallBack(BlockException e) {
log.info("流控调用");
List<Depart> list = new ArrayList<>();
Depart depart = new Depart();
depart.setName("listFallBack");
list.add(depart);
return list;
}
}

在controller层有两个方法调用list

package com.zjw.controller;

import com.zjw.domain.Depart;
import com.zjw.service.IDepartService;
import jakarta.annotation.Resource;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.web.bind.annotation.*; import java.util.HashMap;
import java.util.List;
import java.util.Map; /**
* <p>
* 部门表 前端控制器
* </p>
*
* @since 2023-11-20
*/
@RestController
@RequestMapping("/depart")
public class DepartController { @Resource
private IDepartService departService; /**
* 查询所有部门
*/
@GetMapping("/list")
public List<Depart> list() {
return departService.list();
} @GetMapping("/all")
public List<Depart> all() {
return departService.list();
}

如果我们需要对通过/depart/list的方法进行流控,可以在sentinel控制台进行如下设置:

测试:

访问http://localhost:8081/depart/list会被流控,访问http://localhost:8081/depart/all并不会被流控。



控制效果

当 QPS 超过某个阈值的时候,则采取措施进行流量控制。流量控制的效果包括以下几种:直接拒绝Warm Up匀速排队(排队等待)。对应 FlowRule 中的 controlBehavior 字段。[1]

注意:若使用除了直接拒绝之外的流量控制效果,则调用关系限流策略(strategy)会被忽略。

快速失败

直接拒绝(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。具体的例子参见 FlowQpsDemo[2]

Warm Up

Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。详细文档可以参考 流量控制 - Warm Up 文档,具体的例子可以参见 WarmUpFlowDemo

通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:

排队等待

匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细文档可以参考 流量控制 - 匀速器模式,具体的例子可以参见 PaceFlowDemo

该方式的作用如下图所示:

这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。

注意:匀速排队模式暂时不支持 QPS > 1000 的场景。

三种控制效果API实现


private static void initFlowRule() {
FlowRuleManager.loadRules(configQpsFlowRule());
} // QPS 控制效果
private static List<FlowRule> configQpsFlowRule() {
List<FlowRule> flowRuleList = new ArrayList<>();
FlowRule rule = new FlowRule();
// 资源名
rule.setResource("get");
// 针对来源
rule.setLimitApp("default");
// 阈值类型 (并发线程数 0: thread count, 1: QPS).
// RuleConstant.FLOW_GRADE_THREAD 0
// RuleConstant.FLOW_GRADE_QPS 1
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 单机阈值
rule.setCount(3);
flowRuleList.add(rule);
return flowRuleList;
} // warm up 控制效果
private static List<FlowRule> configWarmUpFlowRule() {
List<FlowRule> flowRuleList = new ArrayList<>();
FlowRule rule = new FlowRule();
// 资源名
rule.setResource("get");
// 针对来源
rule.setLimitApp("default");
// 阈值类型 (并发线程数 0: thread count, 1: QPS).
// RuleConstant.FLOW_GRADE_THREAD 0
// RuleConstant.FLOW_GRADE_QPS 1
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 单机阈值
rule.setCount(3);
// 流控效果
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
// 预热时长 s
rule.setWarmUpPeriodSec(10);
flowRuleList.add(rule);
return flowRuleList;
} // 排队等待 控制效果
private static List<FlowRule> configPaceFlowRule() {
List<FlowRule> flowRuleList = new ArrayList<>();
FlowRule rule = new FlowRule();
// 资源名
rule.setResource("get");
// 针对来源
rule.setLimitApp("default");
// 阈值类型 (并发线程数 0: thread count, 1: QPS).
// RuleConstant.FLOW_GRADE_THREAD 0
// RuleConstant.FLOW_GRADE_QPS 1
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 单机阈值
rule.setCount(3);
// 流控效果
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
// 预热时长 20s
rule.setMaxQueueingTimeMs(20 * 1000);
flowRuleList.add(rule);
return flowRuleList;
}

来源流控

来源名称可以在请求参数、请求头或Cookie 中通过 key-value 形式指定,而sentinel 提供了一个RequestOriginParser 的请求解析器,sentinel可以从该解析器中获取到请求中携带的来源,然后将流控规则应用于获取到的请求来源之上。

package com.zjw.parser;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils; /**
* @since 2023/12/04 18:33
*/
@Component
@Slf4j
public class DepartRequestOriginParser implements RequestOriginParser { @Override
public String parseOrigin(HttpServletRequest request) {
String source = request.getParameter("source");
if(!StringUtils.hasText(source)){
//设置为默认来源
source = "guest";
}
log.info("source = " + source);
return source;
}
}

controller

package com.zjw.controller;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.zjw.domain.Depart;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate; import java.util.List; /**
* <p>
* 部门表 前端控制器
* </p>
*
* @since 2023-11-20
*/
@RestController
@RequestMapping("/depart")
@Slf4j
public class DepartController { @Resource
private RestTemplate restTemplate; private static final String PROVIDER_URL = "http://depart-provider/depart"; /**
* 根据id查询部门
*/
//发生异常会降级,调用getFallBack方法, 触发流控,会调用流控的getFlowFallBack方法
@SentinelResource(value = "get", fallback = "getFallBack", blockHandler = "getFlowFallBack")
@GetMapping("/get/{id}")
public Depart get(@PathVariable Long id) {
return restTemplate.getForObject(PROVIDER_URL + "/get/" + id, Depart.class);
} /**
* 服务流控使用的方法.
* 需要指定BlockException参数,否则调用降级方法
*/
public Depart getFlowFallBack(Long id, BlockException e) {
log.info("id = " + id);
log.info("exception = " + e.getMessage());
Depart depart = new Depart();
depart.setId(id);
depart.setName("flow fall back");
return depart;
} /**
* 服务降级使用的方法
*/
public Depart getFallBack(Long id, Throwable t) {
log.info("id = " + id);
log.info("throwable = " + t.getMessage());
Depart depart = new Depart();
depart.setId(id);
depart.setName("no this depart");
return depart;
}
}

sentinel控制台配置

对于来源为guest的进行流控限制。

参考:


  1. https://github.com/alibaba/Sentinel/wiki/流量控制

  2. https://github.com/alibaba/Sentinel/blob/master/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/flow/FlowQpsDemo.java

Sentinel——流控规则的更多相关文章

  1. 什么!Sentinel流控规则可以这样玩?

    项目源码地址:公众号回复 sentinel,即可免费获取源码 前言 上一篇文章中,我们讲解了关于sentinel基本介绍以及流控规则中直接和快速失败的效果,有兴趣的可以去看上一篇文章,今天,我们给大家 ...

  2. SpringBoot 2.0 + Nacos + Sentinel 流控规则集中存储

    前言 Sentinel 原生版本的规则管理通过API 将规则推送至客户端并直接更新到内存中,并不能直接用于生产环境.不过官方也提供了一种 Push模式,扩展读数据源ReadableDataSource ...

  3. Sentinel流控规则

    流控规则 注:Sentinel的监控页面一开始是没有东西,需要对监控的服务发起请求后才会出现 资源名:唯一名称,默认请求路径 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,指定对哪个 ...

  4. sentinel流控规则校验之源码分析

    前言: 上节给大家把sentinel流控整个执行大致过了,但涉及到最核心的流控算法还没有讲,先提前说明一下 sentinel用的流控算法是令牌桶算法,参考了Guava的RateLimiter,有读过R ...

  5. 线上应用接入sentinel的第一个流控规则

    sentinel接入第1个应用A以及控制台,已经上线一段时间了,本周接入了第2个应用B: 因为测试同学只有几个,没有压测团队.测试平台.. 各接口能承载的最大qps不确定 ,接入的应用暂时都没有配置规 ...

  6. Sentinel Dashboard(基于1.8.1)流控规则持久化到Nacos——涉及部分Sentinel Dashboard源码改造

    前言 之前虽然也一直在使用sentinel实现限流熔断功能,但却没有好好整理之前看的源码与资料,今天有时间将之前自己整理过的资料写成一篇博文,或者是是一篇关于Sentinel(基于目前最近版本1.8, ...

  7. Sentinel之流控规则

    在上文Sentinel流量防卫兵中讲到了Sentinel入门以及流控规则一小部分,而Sentinel还有以下规则: 熔断降级规则 热点参数规则 系统规则 黑白名单规则 本文要讲的是流控规则 流量控制规 ...

  8. sentinel的四种流控规则介绍

    sentinel的四种流控规则介绍 今天的内容我们主要围绕四个点进行展开介绍. 流控模式 :关联.链路 流控效果 :Warm Up.排队等待 这四点具体是什么意思呢? 首先启动项目:cloud-ali ...

  9. Sentinel流控与熔断

    参考: https://thinkwon.blog.csdn.net/article/details/103770879 项目结构 com.guo     ├── guo-sentinel       ...

  10. 微服务架构 | 5.4 Sentinel 流控、统计和熔断的源码分析

    目录 前言 1. Sentinel 的自动装配 1.2 依赖引入 1.3 SentinelWebAutoConfiguration 配置类 1.4 CommonFilter 过滤器 1.5 小结 2. ...

随机推荐

  1. windows配置maven

    1.下载mavenhttps://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/ 中找到相应的版本2.解压3.配置环境变量MAVEN_HOMED: ...

  2. 安川机器人U轴减速机 HW9381465-C维修具体细节

    安川机器人U轴减速机 HW9381465-C的维修是一个相对复杂的过程,涉及到多个部件的检查.维修和更换.以下是一些具体细节: 1.故障诊断: · 对安川机器人U轴减速机 HW9381465-C进行彻 ...

  3. JUC并发—10.锁优化与锁故障

    大纲 1.标志位修改场景优先使用volatile(服务优雅停机) 2.数值递增场景优先使用Atomic类(心跳计数器) 3.共享变量仅对当前线程可见的场景优先使用ThreadLocal(edits l ...

  4. DW005 - ArgoDB基础

    第1章 Argo基础 1.1 ArgoDB对象 说明 在ArgoDB中,可以使用常见的数据库对象,包括数据库(Database),表(Table),视图(View)和函数(Function) 可以使用 ...

  5. OERV兴趣探索:模拟器移植

      最近看了很多开源项目,主要都集中在模拟器方面,我指的是游戏模拟器比如GameBoy或者PlayStation这一类.现在想玩这系列的游戏可以在手机或者电脑下载相应的模拟器,并且获取对应的ROM文件 ...

  6. 一个生成随机颜色的js函数

    function getRandomColor(){ let rgb = []; for(let i=0;i<3;++i){ let color = Math.floor(Math.random ...

  7. 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!

    3月6日最新消息,阿里云通义千问官方宣布推出最新推理模型 QwQ-32B,这一模型仅有 32B 参数,但在效果上与拥有 671B 参数的 DeepSeek-R1 相媲美.如果你自己部署 DeepSee ...

  8. 【WPF开发】HandyControl Growl控件Error通知不自动消失的问题

    需求 HandyControl Growl在Error类型的通知不自动消失,此时需要他跟其他的统一. 找寻原因 那么翻翻代码看看为啥不消失呗 1.这是决定关闭通知的计时器 2.这是通过_staysOp ...

  9. linux怎么关闭selinux

    关闭方法:1.临时关闭,只需执行"setenforce 0"命令即可.2.永久关闭,需要执行"vi /etc/selinux/config"命令打开config ...

  10. 【SpringMVC】数据转换 & 数据格式化

    数据转换 & 数据格式化 & 数据校验 数据转换 数据绑定流程 Spring MVC 主框架将 ServletRequest 对象及目标方法的入参实例传递给 WebDataBinder ...