最近公司里面在进行微服务开发,因为有使用到限流降级,所以去调研学习了一下Sentinel,在这里做一个记录。

  Sentinel官方文档:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D


一、Sentinel的作用

1、Sentinel是什么

  Sentinel---分布式系统的流量卫兵。

  主要面向分布式架构的流量控制产品。以流量为切入点,从流量控制、熔断降级、系统负载保护等各个维度对服务提供保护。官方称其为轻量级产品。

2、基础概念

  2.1、资源:Sentinel的核心概念,一切需要Sentinel保护的东西都称为资源。它可以是一段代码、由应用程序提供的服务、或由应用程序调用的其它应用程序提供的服务。在程序中最直观的呈现就是由Sentinel Api包围起来的一段代码。

  2.2、规则:对资源的保护规则。如流量控制规则、熔断降级规则、系统保护规则等。所有规则都是可以实时动态调整的。

  2.3、埋点:定义资源的过程。

3、基本用途

  3.1、流量控制

  因为流量是不规则的,在流量高峰期,大批量流量瞬间涌入系统会冲垮服务,这个时候我们需要限制流量对系统进行保护;同时在高峰期,如果直接丢弃掉超过服务承载能力的所有流量,而在流量低谷期又只有很少的流量或者无流量,那么此时服务又是一种资源浪费,并且也无无法给用户一个很好的体验感,因为我们需要将流量高峰期的部分流量分流到低谷期,而不是在高峰期全数丢弃超过服务负载能力的流量。

  其原理就是监控应用流量的QPS或并发线程数,当达到指定的阈值时对流量进行控制。

  流控规则在Sentinel控制台的直观体现。

流控模式:

  直接:当接口达到限流条件时,开启限流;

  关联:当关联的资源达到限流条件时,开启限流;适合做应用让步;比如一个查询的接口添加关联限流,关联限流资源为一个更新的接口,当更新的接口达到阈值时,开启查询接口的限流,为更新接口让步服务器资源。

  比如说对于订单,现在有两种请求,一种是使用更新订单量请求【QUERY_ORDER_NUM】,另一种是查询请求【UPDATE_ORDER_NUM】。

  当正处于订单使用高峰期,更新请求达到了设定阈值,这时可以暂时降低订单查询服务的可用性,优先保障订单扣减逻辑的执行。

  链路:当从某个接口过来的资源达到限流条件时,开启限流。这是一种更精细化的资源管理方式。

  比如说定义一个资源 【SAY_HELLO】:

  且同时有两个接口 【/friendly】【/haughty】都调用该资源:

  这时我们配置SAY_HELLO的入口资源为 【/friendly】,那么当【/friendly】调用超过阈值时就会触发限流开启,而【/haughty】则不会触发。

流控效果

  快速失败:当请求达到限流阈值的时候,后续请求会立即拒绝,拒绝方式就是抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。

  Warm Up(预热/冷启动):当系统长期处于低水位的情况下,而流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",在指定的预热时间内,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。主要用于启动需要额外开销的场景;

  排队等待:该种模式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,从而达到一种流量整形的效果。

  3.2、熔断降级

  一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。

  例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。

  因此,我们需要对不稳定的依赖服务调用进行熔断降级。

熔断策略说明:

  慢调用比例:选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。

  异常比例:当单位统计时长内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。

  异常数:当单位统计时长内的异常数目超过阈值之后会自动进行熔断。


二、Sentinel的使用

  1、基础使用

  1.1、引入jar包

<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.0</version>
</dependency>

  1.2、定义资源

  用 Sentinel API SphU.entry("HelloWorld") 和 entry.exit()将需要进行流量控制的代码包围起来。被包围起来的代码就作为资源,用API包围起来即是埋点。

public static void fun() {
Entry entry = null;
try {
entry = SphU.entry(SOURCE_KEY);
pass.incrementAndGet();
// todo 业务逻辑
int temp = 10 / 0;
} catch (BlockException e1) {
block.incrementAndGet();
// todo 流控处理
} catch (Throwable e) {
Tracer.traceEntry(e, entry);
// todo 业务异常处理
} finally {
total.incrementAndGet();
if (entry != null) {
entry.exit();
}
}
}

  1.3、定义规则

private static void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule1 = new FlowRule();
rule1.setResource(SOURCE_KEY);
// 采用qps策略,每秒允许通过1个请求
rule1.setCount(1);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
}

  1.4、完整代码

/**
* 流量控制
*/
public class FlowQpsDemo { private static final String SOURCE_KEY = "CESHI_KEY"; private static AtomicInteger pass = new AtomicInteger();
private static AtomicInteger block = new AtomicInteger();
private static AtomicInteger total = new AtomicInteger(); public static void main(String[] args) throws InterruptedException {
initFlowQpsRule();
for (int i = 0;i < 10;i++) {
fun();
}
System.out.println("total=" + total.get() + " pass=" + pass.get() + " block=" + block.get());
} public static void fun() {
Entry entry = null;
try {
entry = SphU.entry(SOURCE_KEY);
// todo 业务逻辑
pass.incrementAndGet();
} catch (BlockException e1) {
// todo 流控处理
block.incrementAndGet();
} finally {
total.incrementAndGet();
if (entry != null) {
entry.exit();
}
}
} private static void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule1 = new FlowRule();
rule1.setResource(SOURCE_KEY);
// 采用qps策略,每秒允许通过1个请求
rule1.setCount(1);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
} private static void sleep(int sleep) {
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
}
}
}

  2、生产使用

  上面的示例代码演示了基础的使用方式,但是实际生产环境不会如此使用。一是直接将Sentinel Api代码写入到业务代码中,对业务代码造成了侵入;二是将规则写死在代码里面,不能动态调整。所以针对实际生产使用,我们使用Sentinel注解埋点,且集成Nacos,将配置数据同步到Nacos配置中心进行存储;

  2.1、Sentinel推模式

  在介绍生产环境使用Sentinel的时候,首先介绍一下Sentinel控制台、Sentinel客户端、配置中心的关系:

  首先在Sentinel Dashboard中维护各种限流、降级规则,Sentinel Dashboard将规则推送到Nacos进行持久化保存;同时Sentinel客户端注册Nacos数据源,订阅Nacos数据中心的流控规则,当发生变化时更新客户端本地内存规则。

  2.2、引入jar包

<!-- spring cloud 与 sentinel 集成包 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<!-- SpringCloud ailibaba sentinel-datasource-nacos 配置nacos动态配置中心 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.8.0</version>
</dependency>

  2.3、配置Sentinel

  2.3、注册动态配置中心

import com.alibaba.cloud.sentinel.SentinelProperties;
import com.alibaba.cloud.sentinel.datasource.config.NacosDataSourceProperties;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct;
import java.util.List; @Configuration
public class SentinelConfig { @Autowired
private SentinelProperties sentinelProperties; @PostConstruct
public void run() throws Exception {
// sentinel客户端注册流控nacos动态数据源
NacosDataSourceProperties flowConfiguration = sentinelProperties.getDatasource().get("flow").getNacos();
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(
flowConfiguration.getServerAddr(), flowConfiguration.getGroupId(), flowConfiguration.getDataId(),
source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
}));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty()); // sentinel客户端注册降级nacos动态数据源
NacosDataSourceProperties degradeConfiguration = sentinelProperties.getDatasource().get("degrade").getNacos();
ReadableDataSource<String, List<DegradeRule>> degradeRuleDataSource = new NacosDataSource<>(
degradeConfiguration.getServerAddr(), degradeConfiguration.getGroupId(),
degradeConfiguration.getDataId(),
source -> JSON.parseObject(source, new TypeReference<List<DegradeRule>>() {
}));
DegradeRuleManager.register2Property(degradeRuleDataSource.getProperty());
}
}

  2.4、使用注解埋点:@SentinelResource

@GetMapping("/ceshi/flow")
@SentinelResource(value = "CESHI_FLOW", blockHandler = "ceshiFlow")
public String ceshiFlow(@RequestParam String message) {
return "hello " + message;
} public String ceshiFlow(String message, BlockException exception) {
return "当前服务被限流,暂不可用! message=" + message;
} @GetMapping("/ceshi/degrade")
@SentinelResource(value = "CESHI_DEGRADE", blockHandler = "ceshiDegradeBlock", fallback = "ceshiDegradeFallback")
public String ceshiDegrade(@RequestParam int age) throws InterruptedException {
if (age < 0) {
throw new ApplicationBaseException("年龄非法");
} else if (age == 0) {
Thread.sleep(500);
return "***刚出生***";
}
return "您的年龄为:" + age;
} public String ceshiDegradeBlock(int age, BlockException e) {
return "当前请求被阻塞,age=" + age + " exMessage=" + e.getMessage();
} public String ceshiDegradeFallback(int age, Throwable t) {
return "当前请求执行失败,age=" + age + " exMessage=" + t.getMessage();
}
 

  2.6、在Sentinel控制台维护各项流控规则

  3、注解埋点详解-@SentinelResource

  官方文档地址:https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81

  摘抄几个属性如下:

  1、value:资源名称,必需项(不能为空)。

  2、entryType:entry 类型,可选项(默认为 EntryType.OUT)。简单来说从外面发起的请求进到系统,这类流量就属于EntryType.IN,如果从内部发起的内部资源调用或者内部发起的对外部资源调用,这类流量就为EntryType.OUT。其中系统保护规则只针对EntryType.IN生效。

  3、blockHandler / blockHandlerClass、fallback / fallbackClass:blockHandler是处理Sentinel本身限流降级的异常;而fallback处理的则是业务异常;如果两者都没有配置,那么直接抛出异常,若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException;如果只有blockHandler,那么只会处理sentinel的阻塞异常,不会处理业务异常;若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。

Sentinel入门学习记录的更多相关文章

  1. redis入门学习记录(二)

    继第一节 redis入门学习记录(一)之后,我们来学习redis的基本使用. 接下来我们看看/usr/local/redis/bin目录下的几个文件作用是什么? redis-benchmark:red ...

  2. gulp入门学习教程(入门学习记录)

    前言 最近在通过教学视频学习angularjs,其中有gulp的教学部分,对其的介绍为可以对文件进行合并,压缩,格式化,监听,测试,检查等操作时,看到前三种功能我的心理思想是,网上有很多在线压缩,在线 ...

  3. SpringBoot入门学习记录(一)

    最近,SpringBoot.SpringCloud.Dubbo等框架非常流行,作为Coder里的一名小学生,借着改革开放的东风,自然也是需要学习学习的,于是将学习经历记录于此,以备日后查看. 官网:h ...

  4. [2017.02.07] Lua入门学习记录

    #!/home/auss/Projects/Qt/annotated/lua -- 这是第一次系统学习Lua语言 --[[ 参考资料: 1. [Lua简明教程](http://coolshell.cn ...

  5. scikit-learn入门学习记录

    一加载示例数据集 from sklearn import datasets iris = datasets.load_iris() digits = datasets.load_digits() 数据 ...

  6. mybatis入门学习记录(一)

    过硬的技术本领,可以给我们保驾护航,飞得更高.今天开始呢.我们就一起来探讨使用mybatis的好处. 首先我们一起来先看看原生的JDBC对于数据库的操作,然后总结其中的利弊,为学习mybatis奠定基 ...

  7. Python3.5入门学习记录-File

    在Python中,操作文件对象使用open函数来创建,下表列出了常用的操作file的函数: 序号 方法及描述 1.file.close() 关闭文件.关闭后文件不能再进行读写操作. 2.file.fl ...

  8. Python3.5入门学习记录-模块

    模块让你能够有逻辑地组织你的Python代码段. 把相关的代码分配到一个 模块里能让你的代码更好用,更易懂. 模块也是Python对象,具有随机的名字属性用来绑定或引用. 简单地说,模块就是一个保存了 ...

  9. Python3.5入门学习记录-函数

    Python 函数 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print().但你也 ...

随机推荐

  1. Java里的数组降序

    Java升序容易,降序不易. 基本类型不能降序,至少要是包装类. 升序使用Arrays.sort() 降序要么使用Collections.reverse,要么实现Comparator接口 import ...

  2. Linux Tomcat安装篇(daemon运行,开机自启动)

    目录 前言 作为一个Java后端开发者,tomcat想必是最最最熟悉的一个开发组件了,tomcat环境的搭建部署都十分简单.安装部署只需要两步,第一步下载,第二步解压,这样一个基础的tomcat环境就 ...

  3. 20200221_python虚拟环境在Windows下安装配置_virtualenv不是内部或外部命令也不是可运行的程序或批处理文件

    1. 使用管理员启动命令行; 2. 安装虚拟环境 a)      .\pip install virtualenv  -i https://pypi.douban.com/simple/ b)     ...

  4. 基于CefSharp开发(四)浏览器文件下载

    一.CefSharp文件下载分析 查看ChromiumWebBrowser类发现cef数据下载处理在IDownloadHandler中进行,但并未找到相应的实现类,故我们需要自己实现DownloadH ...

  5. 第2.3节 Python运算符大全

    一. Python的算术运算 Python的算术运算符与C语言类似,略有不同.包括加(+).减(-).乘(*).除(/).取余(%).按位或(|).按位与(&).按位求补(~).左移位(< ...

  6. moviepy音视频剪辑:与time时间线相关的变换函数freeze_region、make_loopable、speedx、time_mirror、time_symmetrize介绍

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 在<moviepy音视频剪辑:moviepy中的剪辑基类Clip详解>介绍了剪辑基类的fl.fl_time.fx方法,在<movi ...

  7. 第7.23节 Python使用property函数定义属性简化属性访问的代码实现

    第7.23节 Python使用property函数定义属性简化属性访问的代码实现 一.    背景       在本章前面章节中,我们介绍了类相关的知识,并举例进行了说明,在这些例子中会定义一些形如 ...

  8. python序列化与反序列化(json、pickle)-(五)

    1.什么是序列化&反序列化? 序列化:将字典.列表.类的实例对象等内容转换成一个字符串的过程. 反序列化:将一个字符串转换成字典.列表.类的实例对象等内容的过程 PS:Python中常见的数据 ...

  9. [BJDCTF 2nd]假猪套天下第一 && [BJDCTF2020]Easy MD5

    [BJDCTF 2nd]假猪套天下第一 假猪套是一个梗吗? 进入题目,是一个登录界面,输入admin的话会返回错误,登录不成功,其余用户可以正常登陆 以为是注入,简单测试了一下没有什么效果 抓包查看信 ...

  10. Panda 交易所视点观察!区块链金融应用迎新规,哪些版块受影响?

    Panda交易所获悉,近日央行下发推动<区块链技术规范应用的通知>(以下简称"通知")及<区块链技术金融应用评估规则>(以下简称"规则" ...