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基础教程——String类
String类 Java程序中的所有字符串字面值(如 "abc" )都是String的实例 字符串是常量(因为 String 对象是不可变的,所以可以共享) 字符串的本质是字符数组 ...
- dubbo源码调试
1.从github上clone下duboo的源码并checkout tag到2.6.5可以看到如下的结构: 其中all-dubbo的pom如下: 这里会将dubbo的其他项目在package的时候打到 ...
- 加快alter table
mysql的alter table操作的性能对打表来说是个大问题. mysql执行大部分修改表结构的方法是用新的结构创建一个空表,从旧表中查出所有的数据插入新表,然后删除旧表.这样操作就可能需要花费很 ...
- SQL优化思路与解决方案
1.面对问题SQL的思考 这条查询SQL的语句到底有没有问题? 存在什么问题? 什么情况下存在问题? 怎么去优化? 2.SQL优化思路 where查询字段是否建立索引? 是否有建立索引但是查询时候没有 ...
- 第8.22节 Python案例详解:重写 “富比较”方法控制比较逻辑
一. 案例说明 本节定义一个小汽车的类Car,类中包括车名carname.百公里油耗oilcostper100km.价格price三个属性.然后实现__lt__.__gt__.__le__.__ge_ ...
- ABP框架使用Mysql数据库,以及基于SQLServer创建Mysql数据库的架构和数据
ABP默认的数据库是SQLServer,不过ABP框架底层是EF框架,因此也是很容易支持其他类型的数据库的,本篇随笔介绍在ABP框架使用Mysql数据库,以及基于SQLServer创建MySql数据库 ...
- jq中$(function(){})和js中window.onload区别
先看下执行代码: $(function(){ console.log("jq");}) $(function(){ console.log("jq1") ...
- js 导出div 中的类容为 word 文件
//引入包 <script src="/FileSaver.js"></script> <script src="/jquery.word ...
- 权威部门接连下发文件,Panda交易所带你走进区块链概念股
Panda交易所获悉,7月21日,北京市地方金融监督管理局发文<北京股权交易中心获得首批开展区块链试点建设资格>,文中表示证监会7月7日发布<关于原则同意北京.上海.苏州.浙江.深圳 ...
- 【题解】「P1504」积木城堡
这题是01背包(\(DP\)) 如何判断要拆走那个积木,首先定义一个\(ans\)数组,来存放这对积木能拼成多高的,然后如果\(ans_i = n\)那么就说明这个高度的积木可以. 话不多说,上代码! ...