Sentinel入门学习记录
最近公司里面在进行微服务开发,因为有使用到限流降级,所以去调研学习了一下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入门学习记录的更多相关文章
- redis入门学习记录(二)
继第一节 redis入门学习记录(一)之后,我们来学习redis的基本使用. 接下来我们看看/usr/local/redis/bin目录下的几个文件作用是什么? redis-benchmark:red ...
- gulp入门学习教程(入门学习记录)
前言 最近在通过教学视频学习angularjs,其中有gulp的教学部分,对其的介绍为可以对文件进行合并,压缩,格式化,监听,测试,检查等操作时,看到前三种功能我的心理思想是,网上有很多在线压缩,在线 ...
- SpringBoot入门学习记录(一)
最近,SpringBoot.SpringCloud.Dubbo等框架非常流行,作为Coder里的一名小学生,借着改革开放的东风,自然也是需要学习学习的,于是将学习经历记录于此,以备日后查看. 官网:h ...
- [2017.02.07] Lua入门学习记录
#!/home/auss/Projects/Qt/annotated/lua -- 这是第一次系统学习Lua语言 --[[ 参考资料: 1. [Lua简明教程](http://coolshell.cn ...
- scikit-learn入门学习记录
一加载示例数据集 from sklearn import datasets iris = datasets.load_iris() digits = datasets.load_digits() 数据 ...
- mybatis入门学习记录(一)
过硬的技术本领,可以给我们保驾护航,飞得更高.今天开始呢.我们就一起来探讨使用mybatis的好处. 首先我们一起来先看看原生的JDBC对于数据库的操作,然后总结其中的利弊,为学习mybatis奠定基 ...
- Python3.5入门学习记录-File
在Python中,操作文件对象使用open函数来创建,下表列出了常用的操作file的函数: 序号 方法及描述 1.file.close() 关闭文件.关闭后文件不能再进行读写操作. 2.file.fl ...
- Python3.5入门学习记录-模块
模块让你能够有逻辑地组织你的Python代码段. 把相关的代码分配到一个 模块里能让你的代码更好用,更易懂. 模块也是Python对象,具有随机的名字属性用来绑定或引用. 简单地说,模块就是一个保存了 ...
- Python3.5入门学习记录-函数
Python 函数 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print().但你也 ...
随机推荐
- Java反射——读取XML文件,创建对象
读取XML文件,创建对象 config.xml <?xml version="1.0" encoding="UTF-8"?> <beans&g ...
- mfc c++优化
1.不住求精度时,尽量使用单精度浮点型2.使用32位数据类型3.使用有符号和无符号整型: 前提:无需考虑正负时 double x; int i; x = i; 使用有符号:unsigned int i ...
- 学习Java的第一步,配置电脑环境
JAVA安装与配置 俗话说的好,工欲善其事,必先利其器,想要学习Java,那么我们首先需要一个能够进行学习的环境. 一.安装JDK 为什么要安装jdk,jdk是什么? JDK是java软件开发包( ...
- java12(eclipse断点调试)
选择结构switch 1.格式: switch(整型数据){ case 值A:System.out.println("");break; case 值B:System.out.pr ...
- HTML 和CSS
1 HTML 介绍1.1 web 服务本质import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080))sk.lis ...
- ModelViewSet里的过滤、排序、分页、序列化设置
1.DRF初始化 1.认证 2.权限 3.限流 4.序列化 5.分页 6.版本 7.过滤 8.排序 1.1安装DjangoRestFramework pip install djangoresfra ...
- moviepy音视频剪辑:使用fl_time报错OSError: MoviePy error: failed to read the first frame of video file
专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt+moviepy音视频剪辑实战 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 在m ...
- 第8.13节 Python类中内置方法__repr__详解
当我们在交互环境下输入对象时会直接显示对象的信息,交互环境下输入print(对象)或代码中print(对象)也会输出对象的信息,这些输出信息与两个内置方法:__str__方法和__repr__方法有关 ...
- 第12.6节 Python标准库其他内置模块导览
一. 文本处理服务 string模块 : 常见的字符串操作 difflib模块: 计算差异的辅助工具 textwrap模块: 文本自动换行与填充,能够格式化文本段落,以适应给定的屏幕宽度: unico ...
- 问题:PyCharm的几种调试方法的区别
关于PyCharm的调试方式,step into.step over.step out.run to cursor.resume programe与c语言相关的调试器功能基本相同,但PyCharm提供 ...