sentinel基本规则的api操作
sentinel的功能作用: 限流。
sentinel和hystrix的区别是怎么样的:
sentinel有哪些规则呢:
流量控制,熔断降级,热点参数限流,系统自适应限流,黑白名单控制,网关流控,集群流控
流量控制
流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
pom.xml
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>${sentinel.version}</version>
</dependency>
代码:
public class Demo {
private static final String resourceName = "helloResource";
public static void main(String[] args) {
initFlowRules();
sphoMethod();
}
/**
* sphu的方式
* 抛出异常的方式定义资源
*/
public static void sphuMethod(){
while (true){
//如果违反了规则就抛出异常
try (Entry entry = SphU.entry(resourceName)){
System.out.println(new Date().toString() + ": hello world");
} catch (BlockException e) {
System.out.println("blocked!");
}
}
}
/**
* spho的方式
* 返回布尔值方式定义资源
*/
public static void sphoMethod(){
while (true){
if(SphO.entry(resourceName)){
try {
System.out.println(new Date().toString() + ": hello world");
}finally {
SphO.exit();
}
}else{
System.out.println("blocked!");
}
}
}
//这个规则代表只允许每秒qps为10的请求通过resourceName这个资源
public static void initFlowRules(){
//定义规则
FlowRule flowRule = new FlowRule();
flowRule.setResource(resourceName);
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
flowRule.setCount(10);
FlowRuleManager.loadRules(Collections.singletonList(flowRule));
}
}
首先解析一下我们配置的规则是什么:只允许每秒qps为10的请求通过resourceName这个资源
运行一下main方法:
这样我们就可以知道:限流的直接表现是在执行 Entry nodeA = SphU.entry(resourceName)
的时候抛出 FlowException
异常。FlowException
是 BlockException
的子类,我们可以捕捉 BlockException
来自定义被限流之后的处理逻辑。
我们先看上面的initFlowRules方法里面定义的规则含义,即FlowRule的Api含义:
一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:
-
resource
:资源名,即限流规则的作用对象 -
count
: 限流阈值 -
grade
: 限流阈值类型(QPS 或并发线程数) -
limitApp
: 流控针对的调用来源,若为default
则不区分调用来源 -
strategy
: 调用关系限流策略 -
controlBehavior
: 流量控制效果(直接拒绝、Warm Up、匀速排队)
具体代码请看: com.onion.apiDemo.flowRule.Demo这个demo。github地址会放到文章结尾
熔断降级
Sentinel 提供以下几种熔断策略:
- 慢调用比例 (
SLOW_REQUEST_RATIO
):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。 - 异常比例 (
ERROR_RATIO
):当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是[0.0, 1.0]
,代表 0% - 100%。 - 异常数 (
ERROR_COUNT
):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
配置如下:
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule(KEY)
.setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType())
// Set ratio threshold to 50%.
.setCount(0.5d)
.setStatIntervalMs(30000)
.setMinRequestAmount(50)
// Retry timeout (in second)
.setTimeWindow(10);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
执行代码如下:
try {
entry = SphU.entry(KEY);
sleep(ThreadLocalRandom.current().nextInt(5, 10));
pass.addAndGet(1);
// Error probability is 45%
if (ThreadLocalRandom.current().nextInt(0, 100) > 55) {
// biz code raise an exception.
throw new RuntimeException("oops");
}
} catch (BlockException e) { //熔断降级的时候会抛出BlockException
block.addAndGet(1);
sleep(ThreadLocalRandom.current().nextInt(5, 10));
} catch (Throwable t) {
bizException.incrementAndGet();
Tracer.traceEntry(t, entry); //注意:为了统计异常比例或异常数,需要通过 Tracer.trace(ex) 记录业务异常
} finally {
total.addAndGet(1);
if (entry != null) {
entry.exit();
}
}
}
熔断降级规则(DegradeRule)包含下面几个重要的属性:
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,即规则的作用对象 | |
grade | 熔断策略,支持慢调用比例/异常比例/异常数策略 | 慢调用比例 |
count | 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值 | |
timeWindow | 熔断时长,单位为 s | |
minRequestAmount | 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) | 5 |
statIntervalMs | 统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) | 1000 ms |
slowRatioThreshold | 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入) |
注意:这里count的比例是: 异常比例/总数的。比如设置了count等于0.5。总请求是100,异常请求个数为:60,正常请求个数为:40,那么就会触发熔断降级了。
熔断降级运行:
也是通过SphU.entry(KEY)来进行。如果SphU.entry(KEY)熔断降级的话。会抛出BlockException异常。
注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效,为了统计异常比例或异常数,需要通过 Tracer.trace(ex) 记录业务异常,慢调用不需要
熔断器支持事件监听:
即是Sentinel 支持注册自定义的事件监听器监听熔断器状态变换事件(state change event),代码如下:
EventObserverRegistry.getInstance().addStateChangeObserver("logging",
(prevState, newState, rule, snapshotValue) -> {
// 变换至 OPEN state 时会携带触发时的值
if (newState == State.OPEN) {
System.err.println(String.format("%s -> OPEN at %d, snapshotValue=%.2f", prevState.name(),
TimeUtil.currentTimeMillis(), snapshotValue));
} else {
System.err.println(String.format("%s -> %s at %d", prevState.name(), newState.name(),
TimeUtil.currentTimeMillis()));
}
});
具体代码请看: com.onion.apiDemo.degradeRule.ExceptionRatioCircuitBreakerDemo 这个demo。github地址会放到文章结尾
这个demo主要功能就是:8个线程跑一段代码。该代码有百分之55的机会抛出异常并且还会统计该段代码的总的运行数量,通过的数量,异常的数量,block(熔断降级)的数量。 并且有个线程每隔一秒就打印这些指标出来。
热点参数限流
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
- 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
- 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
规则配置如下:
需要额外加入这个依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
<version>${sentinel.version}</version>
</dependency>
代码:
public static void initParamFlowRule(){
ParamFlowRule paramFlowRule = new ParamFlowRule();
paramFlowRule.setResource(RESOURCE_KEY);
// 限流阈值,全局的
paramFlowRule.setCount(5);
paramFlowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//热点参数的索引,必填,对应 SphU.entry(xxx, args) 中的参数索引位置
paramFlowRule.setParamIdx(0);
ParamFlowItem paramFlowItem = new ParamFlowItem()
.setObject(String.valueOf(PARAM_B)) //设置热点参数的数值
.setClassType(int.class.getName()) //设置热点参数的类型
.setCount(10);//单独参数的阈值
paramFlowRule.setParamFlowItemList(Collections.singletonList(paramFlowItem));
ParamFlowRuleManager.loadRules(Collections.singletonList(paramFlowRule));
}
热点参数规则(ParamFlowRule
)类似于流量控制规则(FlowRule
):
ParamFlowItem其实就是设置单独参数的阈值,ParamFlowRule是设置全局的阈值
运行部分的代码:
T param = generateParam();
try {
entry = SphU.entry(resourceName, EntryType.IN, 1, param,param);
// Add pass for parameter.
passFor(param);
} catch (BlockException e) {
// block.incrementAndGet();
blockFor(param);
} catch (Exception ex) {
// biz exception
ex.printStackTrace();
} finally {
// total.incrementAndGet();
if (entry != null) {
entry.exit(1, param);
}
}
Entry entry(String name, EntryType type, int count, Object... args) 参数解析:
- entryType: 资源调用的流量类型,是入口流量(
EntryType.IN
)还是出口流量(EntryType.OUT
),注意系统规则只对 IN 生效 - count: 表示每次调用计数为多少,通常传1。
- args:传入的参数
注意:若 entry 的时候传入了热点参数,那么 exit 的时候也一定要带上对应的参数(exit(count, args)
),否则可能会有统计错误。
具体代码请看: com.onion.apiDemo.ParamFlowRule.ParamFlowRuleDemo 这个demo。github地址会放到文章结尾
系统自适应限流
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统规则支持以下的模式:
- Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的
maxQps * minRt
估算得出。设定参考值一般是CPU cores * 2.5
。 - CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
- 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
- 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
系统自适应限流得代码如下:
List<SystemRule> rules = new ArrayList<SystemRule>();
SystemRule rule = new SystemRule();
// max load is 3
rule.setHighestSystemLoad(3.0);
// max cpu usage is 60%
rule.setHighestCpuUsage(0.6);
// max avg rt of all request is 10 ms
rule.setAvgRt(10);
// max total qps is 20
rule.setQps(20);
// max parallel working thread is 10
rule.setMaxThread(10);
rules.add(rule);
SystemRuleManager.loadRules(Collections.singletonList(rule));
具体代码请看: com.onion.apiDemo.systemRule.SystemGuardDemo 这个demo。github地址会放到文章结尾
黑白名单控制
很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin
)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
调用方信息通过
ContextUtil.enter(resourceName, origin)
方法中的origin
参数传入。
规则配置
来源访问控制规则(AuthorityRule
)非常简单,主要有以下配置项:
resource
:资源名,即限流规则的作用对象。limitApp
:对应的黑名单/白名单,不同 origin 用,
分隔,如appA,appB
。strategy
:限制模式,AUTHORITY_WHITE
为白名单模式,AUTHORITY_BLACK
为黑名单模式,默认为白名单模式。
代码显示:
public class AuthorityRuleDemo {
public static String KEY = "AuthorityRuleKey";
public static void main(String[] args) {
System.out.println("---------------------白名单打印---------------------");
initWhiteRule();
test(KEY,"appA");
test(KEY,"appB");
test(KEY,"appC");
System.out.println("---------------------黑名单打印---------------------");
initBlackRule();
test(KEY,"appA");
test(KEY,"appB");
test(KEY,"appC");
}
public static void test(String resource,String appName){
ContextUtil.enter(resource,appName);
Entry entry = null;
try {
entry = SphU.entry(KEY);
System.out.println("pass:" + appName);
}catch (BlockException e){
System.out.println("block:" + appName);
}finally {
if(entry!=null){
entry.exit();
}
ContextUtil.exit();
}
}
/**
* 初始化白名单规则
*/
public static void initWhiteRule(){
AuthorityRule authorityRule = new AuthorityRule();
authorityRule.setResource(KEY);
authorityRule.setStrategy(RuleConstant.AUTHORITY_WHITE); //设置白名单类型
authorityRule.setLimitApp("appA,appB,appE");
AuthorityRuleManager.loadRules(Collections.singletonList(authorityRule));
}
/**
* 初始化黑名单规则
*/
public static void initBlackRule(){
AuthorityRule authorityRule = new AuthorityRule();
authorityRule.setResource(KEY);
authorityRule.setStrategy(RuleConstant.AUTHORITY_BLACK); //设置白名单类型
authorityRule.setLimitApp("appA,appB,appE");
AuthorityRuleManager.loadRules(Collections.singletonList(authorityRule));
}
}
具体代码请看:com.onion.apiDemo.authorityRule.AuthorityRuleDemo这个demo。github地址会放到文章结尾
github地址:https://gitee.com/gzgyc/dubbo-demo.git
一些比集群流控的文章:
https://blog.csdn.net/weixin_42073629/article/details/106450575
https://www.jianshu.com/p/67d02420e814
https://blog.csdn.net/wangzhanzheng/article/details/86477559
生产环境落地的文章: